What CSS variables are
CSS variables (also called custom properties) are reusable values defined once and referenced anywhere in your CSS. Syntax: --my-var: value to define, var(--my-var) to use. Unlike Sass variables, CSS ones live at runtime: you can change them with JS or media queries and the whole system updates.
Why they matter
- Single source of truth: change a value, it updates everywhere.
- Dynamic theming: dark mode, high contrast, custom themes.
- Reusable components: components consume variables, not fixed values.
- Native cascade: variables respect inheritance, allowing scoped overrides.
Recommended structure
Standard pattern is to define all global variables in :root:
:root {
--color-primary: #0f172a;
--color-accent: #059669;
--bg: #ffffff;
--text: #09090b;
--radius: 8px;
--space: 1rem;
} Then use var(--color-primary) wherever needed.
Typical categories
- Color: primary, accent, background, text, borders, states (success, error, warning).
- Typography: font-family, sizes, weights, line-heights.
- Spacing: a scale (4, 8, 16, 24, 32, 48, 64).
- Radii: sm, md, lg, xl or a single base.
- Shadows: shadow-xs, sm, md, lg.
- Transitions: consistent durations and easings.
Dark mode with one media query
The most powerful use of variables is dark mode. Define the light set in :root and the dark one inside @media (prefers-color-scheme: dark). Components don't know about modes: they consume --bg and --text, CSS does the rest.
:root {
--bg: white;
--text: black;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: black;
--text: white;
}
} Manipulation with JavaScript
To change a variable from JS:
document.documentElement.style.setProperty('--color', '#fff'); This lets you implement theme pickers, sliders affecting the design system live, accessibility modes and any dynamic change without recompiling anything.
Best practices
Semantic names beat descriptive ones: --color-primary beats --blue. If primary changes to green tomorrow, you don't have to rename anything. Keep a consistent scale for spacing and typography: 4, 8, 16, 24 works better than arbitrary values. And group variables by category with clear comments.