Theming

Theme presets, dark mode, and how to create custom themes. uicraft supports multiple visual themes and light/dark mode independently.

How It Works

Two independent axes: visual theme (fonts, radius, colors) and color mode (light/dark).

Visual Theme
data-theme="..."
Controls font family, border radius, color palette
Color Mode
class="dark"
Toggles light/dark semantic tokens

Built-in Presets

Three theme presets ship with uicraft. Each changes fonts, radius, and subtle color shifts.

Theme Font Family Radius (lg) Character
default Inter, system-ui, sans-serif 12px Clean and modern
editorial Source Serif 4, Georgia, serif 10px Classic, editorial feel
rounded-sans Manrope, Inter, sans-serif 14px Soft, rounded corners
Aa
Default
data-theme="default"
Aa
Editorial
data-theme="editorial"
Aa
Rounded Sans
data-theme="rounded-sans"

Dark Mode

Add .dark to <html> to switch all semantic tokens to their dark variants.

Light mode
--fg-primary → grey-950
--neutrals-background → grey-50
--neutrals-surface → white
--border-default → grey-100
Dark mode
--fg-primary → grey-50
--neutrals-background → grey-950
--neutrals-surface → black
--border-default → grey-900
html
Dark mode toggle & persistence script
&lt;!-- Toggle dark mode with JS --&gt;
&lt;script&gt;
  function toggleDarkMode() {
    const html = document.documentElement;
    html.classList.toggle('dark');
    const mode = html.classList.contains('dark') ? 'dark' : 'light';
    localStorage.setItem('theme', mode);
  }
&lt;/script&gt;

&lt;!-- Restore on page load (put in &lt;head&gt;) --&gt;
&lt;script&gt;
  const mode = localStorage.getItem('theme');
  if (mode === 'dark' ||
      (!mode &amp;&amp; matchMedia('(prefers-color-scheme: dark)').matches)) {
    document.documentElement.classList.add('dark');
  }
&lt;/script&gt;

Switching Themes

Set data-theme on <html> and persist with localStorage.

html

Live Theme Controls

Mode

Theme

Live Tokens

Preview

Foreground and accents react to current theme + mode.

Semantic tokens in sync

Sample Component

Button

Radius and colors update as you switch presets.

&lt;!-- Switch theme preset --&gt;
&lt;script&gt;
  function setTheme(name) {
    document.documentElement.setAttribute('data-theme', name);
    localStorage.setItem('uicraft-theme', name);
  }

  // Restore on page load
  const theme = localStorage.getItem('uicraft-theme') || 'default';
  document.documentElement.setAttribute('data-theme', theme);
&lt;/script&gt;

Token Override Order

How CSS specificity works: theme preset → dark mode → component styles → utilities.

1
Theme preset — sets font, radius, base color tokens
2
Dark mode — overrides color tokens for dark palette
3
Component classes — uc-btn, uc-card use tokens
4
Utility classes — override anything inline

Custom Theme

Create a JSON file in themes/ to add your own preset.

json
1
Create a JSON file: themes/my-theme.json
2
Run the build — theme CSS is auto-generated from JSON
3
Apply with data-theme="my-theme"
{
  "name": "my-theme",
  "label": "My Theme",
  "fontFamily": "Poppins, system-ui, sans-serif",
  "radius": {
    "sm": "8px",
    "lg": "12px",
    "xl": "16px",
    "2xl": "20px",
    "3xl": "24px"
  },
  "colors": {
    "constantWhite": "0 0% 100%",
    "constantBlack": "0 0% 0%",
    "fgPrimary": "240 3% 6%",
    "fgSecondary": "228 4% 25%",
    "fgTertiary": "227 4% 43%",
    "fgDisabled": "231 6% 61%",
    "neutralsBackground": "0 0% 98%",
    "neutralsSurface": "0 0% 100%",
    "neutralsSubtle": "0 0% 98%",
    "neutralsMuted": "0 0% 96%",
    "neutralsEmphasis": "0 5% 92%",
    "borderDefault": "0 5% 92%",
    "borderStrong": "245 10% 78%",
    "accentsBlue": "219 88% 54%",
    "accentsGreen": "162 86% 40%",
    "accentsRed": "4 83% 55%",
    "accentsOrange": "30 100% 55%",
    "tintRed": "0 100% 95%",
    "tintGreen": "152 39% 89%",
    "tintBlue": "217 100% 92%",
    "tintOrange": "34 100% 95%"
  },
  "dark": {
    "colors": {
      "fgPrimary": "0 0% 98%",
      ...
    }
  }
}