Skip to content

Theming ​

Every color and every corner radius in the system comes from a CSS variable. To restyle the components, you override variables — no component code, no overrides, no specificity battles.

The variables follow the shadcn/ui naming, so if you know that convention, you already know these. Colors come in pairs: a surface and the text that sits on it (--primary and --primary-foreground).

How to theme ​

Import the library's tokens, then override any variable. Set light values on :root and dark values on .dark:

css
css
@import "tailwindcss";
@import "@dlbcodes/ui/tokens.css";

:root {
    --radius: 0.5rem;
    --brand: oklch(60% 0.14 250);
    --primary: oklch(25% 0.02 260);
}

.dark {
    --brand: oklch(70% 0.13 250);
    --primary: oklch(92% 0.01 260);
}

Because every component reads these variables, overriding them re-themes the whole system at once. Override only what you want to change — the rest keep their defaults.

Set both :root and .dark

:root and .dark have the same specificity, and your stylesheet comes after the library's. A value you set only on :root therefore also wins in dark mode. If you support dark mode, give every variable you override a .dark value too.

Dark mode ​

Dark mode is a dark class on the <html> element. Toggle it however you like:

ts
ts
document.documentElement.classList.toggle("dark");

Radius ​

--radius is the base corner radius. Every component's corners are derived from it, so one variable changes the whole system, from square to very round:

ClassValue
rounded-sm--radius × 0.6
rounded-md--radius × 0.8
rounded-lg--radius
rounded-xl--radius × 1.4
rounded-2xl--radius × 1.8
rounded-3xl--radius × 2.2
css
css
:root {
    --radius: 0; /* fully square */
}

Shapes that are round by definition (switches, progress bars, avatars' pills) use rounded-full and are not affected.

Semantic tokens ​

Surfaces ​

TokenRole
--backgroundPage background
--foregroundBody text, headings
--cardCards and panels
--card-foregroundText on cards
--popoverDropdowns, popovers, dialogs
--popover-foregroundText on popovers

Actions ​

TokenRole
--primaryMain neutral action, checked controls
--primary-foregroundText on --primary
--secondarySecondary surfaces
--secondary-foregroundText on --secondary
--brandBrand color: primary button, links
--brand-foregroundText on --brand
--proPremium / "pro" accent
--pro-foregroundText on --pro

Muted and accent ​

TokenRole
--mutedQuiet fills: tags, tracks, skeleton areas
--muted-foregroundLabels, metadata, placeholders
--accentHover and selected rows
--accent-foregroundText on --accent

Lines and focus ​

TokenRole
--borderDefault borders
--border-subtleSoft dividers
--border-strongEmphasis, scrollbar thumbs
--inputInput borders
--ringFocus rings

Status ​

Each status has four tokens: the solid color (also used for text), the text on that solid color, a light subtle surface, and a border.

StatusSolidOn solidSubtleBorder
Destructive--destructive--destructive-foreground--destructive-subtle--destructive-border
Success--success--success-foreground--success-subtle--success-border
Warning--warning--warning-foreground--warning-subtle--warning-border
Info--info--info-foreground--info-subtle--info-border

Charts ​

--chart-1 to --chart-5 are a five-color categorical palette.

The sidebar has its own set so it can be themed separately from the page: --sidebar, --sidebar-foreground, --sidebar-primary, --sidebar-primary-foreground, --sidebar-accent, --sidebar-accent-foreground, --sidebar-border and --sidebar-ring.

Using tokens in your own code ​

Every token is available as a Tailwind color, so your own UI can match the components:

vue
vue
<div class="rounded-xl border border-border bg-card p-4 text-card-foreground">
    <p class="text-muted-foreground">Same colors as the components.</p>
</div>

Opacity modifiers work too: bg-destructive/10, text-foreground/70.

Theming part of the page ​

Variables cascade, so you can retheme any subtree by overriding them on a wrapper element:

css
css
.checkout {
    --brand: oklch(55% 0.16 155);
    --radius: 1rem;
}

Everything inside .checkout picks up the new brand color and radius.

Utilities ​

The token file also ships two scrollbar utilities you can use on any element:

  • no-scrollbar — hides the scrollbar while keeping scroll behavior.
  • scrollbar-thin — a slim, themed scrollbar.
vue
vue
<div class="overflow-y-auto scrollbar-thin">…</div>

Migrating from the old tokens ​

Earlier versions used --color-bg-*, --color-text-* and a primitive palette. Those are gone. Rename them as follows:

OldNew
--color-text-primary--foreground
--color-text-secondary, -tertiary--muted-foreground
--color-text-inversethe matching --*-foreground
--color-bg-base--background
--color-bg-surface, -elevated--muted
--color-bg-raised--popover or --card
--color-bg-subtle--accent
--color-border-default--border (--input on inputs)
--color-border-dark--foreground
--color-{status}-surface / -text--{status}-subtle / --{status}
--color-brand-200--brand

Two components also renamed a variant: Alert danger is now destructive, and Badge pending is now warning. Input and Textarea sizes now match Button heights, so the old base (48px) is now lg.