Skip to main content
Urbicon UI

Customization

Every Urbicon UI component is restyleable through one predictable ladder of escape hatches. Pick the lowest rung that solves your problem — lower rungs preserve more of the design system's behavior (dark mode, hover/active cascade, focus rings).

Which tool do I use?

Start from your goal, not from the API. Find the row that matches what you want to change — the Reach for column is the tool to use.

I want to…Reach forExample
Restyle one element on one instance Highest priority. Merges onto the OUTERMOST (root) slot only — see the trap below.class<Button class="rounded-full">
Restyle an inner element (the actual <input>, a header, a chevron…) Type-safe — autocomplete lists the available slot names for each component.slotClasses.<slot><Input slotClasses={{ base: "rounded-full" }} />
App-wide look for a component type (every Button, every Card) defaults apply to every instance; presets are opt-in via preset="name".preset / BlocksProvider defaultsdefaults={{ Button: { slotClasses: { base: "rounded-full" } } }}
Style only one variant / intent / state (e.g. only outlined) Prop-conditional rule — what unconditional slotClasses cannot express.overridesoverrides: [{ variant: "outlined", class: { base: "border" } }]
Rebuild a component from scratch (strip every default) Renders the HTML structure only; you own all visuals.unstyled + slotClasses<Card unstyled slotClasses={{ base: "…" }} />
Full precedence chain (weakest → strongest): Conflicting Tailwind utilities are resolved per bucket, so a later source wins (e.g. an instance rounded-none defeats a default rounded-full); non-conflicting classes accumulate.
  1. tv() variant styles (library default)
  2. BlocksProvider defaults.slotClasses
  3. BlocksProvider defaults.overrides[match]
  4. preset.slotClasses (when preset="…" is set)
  5. preset.overrides[match]
  6. Instance slotClasses prop
  7. Instance class prop (root slot only)

The class Root-Slot Trap

The class prop only reaches the outermost (root) slot. Most components wrap several elements. class lands on the root wrapper, not the element you are usually picturing. To style something inside, go through slotClasses.<slot>.

The classic surprise is Input: its root slot is wrapper (the label + field column), and the real <input> element is the base slot. So class="rounded-full" rounds the column, not the field.

class vs. slotClasses on Input

Loading...
Loading syntax highlighting...

slotClasses is now type-safe on every component: the keys are derived from the component's tv() slots, so your editor autocompletes the available slot names (wrapper, container, base, label, message… for Input). Check a component's API reference, or the Slot Names reference, for its slot map.

CSS Token Themes

The simplest way to brand the library. Import a theme CSS file after the base styles to override the primary and secondary accents plus the neutral chassis. The semantic layer (surface, text, border tokens) derives from that chassis, so each theme re-tints it to match the accent's temperature — warm accents get warm surfaces, not cold grey ones.

Use a built-in theme

Loading...
Loading syntax highlighting...
Ocean
Forest
Sunset
Rose
Neutral

You can also create your own theme. Use the Theme Builder to generate an OKLCH palette, or write a custom @theme block:

Custom @theme block

Loading...
Loading syntax highlighting...

Global Component Defaults

When CSS token overrides are not enough, use BlocksProvider to set default slotClasses for every component type. Wrap your app once, and every Button, Card, Input etc. picks up the defaults. Instance-level slotClasses still override the global ones.

Global defaults via BlocksProvider

Loading...
Loading syntax highlighting...

Defaults sit near the bottom of the precedence chain above — instance slotClasses and class still win. For prop-conditional defaults (overrides) and named presets, see the BlocksProvider API.

Global Unstyled Mode

For a completely custom design, set unstyled on BlocksProvider. All components strip their default styles and only render the HTML structure. Use slotClasses (globally via defaults or per instance) to apply your own design.

Global unstyled mode

Loading...
Loading syntax highlighting...

Deep Dives