CSS custom properties — var(--x) — are the foundation of a token-driven design system. But most teams use them wrong. Here are the three rules that separate a real token system from a pile of variables.
RULE 1: NO FALLBACKS
var(--zt-bg, #050505) is a bug. The fallback hides a missing token. If --zt-bg is not defined, the system is broken, and the fallback papers over it instead of surfacing the problem. Define every token on :root. If a token doesn't exist, add it — don't paper over it with a fallback.
"A fallback in var() is a silent failure. You want loud failures — undefined tokens should break, not render as a default."
RULE 2: TOKENS ON :ROOT
Every token lives on :root. Not on body, not on a wrapper div, not on the component that uses it. :root is the single source of truth. Components read tokens; they do not define them. If a component needs a value that no token provides, you add a token — you do not hardcode the value in the component.
Why :root and not body?
:root has higher specificity than html or body, and it is the conventional place for global custom properties. Every linter, every tool, every developer expects tokens on :root. Don't surprise them.
RULE 3: NAMING IS THE SYSTEM
A token named --dark-blue is wrong. It describes the value, not the role. When you change the accent from green to blue, --dark-blue is now a lie. Name by role: --zt-accent, --zt-bg, --zt-line. The role is stable; the value changes.
The Prefix
Use a two-letter prefix — --zt- — to avoid collisions with third-party CSS and to make tokens greppable. Every token in the system starts with the prefix. If you see var(--zt-, it is a system token. If you see anything else, it is not from the system and should be questioned.
CONCLUSION
No fallbacks, tokens on :root, name by role not by value. Three rules, and every one of them prevents a class of bug that will cost you hours. The token system is the design system — get it right, and the rest follows.