C&C Docs
Log in

app-color-mode

The light / dark / system switcher. It writes the choice to a mode cookie, toggles the dark class and color-scheme on <html>, and follows the operating system while the choice is system. Because the choice is a cookie rather than browser storage, the server already knows it on the next request and ships the correct theme in the first paint.

Quick start

Drop the element into a header. It needs no attributes and no wiring — everything on this page, including the page you are reading, re-themes when you use it.

Click the icon to choose Light, Dark, or System.
<app-color-mode></app-color-mode>

Events

The widget dispatches nothing. It is a control over global document state, so the state itself is the interface: watch the dark class on <html> with a MutationObserver if you need to react in JavaScript, and read $device.mode on the next request if you need to react on the server.

What changes Where Detail
mode cookie path=/, max-age=31536000 (one year), SameSite=Lax. Read back server-side as $device.mode.
class="dark" <html> Added when the effective theme is dark. This is the class every dark: utility keys off.
style.colorScheme <html> Set to light or dark so native controls, scrollbars and form widgets follow.

The server already knows

The choice is a cookie, so the very next request carries it and the server resolves the theme before rendering. That is what $device.mode and $device.effective hold, and it is why this page's markup arrives already themed rather than flashing light and correcting itself. Both values are live below — change the mode above and reload.

$device.mode
light
$device.effective
light
Cookie $device.mode $device.effective
dark dark dark
light light light
system system the Sec-CH-Prefers-Color-Scheme hint, else light — and the document corrects it before it paints
absent / invalid light light

The client hint only arrives on Chromium browsers, so under system the server's answer is a guess everywhere else. Nothing is recorded to paper over that — the document resolves system in its own head, before the first paint, which is earlier than any note about last time could be read and right on every browser. An explicit light or dark needs none of this: it is the visitor's own decision and the server already has it.

Styling against it

Everything downstream is the ordinary dark: variant — the widget's only job is putting the class on the root. Because the class is already on the server-rendered <html>, the dark styles apply to the first paint too.

bg-neutral-100 dark:bg-neutral-800

Surface

border-neutral-200 dark:border-neutral-700

Border

bg-primary-600

Brand colours are mode-independent

Usage guidance

Place one instance in the application shell, not one per page — every copy drives the same document root and the same cookie, so several on screen simply mirror each other (as the three on this page do). Prefer the drop over toggle unless space is tight: system is the setting most people want, and the toggle cannot reach it. Do not write the mode cookie yourself from application code; let the widget own it so the document class, the color-scheme and the cookie can never disagree.

← Back to Widgets