Skip to content

Theming

The design system supports light and dark themes through CSS custom properties.

Theme Structure

Themes work by remapping semantic tokens to mode-specific values:

Base Token (e.g., --bg-light)
    ↓
Theme Mapping (in light.css or dark.css)
    ↓
Semantic Token (e.g., --bg)
    ↓
Used in Components

Light Theme

Defined in src/themes/light.css:

:root:not(.dark) {
  --bg: var(--bg-light);           /* #dde3e9 */
  --bg-elevated: #f5f7fa;
  --text-primary: var(--text-primary-light);
  --text-secondary: rgba(13, 41, 53, 0.8);
  --shadow: var(--shadow-light);
  --neu-1: var(--neu-light-1);
  --neu-2: var(--neu-light-2);
  --neu-inset: var(--neu-light-inset);
  --border: var(--border-light);
  --primary-text: #186891;
  --success-text: #024d47;
  /* ... */
}

Dark Theme

Defined in src/themes/dark.css:

.dark {
  --bg: var(--bg-dark);            /* #0f1419 */
  --bg-elevated: var(--bg-dark-elevated);
  --text-primary: var(--text-primary-dark);
  --text-secondary: var(--text-secondary-dark);
  --shadow: var(--shadow-dark);
  --neu-1: var(--neu-dark-1);
  --neu-2: var(--neu-dark-2);
  --neu-inset: var(--neu-dark-inset);
  --border: var(--border-dark);
  --primary-text: #55ccd9;
  --success-text: #03a89e;
  /* ... */
}

Enabling Dark Mode

Toggle the .dark class on the <html> element. In this project, dark mode is managed via component state in the admin-ui TopBar.

Manual Toggle

function ThemeToggle() {
  const [isDark, setIsDark] = useState(false);

  const toggle = () => {
    document.documentElement.classList.toggle('dark');
    setIsDark(!isDark);
  };

  return (
    <button onClick={toggle}>
      Toggle Theme
    </button>
  );
}

System Preference

/* Respect OS preference as default */
@media (prefers-color-scheme: dark) {
  :root {
    /* Apply dark token mappings */
  }
}

Using Theme-Aware Tokens

Always use semantic tokens instead of hardcoded values:

/* ✅ Good - Theme aware */
.my-component {
  background: var(--bg);
  color: var(--text-primary);
  box-shadow: var(--neu-1);
  border-color: var(--border);
}

/* ❌ Bad - Not theme aware */
.my-component {
  background: #dde3e9;
  color: #0d2935;
}

Token Reference

Backgrounds

Token Light Dark
--bg #dde3e9 #0f1419
--bg-elevated #f5f7fa #1a1f26

Text

Token Light Dark
--text-primary #0d2935 #e8edf2
--text-secondary rgba(13,41,53,0.8) rgba(232,237,242,0.8)
--primary-text #186891 #55ccd9
--success-text #024d47 #03a89e
--warning-text #f2c744 #f2c744
--error-text #b85500 #f28d35

Shadows

Token Light Dark
--shadow Soft light shadow Subtle dark shadow
--neu-1 Light neumorphic Dark neumorphic
--neu-2 Elevated light Elevated dark
--neu-inset Light inset Dark inset

Borders

Token Light Dark
--border rgba(13,41,53,0.15) rgba(85,204,217,0.2)
--border-subtle rgba(13,41,53,0.08) rgba(85,204,217,0.12)

Customizing Themes

To customize themes, override the CSS custom properties:

/* Custom light theme overrides */
:root:not(.dark) {
  --primary-accent: #your-brand-color;
  --primary-text: #your-text-color;
}

/* Custom dark theme overrides */
.dark {
  --primary-accent: #your-brand-color-dark;
  --primary-text: #your-text-color-dark;
}

Creating a New Theme

  1. Create a new CSS file (e.g., themes/custom.css)
  2. Define all semantic token mappings
  3. Import in your app after the base styles
/* themes/custom.css */
.theme-custom {
  --bg: #your-bg;
  --bg-elevated: #your-elevated-bg;
  --text-primary: #your-text;
  --primary-accent: #your-accent;
  /* Define all tokens... */
}

WCAG Compliance

The design system uses WCAG-compliant text colors for accessibility:

  • Light mode text colors have sufficient contrast against light backgrounds
  • Dark mode text colors have sufficient contrast against dark backgrounds
  • Interactive elements meet minimum contrast requirements

Color contrast ratios: - Normal text: 4.5:1 minimum - Large text: 3:1 minimum - UI components: 3:1 minimum

Testing Themes

Use browser dev tools to test:

  1. Toggle .dark class on <html> element
  2. Check contrast ratios in Accessibility panel
  3. Test with system preference simulation