Skip to main content
Every scaffolded app includes a dark mode toggle that persists across pages via a dm cookie. The toggle is in the header of every page, including the admin panel.

How it works

1

The cookie is read before first paint

A dark-mode.js script loaded in <head> reads the dm cookie and applies a .dark class to <html> before first render — so there is no flash of the wrong theme.
2

Any element can toggle it

Clicking any element carrying data-toggle-dark-mode toggles the class and updates the cookie.
3

Styles respond through a custom variant

All CSS uses dark: variants via Tailwind’s custom variant: @custom-variant dark (&:where(.dark, .dark *)).

The toggle button

Scaffolded into your templates:
The only thing dark-mode.js looks for is the data-toggle-dark-mode attribute. Which icon shows is plain dark: variants on the two spans. If you would rather keep the markup clean, move the swap into app.css and give the button a class of your own:
app.css

Writing dark-mode-aware styles

When adding custom styles, include dark: variants for every color-related class. Feather recommends CSS classes with @apply rather than inline Tailwind, so dark mode support looks like this:
app.css
Utility classes in markup instead of @apply in app.css are reported by feather check as the inline-tailwind warning. Keeping styles in the stylesheet is what makes a one-line dark mode audit possible.