Getting Started

Theming

How Shelf UI's CSS variable system works — token reference, light/dark modes, and how to customize every token to match your brand.

Shelf UI uses CSS custom properties (CSS variables) for all visual tokens. Every component reads from the same token set — override a token once in your globals.css and it propagates to every component automatically.

This is the same system used by shadcn/ui. If you've already customized shadcn tokens, most of your work is done.

How it works

Tokens are defined in globals.css under :root (light mode) and .dark (dark mode). Tailwind v4 maps them to utility classes via @theme:

globals.css (simplified)
:root {
  --background: #f4f4f5;
  --foreground: #09090b;
  --primary: #b45309;
}

.dark {
  --background: #07070a;
  --foreground: #eeedf5;
  --primary: #f0b429;
}

Tailwind then exposes them as bg-background, text-foreground, text-primary, etc.

Token reference

Colors

TokenValueUsage
--background#f4f4f5Page background
--foreground#09090bDefault text
--card#ffffffCard surfaces
--card-foreground#09090bText on cards
--popover#ffffffPopovers, dropdowns
--popover-foreground#09090bText in popovers
--primary#b45309Brand color, CTAs
--primary-foreground#ffffffText on primary
--secondary#f4f4f5Secondary surfaces
--secondary-foreground#09090bText on secondary
--muted#f4f4f5Subtle backgrounds
--muted-foreground#747477De-emphasized text
--accent#e4e4e7Hover states
--accent-foreground#09090bText on accent
--destructive#ef4444Error states
--borderrgba(0,0,0,0.08)Default borders
--inputrgba(0,0,0,0.1)Input borders
--ring#b45309Focus rings
TokenValueUsage
--background#07070aPage background
--foreground#eeedf5Default text
--card#111118Card surfaces
--card-foreground#eeedf5Text on cards
--popover#111118Popovers, dropdowns
--popover-foreground#eeedf5Text in popovers
--primary#f0b429Brand color, CTAs
--primary-foreground#07070aText on primary
--secondary#1c1c26Secondary surfaces
--secondary-foreground#eeedf5Text on secondary
--muted#18181fSubtle backgrounds
--muted-foreground#99999eDe-emphasized text
--accent#1c1c26Hover states
--accent-foreground#eeedf5Text on accent
--destructive#f87171Error states
--borderrgba(255,255,255,0.06)Default borders
--inputrgba(255,255,255,0.08)Input borders
--ring#f0b429Focus rings

Typography

TokenValueUsage
--font-sans"Bai Jamjuree", sans-serifBody text, UI labels
--font-mono"Courier Prime", monospaceCode blocks

Border radius

TokenValue
--radius0.625rem (10px) — base radius
--radius-smcalc(var(--radius) - 4px)
--radius-mdcalc(var(--radius) - 2px)
--radius-lgvar(--radius)
--radius-xlcalc(var(--radius) + 4px)

Shadows

TokenValue
--shadow-sm0px 2px 6px 0px hsl(0 0% 0% / 0.09)
--shadow0px 4px 10px 0px hsl(0 0% 0% / 0.1)
--shadow-lg0px 8px 20px 0px hsl(0 0% 0% / 0.12)

Overriding tokens

Change the primary brand color

The most common customization. Override --primary and --primary-foreground in both modes:

globals.css
:root {
  --primary: #2563eb;           /* your brand blue */
  --primary-foreground: #ffffff;
}

.dark {
  --primary: #60a5fa;           /* lighter for dark mode */
  --primary-foreground: #0f172a;
}

This propagates to every button, badge, focus ring, and active state across all Shelf UI components automatically.

Change the font

Swap in any Google Font:

globals.css
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");

:root {
  --font-sans: "Inter", sans-serif;
}

Or use a Next.js font (recommended for performance):

app/layout.tsx
import { Inter } from "next/font/google";

const fontSans = Inter({ subsets: ["latin"], variable: "--font-sans" });

Change the border radius

For a sharper, more geometric look:

globals.css
:root {
  --radius: 0.25rem; /* 4px — very sharp */
}

For a more rounded, softer look:

globals.css
:root {
  --radius: 1rem; /* 16px — very rounded */
}

Full token block

Copy this into your globals.css if you want to start from scratch:

globals.css
:root {
  --background: #f4f4f5;
  --foreground: #09090b;
  --card: #ffffff;
  --card-foreground: #09090b;
  --popover: #ffffff;
  --popover-foreground: #09090b;
  --primary: #b45309;
  --primary-foreground: #ffffff;
  --secondary: #f4f4f5;
  --secondary-foreground: #09090b;
  --muted: #f4f4f5;
  --muted-foreground: #747477;
  --accent: #e4e4e7;
  --accent-foreground: #09090b;
  --destructive: #ef4444;
  --border: rgba(0, 0, 0, 0.08);
  --input: rgba(0, 0, 0, 0.1);
  --ring: #b45309;
  --radius: 0.625rem;
  --font-sans: "Bai Jamjuree", sans-serif;
  --font-mono: "Courier Prime", monospace;
}

.dark {
  --background: #07070a;
  --foreground: #eeedf5;
  --card: #111118;
  --card-foreground: #eeedf5;
  --popover: #111118;
  --popover-foreground: #eeedf5;
  --primary: #f0b429;
  --primary-foreground: #07070a;
  --secondary: #1c1c26;
  --secondary-foreground: #eeedf5;
  --muted: #18181f;
  --muted-foreground: #99999e;
  --accent: #1c1c26;
  --accent-foreground: #eeedf5;
  --destructive: #f87171;
  --border: rgba(255, 255, 255, 0.06);
  --input: rgba(255, 255, 255, 0.08);
  --ring: #f0b429;
}

Use the shadcn theme generator or Tweakcn to produce a compatible token set — then paste the output into your globals.css. All Shelf UI components will pick it up automatically.

FAQ

Last Updated on Jun 22, 2026

On this page