Skip to content
Theme
Accent
Voice
SkillCase StudiesComponentsExamplesThemingBest PracticesPhilosophyGitHub ↗
Theming · dsgn

Make it yours.

Every component in the registry — all 23 of them — reads color from exactly 16 CSS custom properties. No component hardcodes a hex value; every bg-primary, text-foreground, or border-border class is Tailwind resolving to a variable. Change the 16 variables in your own globals.css and every installed component re-themes at once — no component file needs to be touched, no matter how many you’ve already customized.

The 16 tokens

--backgroundPage background
--foregroundDefault text color
--cardCard / Frame surface background
--card-foregroundText on a card surface
--primaryPrimary button fill
--primary-foregroundText on a primary button
--secondarySecondary button / muted surface fill
--secondary-foregroundText on a secondary surface
--accentThe one brand color — links, focus rings, the accent Button variant
--accent-foregroundText on an accent-filled surface
--mutedLow-emphasis fill (disabled states, subtle backgrounds)
--muted-foregroundLow-emphasis text (captions, placeholders)
--destructiveDestructive button fill, error Alert border
--destructive-foregroundText on a destructive surface
--borderEvery component's border color
--ringFocus-visible ring color

Palette generator

7 curated accent presets — not a random generator, each hand-picked and contrast-checked for both themes. Pick one to re-theme the whole site live, same as the Remix panel.

Contrast ratio of each preset’s current-theme accent color against this theme’s real --void background (#07080c) — WCAG 2.1 normal-text thresholds (AA ≥ 4.5:1, AAA ≥ 7:1), computed live, not looked up from a table. Toggle light/dark in the nav above to see every badge recompute for that theme’s real values.

Preview

The selected preset, on real registry components.

/* Dark mode */
--accent: #28e0ec;
--accent-foreground: #07080c;

/* Light mode */
--accent: #0e8b96;
--accent-foreground: #f7f5f2;

Drop into your own :root / [data-theme="light"] blocks — real hex, not a variable name that only resolves inside this site.

Try it live

Drag any swatch below — this page (and the site’s Remix panel in the bottom-right corner of every page) reads from the exact same tokens.

Every color on this page just changed live — this is the same @theme inline re-read that makes the 16 tokens work in your own project.

:root {
  --background: #07080c;
  --foreground: #f2f4f8;
  --card: #0d0f16;
  --primary: #f2f4f8;
  --secondary: #151822;
  --accent: #28e0ec;
  --muted: #12141c;
  --destructive: #ff5470;
  --border: #1d2130;
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-card: var(--card);
  --color-primary: var(--primary);
  --color-secondary: var(--secondary);
  --color-accent: var(--accent);
  --color-muted: var(--muted);
  --color-destructive: var(--destructive);
  --color-border: var(--border);
  /* ...same pattern for the remaining 7 of the site's 16 tokens */
}

A minimal starting palette

This is the same shape shadcn/ui’s own default theme uses — drop it into your project’s global stylesheet (wherever @import "tailwindcss" lives) and every registry component picks it up immediately, no config file needed.

:root {
  --background: #ffffff;
  --foreground: #0a0a0a;
  --card: #ffffff;
  --card-foreground: #0a0a0a;
  --primary: #171717;
  --primary-foreground: #fafafa;
  --secondary: #f4f4f5;
  --secondary-foreground: #171717;
  --accent: #2563eb;
  --accent-foreground: #fafafa;
  --muted: #f4f4f5;
  --muted-foreground: #71717a;
  --destructive: #dc2626;
  --destructive-foreground: #fafafa;
  --border: #e4e4e7;
  --ring: #2563eb;
}

Dark mode

Re-declare the same 16 variable names under whatever selector or media query your project uses to trigger dark mode — a .dark class, a [data-theme="dark"] attribute (this site’s own approach — see apps/site/app/globals.css for the real example), or a plain @media (prefers-color-scheme: dark) block. Nothing about the components themselves changes — they only ever reference the variable names, never a specific theme.

Proof, not a promise

These aren’t hand-maintained mockups — they’re real screenshots of the live registry, captured with Playwright straight from a running build (see apps/site/scripts/capture-screenshots.mjs). If a component looked wrong in one theme, this gallery would show it.

button
button in light modelight
button in dark modedark
card
card in light modelight
card in dark modedark
badge
badge in light modelight
badge in dark modedark
dialog
dialog in light modelight
dialog in dark modedark
table
table in light modelight
table in dark modedark
accordion
accordion in light modelight
accordion in dark modedark
Accent
Voice
Corner radius

This is applied to the whole site live, not just this panel. Export it as CSS →