Max
Back to Blog

One Set of Styles for Dark and Light: Driving Your Color Scheme with CSS Variables

This site supports both dark and light themes, yet the stylesheet contains no second set of section styles and no JavaScript toggle logic. There is only one secret: no color is ever hard-coded — everything references a CSS variable, and the two themes are just two sets of variable values.

How to extract the variables

Define a semantic set of variables on :root — --bg, --text, --accent, --card-surface, --card-shadow, and so on — with dark values as the default. Then override the same set inside @media (prefers-color-scheme: light). Every section style references var(--*), so 'changing the theme' goes from editing hundreds of style lines to editing a dozen variables.

Naming makes or breaks it

Name variables by meaning, not by color. --card-surface can be a dark gray gradient in dark mode and white in light mode — the name holds either way. Call it --dark-gray and the light-mode override turns the name into a lie. Semantic naming turns your variables into a design-system vocabulary: when adding a new section, pick the right word and both themes are automatically correct.

The devil in the details

A few easily-missed spots: set color-scheme: dark light on :root so native browser widgets (scrollbars, form controls) switch along; the theme-color meta tag accepts a media attribute, letting the mobile browser chrome follow the theme too; and shadows and glows need different opacities on light backgrounds, so even something as seemingly constant as --card-shadow belongs in a variable.

Why no manual toggle button

Following the system preference is the better default for most personal sites: the user has already expressed a preference at the OS level — respect it. If a manual toggle ever becomes necessary, this architecture only needs the variable sets hung on a data attribute and a few lines of JS to switch it — the variable layer itself stays untouched.

Extracting colors into variables is one of those investments that costs an extra hour up front and saves a day on every redesign after. If you are starting a new project, write your very first line of CSS this way.