Colour mode
Light, dark and system, without a flash of the wrong palette.
Colour mode is one attribute. heyo-ui resolves both palettes from a single
set of tokens with light-dark(), which reads color-scheme, which is
driven by data-mode on <html>. Writing that attribute is the entire
switch — there is no class list to keep in sync.
data-mode | Result |
|---|---|
| absent | follow the operating system |
light | force light |
dark | force dark |
In system mode the attribute is removed, not set to today's resolved value. Freezing the current preference into the document would stop the page following the OS when it changes at sunset.
Configure the initial mode
Pass it to the page, and the provider mounted by <StatusPage /> uses it as
the default:
A visitor's own choice is persisted in localStorage under
heyo-status-theme and always wins over the configured default.
Avoid the flash
Render the bootstrap script in the document head, before any paint. It reads the stored preference, falls back to the configured mode and writes the attribute. It is small and CSP-compatible.
statusThemeBootstrapScript(storageKey?, defaultTheme?) — pass undefined
for the default key, heyo-status-theme, which is also exported as
DEFAULT_STATUS_THEME_STORAGE_KEY.
The script mutates the document before React hydrates. Without
suppressHydrationWarning, React reports the attribute it did
not render.
The provider and hook
<StatusPage /> mounts StatusThemeProvider itself. Mount it yourself only
when building a custom page from the primitives:
The stored preference.
What is actually rendered right now.
False during the first client render. Gate mode-dependent UI on it to avoid a hydration mismatch.
Flips between the two resolved palettes.
A disabled or unavailable localStorage is caught: the toggle keeps working,
it simply does not persist.
Built-in toggle
Already part of StatusHeader, and therefore of every bundled theme.