Dynamic Theme Switching with Tailwind CSS

Build a multi-theme system in Tailwind CSS that lets users switch between color themes at runtime.

themingmulti-themecss-variablesruntime
advanced15 min readtutorial

Beyond Dark Mode: Multi-Theme Systems

Most sites only toggle light and dark. But with CSS custom properties and Tailwind, you can support unlimited themes: seasonal palettes, brand sub-themes, or user-personalized colors, all without rebuilding CSS.

Defining Multiple Themes

@layer base {
  :root, .theme-default {
    --color-primary: 59 130 246;
    --color-surface: 255 255 255;
    --color-text: 15 23 42;
  }
  .theme-forest {
    --color-primary: 22 163 74;
    --color-surface: 240 253 244;
    --color-text: 20 83 45;
  }
  .theme-sunset {
    --color-primary: 234 88 12;
    --color-surface: 255 247 237;
    --color-text: 124 45 18;
  }
  .theme-ocean {
    --color-primary: 6 182 212;
    --color-surface: 236 254 255;
    --color-text: 21 94 117;
  }
}

Theme Switcher Component

function ThemeSwitcher() {
  const themes = ['default', 'forest', 'sunset', 'ocean'];

  function setTheme(name) {
    const root = document.documentElement;
    themes.forEach(t => root.classList.remove(`theme-${t}`));
    root.classList.add(`theme-${name}`);
    localStorage.setItem('theme', name);
  }

  return (
    <div className="flex gap-2">
      {themes.map(t => (
        <button key={t} onClick={() => setTheme(t)}
          className="w-8 h-8 rounded-full bg-primary border-2 border-white shadow" />
      ))}
    </div>
  );
}

Architecture Tips

  • Store all theme-dependent colors as CSS variables, never as hardcoded Tailwind classes
  • Use semantic names (primary, surface, text) instead of descriptive names (blue, white)
  • Load saved theme before first paint to prevent flash
  • Each theme should define all required variables to avoid missing values
  • Test contrast ratios for every theme to maintain accessibility

Frequently Asked Questions

How many themes can I support?

There is no practical limit. Each theme is a CSS class with variable overrides. Since variables are inherited, only the overridden values take up space.

Does multi-theme increase CSS bundle size?

Minimally. Each theme adds only the CSS variable declarations (a few hundred bytes). The Tailwind utility classes themselves are shared across all themes.

How do I prevent a flash of wrong theme on load?

Add a blocking script in the head element that reads localStorage and applies the theme class before the browser paints. This avoids any visible theme flash.

Try Our Color Tools

50+ free tools for Tailwind CSS developers. No signup required.

Explore Tools