v0.1.6·How-To
Dark Mode Setup
Implement dark mode support using CSS variables and system preferences.
Published: 12/17/2025

Dark Mode Setup

This guide shows how to implement dark mode using CSS variables and system preferences.

Automatic dark mode

UI Lab components automatically support dark mode via CSS variables. Enable it by:

// app.tsx or main.tsx
import 'ui-lab-components/styles.css';

Components respond to system preference automatically via prefers-color-scheme. No additional setup needed.

Manual theme toggle

Control theme programmatically:

import { useState, useEffect } from 'react';
import { Button } from 'ui-lab-components';

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

  useEffect(() => {
    // Check system preference on load
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    setIsDark(prefersDark);
  }, []);

  const toggleTheme = () => {
    const newIsDark = !isDark;
    setIsDark(newIsDark);

    // Update document class
    if (newIsDark) {
      document.documentElement.classList.add('dark');
    } else {
      document.documentElement.classList.remove('dark');
    }

    // Persist preference
    localStorage.setItem('theme', newIsDark ? 'dark' : 'light');
  };

  return (
    <Button onClick={toggleTheme}>
      {isDark ? '☀️ Light' : '🌙 Dark'}
    </Button>
  );
}

Update CSS variables

Override color variables for your theme. Add to your root CSS file or globals.css:

:root {
  /* Light mode (default) */
  --background-50: oklch(99.2% 0.001 240);
  --background-100: oklch(97.5% 0.002 240);
  --foreground-950: oklch(11.8% 0.018 240);
  --foreground-900: oklch(18% 0.022 240);

  --color-primary: oklch(68% 0.22 245);
  --color-danger: oklch(65% 0.28 25);
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --background-50: oklch(11.8% 0.018 240);
    --background-100: oklch(18% 0.022 240);
    --foreground-950: oklch(99.2% 0.001 240);
    --foreground-900: oklch(97.5% 0.002 240);

    --color-primary: oklch(75% 0.2 245);
    --color-danger: oklch(70% 0.25 25);
  }
}

/* Manual dark mode class */
.dark {
  --background-50: oklch(11.8% 0.018 240);
  --background-100: oklch(18% 0.022 240);
  --foreground-950: oklch(99.2% 0.001 240);
  --foreground-900: oklch(97.5% 0.002 240);

  --color-primary: oklch(75% 0.2 245);
  --color-danger: oklch(70% 0.25 25);
}

Persist user preference

Save theme choice and restore it on page load:

export function useTheme() {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  useEffect(() => {
    // Load saved preference
    const saved = localStorage.getItem('theme');
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    const initialTheme = (saved as 'light' | 'dark') || (prefersDark ? 'dark' : 'light');

    setTheme(initialTheme);
    applyTheme(initialTheme);
  }, []);

  const applyTheme = (newTheme: 'light' | 'dark') => {
    if (newTheme === 'dark') {
      document.documentElement.classList.add('dark');
    } else {
      document.documentElement.classList.remove('dark');
    }
    localStorage.setItem('theme', newTheme);
  };

  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    applyTheme(newTheme);
  };

  return { theme, toggleTheme };
}

Use in your app

import { useTheme } from './useTheme';
import { Button } from 'ui-lab-components';

export function App() {
  const { theme, toggleTheme } = useTheme();

  return (
    <div>
      <Button onClick={toggleTheme}>
        {theme === 'dark' ? '☀️' : '🌙'} {theme === 'dark' ? 'Light' : 'Dark'}
      </Button>
      {/* Rest of app */}
    </div>
  );
}

Color scale reference

All color variables automatically invert in dark mode:

| Variable | Light | Dark | |----------|-------|------| | --background-50 | White | Near black | | --background-100 | Off-white | Very dark | | --foreground-950 | Near black | White | | --foreground-900 | Very dark | Off-white | | --color-primary | Adjusted for light | Adjusted for dark |

Next step

Learn to combine dark mode with forms in Build a Form with Validation.

© 2025 UI Lab • Built for humans and machines