There are several ways to move tokens into code, but in a WordPress theme CSS custom properties are almost always the right answer, for two reasons: they inherit, and they exist at runtime.
How they differ from preprocessor variables
A SCSS variable is substituted at build time and then gone; the browser never hears the name. A custom property arrives at the browser alive, which means you can redeclare it inside a region and flip everything beneath it.
:root {
--brand: #0e7c66;
--brand-hover: #0a5f50;
--ink: #1f2933;
--surface: #ffffff;
--space-4: 16px;
--radius-md: 8px;
}
.site-button {
background: var(--brand);
color: var(--surface);
padding: var(--space-4);
border-radius: var(--radius-md);
}
.site-button:hover {
background: var(--brand-hover);
}
.site-button does not know what the brand colour is, and does not need to. When the rebrand lands, you open the :root block and nothing else.
Scoped redeclaration — the real payoff
Because custom properties inherit, redeclaring tokens inside a region carries every component in it along without touching component code.
.section--inverse {
--ink: #f7f9fa;
--surface: #121a24;
background: var(--surface);
color: var(--ink);
}
A dark section no longer needs rewritten rules for buttons, cards and badges. Dark mode works the same way, with the redeclaration inside a media query instead of a class. This is where the discipline of referencing only tokens pays you back all at once.
Placing it in the theme
File layout needs exactly one rule: no colour or spacing literals outside the token file. What makes it a good rule is that it is checkable — one search for values beginning with # in the components directory becomes your review step.
Settle the load order too. If the parent theme stylesheet loads after yours, a specificity war begins and you start bolting importance flags onto individual rules. Load your bundle last and that war never starts; in a child theme this is simply a higher priority number than the parent uses.
Do not lean on fallback values (var(--brand, #0e7c66)). They look like a safety net, but in practice they are a route for literals to creep back into components and they hide the fact that a token is missing. Tokens should always be defined — and when one is not, you want to see it.
The order in which theme structure actually gets tidied is published on our process page, and the audit tools we release for free live on the tools page.
Next part
The tokens are in the theme. The block editor, however, knows nothing about them — editors still get a rainbow picker. The next part opens that channel with theme.json.