Skip to content
GitHub
Ui Bugs

Derived CSS tokens (color-mix over var()) freeze at :root-resolved values under subtree theming

Derived CSS tokens (color-mix over var()) freeze at :root-resolved values under subtree theming

Problem

@spectral/design-tokens defines derived tokens like --proceed-line: color-mix(in srgb, var(--proceed) 40%, var(--color-border)) in :root. The dark blocks override --proceed and --color-border. When the theme attribute/class lives on <html> (both portals today), the derived token re-resolves correctly. But on a subtree wrapper (.dark on a div), the derived token computes on :root (light inputs) and the subtree inherits the already-substituted light result.

Root Cause

Per css-variables-1, var() substitution happens at computed-value time on the element where the property is declared; descendants inherit the substituted value, not the expression. A derived token declared only at :root is computed with :root’s cascade.

Solution

Redeclare the derived tokens (-line variants, --tier-*, legacy --color-success/warning/error aliases) inside every dark block ([data-theme="dark"], .dark and the prefers-color-scheme fallback) with the same expressions — they then re-resolve on the element carrying the theme. See packages/design-tokens/tokens/colors.css.

Prevention

  • When adding a token defined as an expression over other tokens, mirror its declaration into every theme block, not just :root.
  • Keep the prefers-color-scheme fallback block and the explicit dark block token-for-token identical, or system-dark and toggled-dark drift apart.

References

  • SPEC-603 review fix commit 7034d76; merge 5570e9cb.