Tailwind CSS has built-in support for dark mode through the dark: variant. This lesson covers both the automatic media-based strategy and the manual class-based strategy, including a working toggle button.
What Is Dark Mode in Tailwind?
Dark mode lets you define an alternate color scheme that activates automatically based on the user's operating system setting, or manually through a toggle in your UI. Tailwind supports both approaches through the same dark: variant prefix.
You style the light-mode appearance normally, then add dark: prefixed utilities to override colors, backgrounds, and borders specifically for dark mode, all on the same element.
In light mode the card is white with dark text; in dark mode the background and text colors flip automatically.
Dark Mode Strategy Configuration
// tailwind.config.js
export default {
darkMode: "media", // follows OS preference (default)
// or
darkMode: "class", // toggled by a "dark" class on <html>
};
media strategy uses the prefers-color-scheme CSS media query and requires no JavaScript.
class strategy activates dark styles only when a dark class is present on an ancestor, usually <html>.
The class strategy is required if you want a manual toggle button controlled by JavaScript.
In Tailwind v4, dark mode can also be configured with the @custom-variant directive in CSS.
Dark Mode Cheatsheet
Common dark-mode utility pairs and configuration options.
Purpose
Example
Notes
Background flip
bg-white dark:bg-slate-900
Most common dark mode pattern
Text color flip
text-slate-900 dark:text-slate-100
Keeps text readable in both modes
Border flip
border-slate-200 dark:border-slate-700
Softer border contrast in dark mode
Muted text flip
text-slate-500 dark:text-slate-400
Secondary text stays legible
Shadow adjustment
shadow-md dark:shadow-none
Shadows often look wrong on dark backgrounds
Image dimming
dark:brightness-90
Slightly dims images in dark mode
Config: media
darkMode: "media"
Automatic, follows OS setting
Config: class
darkMode: "class"
Manual, toggled via JS
Toggle target
document.documentElement.classList.toggle("dark")
Adds/removes the dark class
Persist choice
localStorage.setItem("theme", "dark")
Remembers user preference
The Media Strategy (Automatic)
With darkMode: "media" (the default in Tailwind v3), dark styles apply automatically whenever the user's operating system is set to dark mode, using the prefers-color-scheme: dark media query. No JavaScript or toggle button is needed.
<!-- tailwind.config.js: darkMode: "media" -->
<body class="bg-white text-black dark:bg-black dark:text-white">
Automatically follows the OS theme.
</body>
The Class Strategy With a Manual Toggle
With darkMode: "class", dark styles only apply when a dark class exists on an ancestor element, usually <html>. This lets users manually switch themes with a button, independent of their OS setting.
The script checks localStorage on load so the chosen theme persists across page visits, then toggles the dark class and saves the new preference on click.
Avoiding a Flash of the Wrong Theme
If the theme-detection script runs after the page renders, users can briefly see the wrong theme flash before it switches. Place a small inline script in the document head, before any content renders, to apply the dark class immediately.
<head>
<script>
if (
localStorage.getItem("theme") === "dark" ||
(!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
}
</script>
</head>
Accessible Dark Mode Toggles
A dark mode toggle should be a real <button>, announce its current state, and maintain sufficient contrast ratios in both themes, not just visually pleasing colors.
Use aria-pressed to communicate the toggle's current on/off state to screen readers.
Verify text and background color combinations meet WCAG AA contrast in both light and dark themes.
Respect the user's OS preference by default; only override it after an explicit manual choice.
Common Mistakes
Forgetting to set darkMode: "class" in the config, so a manual JavaScript toggle has no effect.
Only styling backgrounds for dark mode and forgetting text, border, and shadow colors, which breaks contrast.
Not persisting the user's theme choice, causing it to reset on every page reload.
Loading the theme-detection script after page content renders, causing a visible flash of the wrong theme.
Assuming dark mode is just inverted colors; some colors (like brand colors) may need entirely different dark-mode shades to stay legible.
Key Takeaways
Tailwind's dark: variant applies utilities only in dark mode, using either the media or class strategy.
The media strategy follows the OS setting automatically with no JavaScript needed.
The class strategy enables a manual toggle by adding or removing a dark class on <html>.
Persist the user's theme choice in localStorage and apply it before the page paints to avoid a flash.
Always update text, border, and shadow colors alongside backgrounds when switching themes.
Pro Tip
Store the user's preference as "light", "dark", or absent (meaning "follow system"), and check prefers-color-scheme only when no explicit choice has been saved yet.
You now know how to implement Tailwind's dark mode with both the media and class strategies. Next, learn the @apply directive for grouping utilities into reusable custom classes.