System variants and contrast
Resolve custom variants and model independent accessibility preferences.
Last updated on
Brand tokens with system preference
Keep "light" and "dark" as theme selections so enableSystem can resolve "system", and map those names to brand attribute tokens with value:
<ThemeProvider
themes={["light", "dark"]}
defaultTheme="system"
value={{
light: "paper",
dark: "midnight",
}}
>
{children}
</ThemeProvider>The default Next provider stays on this light path on purpose. Custom system maps that rename the selection itself belong behind @wrksz/themes/next/extended so the core bundle stays small. See ThemeProvider Extended.
Custom system names
When the stored selection should be "paper" / "midnight" rather than "light" / "dark", pass a direct { light, dark } map:
import { ThemeProvider } from "@wrksz/themes/next/extended";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider
themes={["paper", "midnight"]}
defaultTheme="system"
systemThemeMap={{ light: "paper", dark: "midnight" }}
>
{children}
</ThemeProvider>
</body>
</html>
);
}system then resolves to "paper" or "midnight" from prefers-color-scheme.
Variant families
A Record map keeps a variant family (for example red vs blue) while still following system light/dark. Pair it with followSystem:
import { ThemeProvider } from "@wrksz/themes/next/extended";
<ThemeProvider
themes={["light-red", "dark-red", "light-blue", "dark-blue"]}
defaultTheme="light-red"
followSystem
systemThemeMap={{
"light-red": { light: "light-red", dark: "dark-red" },
"dark-red": { light: "light-red", dark: "dark-red" },
"light-blue": { light: "light-blue", dark: "dark-blue" },
"dark-blue": { light: "light-blue", dark: "dark-blue" },
}}
>
{children}
</ThemeProvider>Selecting "light-red" stays in the red family; OS dark mode resolves to "dark-red". systemThemeMap is serializable, so it works on the Next extended bootstrap. For a client-owned themeRoot, use @wrksz/themes/client/extended-provider instead.
Contrast as an independent axis
Contrast is not a color theme. Model it as a separate createThemes() factory using data-contrast, or use CSS directly when it only follows the operating system:
@media (prefers-contrast: more) {
:root {
--border-strength: 2px;
--muted-opacity: 1;
}
}Keeping contrast separate avoids duplicating every palette as light-high-contrast, dark-high-contrast, and so on. If users can override contrast, persist that factory under a distinct storage key and expose a separate control.