Dark mode

Tailwind CSS · Practical · 8 min

Users expect dark mode, and Tailwind makes supporting it a matter of adding dark: classes.

The dark: variant applies a utility when dark mode is active: bg-white dark:bg-gray-900.

By default it follows the operating system setting, compiling to @media (prefers-color-scheme: dark). No JavaScript is needed.

To let users choose, redefine the variant in your CSS so it depends on a class or attribute instead:

@custom-variant dark (&:where(.dark, .dark *));

Now dark: applies inside any element with the dark class, usually <html class="dark">. A tiny script in the head sets that class before the page paints (from localStorage or matchMedia) to avoid a flash of the wrong theme. A data attribute works the same way: &:where([data-theme=dark], [data-theme=dark] *).

Practical advice:

  • Design the pairs together: text-gray-900 dark:text-gray-100, border-gray-200 dark:border-white/10. Translucent whites and blacks (bg-white/5) adapt well to layered dark surfaces.
  • Set color-scheme (the scheme-light-dark utility or CSS) so form controls and scrollbars match.
  • For larger apps, define semantic colour variables in @theme and switch their values in dark mode, then use bg-surface and text-muted everywhere instead of writing dark: pairs on every element.

Example

<!-- app.css
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
-->
<html class="dark">
<head>
  <script>
    // run before paint: saved choice, else system preference
    const saved = localStorage.getItem("theme");
    const dark = saved ? saved === "dark" : matchMedia("(prefers-color-scheme: dark)").matches;
    document.documentElement.classList.toggle("dark", dark);
  </script>
</head>
<body class="bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-100">
  <div class="rounded-xl border border-gray-200 p-6 dark:border-white/10 dark:bg-white/5">
    <p class="text-gray-600 dark:text-gray-400">Card text</p>
  </div>
</body>
</html>

Remember

  • dark: follows prefers-color-scheme by default
  • @custom-variant dark (...) switches it to a class or attribute
  • Set the class in a head script to avoid a flash of the wrong theme
  • Semantic colour variables beat dark: pairs in large apps

Watch out for

  • Toggling the dark class after the page has rendered, causing a white flash on load.

Check yourself

  1. Without any configuration, when does dark:bg-black apply in Tailwind v4?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: tailwindcss.com/docs/dark-mode

Builds on

Unlocks

Nothing builds on this yet.