Dark mode
Every semantic colour token has a dark value. Components never need a dark variant: they read the tokens, and the tokens change.
How the theme is chosen
- If an element has
data-theme="dark", it and its descendants use dark values. - If an element has
data-theme="light", they use light values, even when the system prefers dark. - Otherwise the tokens follow
prefers-color-scheme.
Because the attribute works on any element, you can render a dark panel inside a light page, or the other way round.
<section data-theme="dark">
{/* everything in here uses dark tokens */}
</section>Adding a theme switch
Store the user's choice, apply it to <html> before the first paint, and fall back to the system preference when nothing is stored.
<script>
try {
const theme = localStorage.getItem("theme");
if (theme === "light" || theme === "dark") {
document.documentElement.setAttribute("data-theme", theme);
}
} catch {}
</script>The script is wrapped in try because storage can be unavailable in private windows or when site data is blocked. Place it in the document head so it runs before the body renders.
What changes in dark mode
- Surfaces step lighter as they rise: page
#0b0d12, surface#12151c, tray#161a22. - Shadows switch from ink tints to deeper black, and elevated surfaces gain a 1px
--mrd-linering, because shadows alone are hard to see on dark backgrounds. - The accent lightens to
#6b8affto keep contrast against the dark page. - Status tints become translucent so they sit on any surface.
Checking your own components
Test both themes whenever you add colour. A quick way is to toggle data-theme on <html> in the browser's element inspector.