Dark Mode
Sisyphos UI ships light and dark themes out of the box. Flip them with one class or use the built-in helpers.
How it works
The dark theme is a CSS class, .sisyphos-theme-dark, that re-assigns the neutral token pool. Toggle the class on <html> (or any ancestor) to switch modes.
snippet.csscss
:root {
--sisyphos-color-neutral: #f9fafb;
/* …light values */
}
.sisyphos-theme-dark {
--sisyphos-color-neutral: #1c1c1c;
/* …dark values */
}Basic toggle
Use the helpers exported from the library for imperative toggles:
snippet.tsxtsx
import { setThemeMode, toggleThemeMode } from "@sisyphos-ui/ui";
<Button onClick={() => toggleThemeMode()}>Toggle theme</Button>
<Button onClick={() => setThemeMode("dark")}>Force dark</Button>Or manage the class yourself:
snippet.tsxtsx
document.documentElement.classList.toggle("sisyphos-theme-dark");Respect system preference
Wire the initial mode to prefers-color-scheme and persist the user's choice:
components/theme-init.tsxtsx
"use client";
import { useEffect } from "react";
import { setThemeMode } from "@sisyphos-ui/ui";
export function ThemeInit() {
useEffect(() => {
const saved = localStorage.getItem("theme") as "light" | "dark" | null;
const system = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
setThemeMode(saved ?? system);
}, []);
return null;
}Avoid flash on load
To prevent a flash of unstyled content on first paint, inject a tiny inline script before hydration that sets the theme class synchronously.
app/layout.tsxtsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script
dangerouslySetInnerHTML={{
__html: `(function(){
var saved = localStorage.getItem("theme");
var mode = saved || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
if (mode === "dark") document.documentElement.classList.add("sisyphos-theme-dark");
})();`,
}}
/>
</head>
<body>{children}</body>
</html>
);
}