Dark Mode
Styling a dark theme alongside a light one using the dark: variant, and choosing between OS-driven and manual toggling.
អាន 2 នាទី
Dark mode in Tailwind works exactly like a responsive breakpoint or a hover state: it's a dark: prefix you add next to the light-mode utility it should override.
<div class="bg-white text-slate-900 dark:bg-slate-900 dark:text-slate-100">
<h2 class="font-semibold dark:text-white">Account settings</h2>
</div>Read each pair together — bg-white dark:bg-slate-900 means "white background normally, dark slate background in dark mode." You're not writing a separate dark-mode stylesheet or duplicating markup; every element simply carries both versions of itself side by side.
Two ways to trigger dark mode
By default, dark: responds to the operating system's prefers-color-scheme setting — if the user's OS is set to dark mode, dark: utilities apply automatically, with no JavaScript involved. That's often exactly right for a simple site, but many products want a manual toggle the user controls independently of their OS setting (a sun/moon switch in the app itself).
To switch to manual, class-based dark mode in a v4 project, you register a custom variant in your CSS:
/* app.css */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));With that in place, dark: utilities only activate when a .dark class is present on an ancestor (typically <html>) — toggled by a small script that adds or removes that class and usually persists the choice in localStorage. The utility classes in your markup don't change at all; only the mechanism deciding when dark: is "on" changes.
Design both states together, not as an afterthought
<button class="rounded-md border border-slate-200 bg-slate-50 px-4 py-2 text-slate-700
hover:bg-slate-100
dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700">
Cancel
</button>Notice hover states need their own dark-mode variant too (dark:hover:bg-slate-700) — a light-mode hover color that happens to also look fine in dark mode is more coincidence than design. Building a component with both light and dark values from the start is far less painful than retrofitting dark mode onto a finished light-only design later, because every color decision has to be revisited anyway.
A common shortcut: neutral-on-neutral
A simple, low-effort dark mode pattern is inverting only your neutral (gray/slate) scale and leaving brand and status colors (blues, greens, reds) mostly as-is, just adjusting their shade slightly for contrast:
<div class="bg-white dark:bg-slate-900">
<span class="rounded-full bg-green-100 px-2 py-0.5 text-green-800 dark:bg-green-900 dark:text-green-200">
Active
</span>
</div>The badge stays recognizably green in both themes — only its specific shades shift to keep enough contrast against the new background. This is usually enough for a good-looking dark mode without redesigning every color decision from scratch.