Skip to main content
Every scaffolded app includes a dark mode toggle that persists across pages in a dm cookie. The toggle sits 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 the wrong theme never flashes.
2

Any element can toggle it

Clicking an 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 script only looks for the data-toggle-dark-mode attribute. Which icon shows is plain dark: variants on the two spans. To keep the markup cleaner, move the swap into app.css and give the button your own class:
app.css

Writing dark-mode-aware styles

Include dark: variants for every color-related class. Feather prefers CSS classes with @apply over 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 dark mode audit a single-file read.