Dark mode
Light, dark and system schemes from one attribute.
Every Syntara theme has a light and a dark scheme, generated and contrast-checked together. Which one you see is one attribute.
Choose a scheme
data-syntara-scheme | Result |
|---|---|
omitted or light | Light scheme. |
dark | Dark scheme. |
auto | Follows the operating system through prefers-color-scheme. |
<html data-syntara-theme="vela" data-syntara-scheme="auto">For part of a page, use ThemeScope:
<ThemeScope theme="vela" scheme="dark">
<AccountSummary />
</ThemeScope>The token CSS also sets color-scheme, so native form controls, scrollbars and the CSS light-dark() function follow the scheme without extra work. The code blocks on this site use exactly that.
Remember the user’s choice
This site keeps auto as the default and stores an explicit choice in localStorage. A tiny inline script in <head> applies it before the first paint, so a stored preference never flashes the wrong scheme.
const script = `try {
const s = localStorage.getItem('scheme');
if (s === 'light' || s === 'dark') document.documentElement.dataset.syntaraScheme = s;
} catch {}`;
export default function RootLayout({ children }) {
return (
<html data-syntara-scheme="auto" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: script }} />
</head>
<body>{children}</body>
</html>
);
}The toggle then sets the attribute and writes localStorage inside try/catch, because storage can be unavailable in private windows.
How dark themes are generated
Dark isn’t an inverted light theme. The engine maps each ramp to roles separately for dark — canvas at the dark end, text near the light end — and runs every contrast check again, so both schemes pass WCAG 2.2 AA on their own.
One rule in dark mode isn’t from WCAG. A near-black brand colour passes contrast with a white label but nearly disappears on a near-black canvas, so the engine lifts primary fills until they reach 2.2:1 against the canvas. It is logged as a visibility adjustment and never reported as a WCAG result (ADR-006). This site’s own near-black brand (tenants/house/brand.json) triggers it:
- dark · visibilityYour primary #18181b nearly disappears on the dark canvas (1.0:1), so dark-mode buttons use a lighter tone of the same hue, #4a4a4e (2.2:1).
- dark · visibilityYour accent #18181b nearly disappears on the dark canvas (1.0:1), so dark-mode accent fills use a lighter tone of the same hue, #4a4a4e (2.2:1).
- dark · contrast#18181b is too dark to see as a focus ring on the default surface #141415 (1.0:1), so the ring uses a lighter tone, #b7b7ba (7.2:1).
Every preview on this site has its own light and dark switch, independent of the site’s scheme.