System preference and a manual theme toggle

A system-preference theme respects the user's operating-system setting. A manual toggle lets the user override that preference. For a manual theme, your app typically stores a theme choice, updates the selector or class that activates dark mode, and prevents a flash of the wrong theme during initial rendering.

In server-rendered frameworks, theme initialization can cause hydration differences if the server and client render different markup. Use a framework-appropriate theme strategy and test the first paint, refresh, and navigation. Never assume a CSS variant alone creates a functioning toggle.

Separate appearance from behavior

Tailwind styles the theme states. Your app still needs to decide where the theme value lives, how it persists, and how the active dark selector is applied.

Conceptual theme toggle
function ThemeToggle({ theme, setTheme }) {
  const isDark = theme === "dark";

  return (
    <button
      type="button"
      aria-pressed={isDark}
      onClick={() => setTheme(isDark ? "light" : "dark")}
      className="rounded-lg border px-3 py-2 text-sm font-medium focus-visible:outline-2 focus-visible:outline-offset-2"
    >
      Switch to {isDark ? "light" : "dark"} mode
    </button>
  );
}
đź’ˇ This component illustrates the accessible control contract; it assumes the parent owns and persists theme, and that the application's dark selector is wired to that state. aria-pressed communicates the toggle state. The code alone does not add or remove a class on the document.