Dark Mode
Light values sit on :root. Dark values apply through :root[data-color-scheme="dark"] and, when the page is not pinned, through prefers-color-scheme: dark on :root:not([data-color-scheme="light"]) — see src/styles/abstracts/_css-vars.scss.
How the scheme is chosen
- The root layout renders
<html lang="en" data-color-scheme="dark">, so the document defaults to dark before hydration. SiteShellacceptscolorScheme. When set,PageChromewrites the attribute on every route change — a pinned scheme always wins over the saved preference and the OS setting.- Every page shipped with Rasuke 1.0 pins
"dark"(including the 404). RemovecolorSchemefrom a page (and the layout default) to let that page follow the visitor's system + saved choice.
<SiteShell pageName="about" bodyClass="page-about" colorScheme="dark" headerName="header-1" footerName="footer-1">
{/* sections */}
</SiteShell>
Runtime toggle
public/assets/js/vendors/color-modes.js is registered before paint with React 19's preinit() in src/app/layout.tsx. It:
- looks for
[data-color-scheme-toggle]buttons (for example the offcanvas menu theme switch) and syncsaria-pressed+aria-label(data-label-to-light/data-label-to-dark); - persists the choice in
localStorageunderrasuke-color-scheme; - applies a stored choice on load; a page that pins
colorSchemere-asserts it inPageChromeafter hydration, so the pinned scheme is what the visitor keeps; - adds the
jsclass before the first frame, so reveal styles never flash; - dispatches a
rasuke:color-schemeevent so canvases that rasterizedcurrentColor(for example the hero wordmark) can re-rasterize.
Write mode-safe CSS
Sections never reference a mode. Bind semantic tokens and they flip automatically:
.about-2 {
background-color: var(--color-surface-subtle);
color: var(--color-text-primary);
border-color: var(--color-border-base);
}
Do not write a dark-only hex in SCSS. If a new color must differ by mode, add both light and dark values in tokens.json. The brand orange #FF6A3A, cream #FCF7F3, and footer surface #1B1B1B are intentionally identical in both modes.
Testing both modes
- Temporarily remove
colorSchemefrom a page and toggle the theme from the offcanvas menu. - Force a mode in DevTools → Rendering → Emulate CSS prefers-color-scheme to check the unpinned fallback.
- Clear the
rasuke-color-schemelocalStorage key to reset a saved choice. - Re-check WebGL sections: deformation canvases sample images, so they need to rebuild when the mode changes.