A modern commercial theme arrives with hundreds of its own tokens declared in :root. Colours, buttons, header, forms — all drawn from them. When you try to lay your design system on top, there appear to be three options.
You can edit the theme files directly — and lose the work at the next update. You can ignore the theme tokens and override with your own CSS — and start a specificity war rule by rule while the screen splits into two systems. The third is the one that survives.
Split it into three layers
Two rules carry it. Never edit vendor tokens. And re-point vendor tokens at your values in exactly one bridge file. Components reference your tokens and do not know vendor token names exist.
The payoff arrives with a theme update. The thing you audit is one bridge file, not every component. If the vendor renamed a token, it surfaces there and nowhere else, and the rest of the code is untouched.
Bridges are usually shorter than expected, because tokens in a well-built theme derive from one another — re-point one base colour and the link colour, button fill, focus ring and selection highlight follow. Read the derivation chain before writing the bridge and you will halve the lines you need.
The rgb pair — the quietest trap in this series
Many themes keep two custom properties per colour: the colour itself (--x-color) and an rgb triple used for transparency (--x-color-rgb). The second is consumed like this.
.badge {
background: rgba(var(--x-color-rgb), 0.12);
}
Change only --x-color and skip the pair, and here is what happens. Every opaque element takes your colour, while only the translucent ones stay in the theme’s original colour — focus rings, overlays, badge backgrounds.
Because most of the screen looks correct, it passes review. It then surfaces in production, usually spotted by someone else first.
The cause is a limit of CSS itself: var() cannot convert a hex value into an rgb triple. So generate both together at build time.
@use 'sass:color';
@mixin token($name, $hex) {
--#{$name}: #{$hex};
--#{$name}-rgb:
#{color.channel($hex, 'red')},
#{color.channel($hex, 'green')},
#{color.channel($hex, 'blue')};
}
:root {
@include token('x-color', #0e7c66);
}
With one mixin, the pair cannot fall out of step. Relative colour syntax in newer CSS opens a runtime route as well, but while browser support is uneven, generating at build time is the certain one.
Let a machine check the convention
None of this survives on human attention. Three searches confirm the convention holds: do components reference vendor tokens directly, is any vendor token redefined outside the bridge, and does the count of colour tokens match the count of -rgb tokens? That last one catches the trap above exactly.
How theme updates are handled safely is set out on our process page, and building this structure on top of a commercial theme is within the scope of our optimization program.
Next part
The structure is complete. The final part is handing it to the next person — because only documentation that lives with the code stays true.