Skip to main content
All articles
Theming May 20, 2026 5 min read

Tailwind v4 Dark Mode: The CSS-Only Approach

No JavaScript provider, no system preference detection, no flash of wrong theme. Here's how dark mode works in Tailwind CSS v4 with pure CSS.

By Ninna UI Team


Dark mode in React apps has historically required JavaScript: a provider to detect the system preference, state to track the current theme, and a script to prevent the flash of wrong theme on load. Tailwind CSS v4 makes all of this unnecessary.

The CSS-only setup

@import "tailwindcss";
@import "@ninna-ui/core/theme/presets/default.css";

/* Enable class-based dark mode */
@variant dark (&:is(.dark *));

That's it. The theme preset defines both light and dark color values. When the .dark class is on the html element, all colors switch automatically.

Toggling dark mode

// Toggle
document.documentElement.classList.toggle("dark");

// Set specific mode
document.documentElement.classList.add("dark");
document.documentElement.classList.remove("dark");

No provider, no context, no re-render. The browser handles the switch through CSS — it's instant and flicker-free.

Preventing the flash of wrong theme

Add a tiny inline script in your HTML head to set the theme class before React hydrates:

<script>
  const theme = localStorage.getItem("theme");
  if (theme === "dark" || (!theme && matchMedia("(prefers-color-scheme: dark)").matches)) {
    document.documentElement.classList.add("dark");
  }
</script>

This runs before React loads, so the correct theme is applied from the first paint. No flash, no hydration mismatch.

How oklch makes dark mode better

oklch color values make dark mode palettes perceptually correct. Instead of inverting RGB values (which produces uneven results), oklch lightness values are adjusted perceptually:

:root {
  --color-primary: oklch(0.55 0.22 280);
}
.dark {
  --color-primary: oklch(0.72 0.18 280);
}

The dark mode primary is lighter (0.72 vs 0.55) with slightly less chroma — perceptually balanced for a dark background, not just an inverted value.

The takeaway

Dark mode in Tailwind v4 is a CSS feature, not a JavaScript feature. One @variant directive, one class toggle, and a 5-line inline script. No provider, no context, no runtime cost.


Build it with Ninna UI

Accessible React components, CSS-only theming, Tailwind v4 native.

Get Started

Keep reading