DEV3 Technologies home

DEV3 Developer Forum

How do I enable class-based dark mode (a toggle button) in Tailwind 4?

dev3 ·

In Tailwind 3 I set darkMode: 'class' in tailwind.config.js. That key is gone in v4 and dark: only follows my OS setting. How do I make a manual toggle work?

#tailwind#tailwind-4#dark-mode

0

1 reply

dev3 · Accepted answer

Default behavior

In Tailwind 4 dark: uses the prefers-color-scheme media query. To control it with a class or attribute you override the variant in CSS.

Class strategy

@import "tailwindcss";

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

Now dark:bg-slate-900 applies when an ancestor (typically <html>) has the class dark.

Data attribute strategy

@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));

Toggle script with persistence (avoid a flash)

Run this as a small inline script in the head, before the body renders (it must be synchronous so the first paint is correct):

try {
  const stored = localStorage.getItem('theme');
  const prefersDark = matchMedia('(prefers-color-scheme: dark)').matches;
  document.documentElement.classList.toggle('dark', stored ? stored === 'dark' : prefersDark);
} catch (e) {}

Toggle button:

function toggleTheme() {
  const dark = document.documentElement.classList.toggle('dark');
  try { localStorage.setItem('theme', dark ? 'dark' : 'light'); } catch (e) {}
}

In Next.js use next-themes with attribute="class" and add suppressHydrationWarning on <html>.

Style with variables for less repetition

:root { --bg: white; --fg: #111; }
.dark { --bg: #0b0b0f; --fg: #eee; }

@theme inline {
  --color-surface: var(--bg);
  --color-ink: var(--fg);
}

Then bg-surface text-ink works in both themes without dark: on every element.

Pitfalls

  • Also set color-scheme: dark on .dark so form controls and scrollbars match.
  • Test both themes for contrast with the contrast checker.
  • Upgrading from v3: remove darkMode from the JS config, it is ignored.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.