fix(tokens): stop tailwind-tokens.css self-referencing (breaks light mode where Tailwind theme output is unlayered) - #119
Merged
Conversation
…nces
The token build generated `@theme inline { --color-x: var(--color-x) }` for
every colour, which Tailwind emitted as `:root,:host { --color-x: var(--color-x) }`.
That declaration is cyclic and so invalid at computed-value time. Wherever a build
leaves Tailwind's theme output unlayered — e.g. Starlight, whose
`@astrojs/starlight-tailwind` ships its own top-level `@theme` and unlayers the
emitted block — the `:root` rule (0,1,0) outranks the real Light values we scope to
`html` (0,0,1), so every token resolved to nothing in Light mode. Dark was
unaffected because `html[data-equality-theme='dark']` (0,1,1) still won.
Register the colour tokens in a non-inline `@theme` carrying their real Light
values. Utilities still compile to `var(--color-*)` (so consumer overrides are
unchanged) and the emitted `:root,:host` block now holds a plain value instead of a
self-reference, so tokens resolve whether or not the block is layered. Dark keeps
overriding via its more specific selector. The genuinely-inline `--color-mixed-*`
entries stay in their own `@theme inline` block.
Verified in headless Chrome against the full theme-config (260 tokens) through both
a Starlight (Tailwind 4.3.3 + starlight-tailwind, previously broken) and a plain
Vite app (Tailwind 4.1.18) pipeline: Light and Dark both resolve, utilities resolve,
and `html`/wrapper `--color-*` overrides still win.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Importing
@eqtylab/equality/theme-config.cssleft every token resolving to nothing in light mode in our docs site (Astro + Starlight + Tailwind v4). Dark mode was fine. Anything built on the tokens — including Equality's own<Button>— rendered unstyled.Root cause
build-tokens.jsgeneratedtailwind-tokens.cssas:Tailwind emits those into
:root,:host, so the shipped CSS contains:root,:host { --color-background: var(--color-background) }. That declaration is cyclic → invalid at computed-value time, so the token computes to nothing.Whether that dead declaration wins comes down to cascade layers, and that is decided by the consumer's build:
@layer theme. Unlayered declarations always beat layered ones, so the real light values Equality scopes tohtml(unlayered) win. Light works — which is why this was invisible for so long.@astrojs/starlight-tailwindships its own top-level@theme {}; when Tailwind merges it, the emitted theme-variable block comes out unlayered. Now it's a pure specificity fight —:root(0,1,0) outrankshtml(0,0,1) — so the cycle wins and light collapses to nothing. Dark survives becausehtml[data-equality-theme='dark'](0,1,1) still wins.This is not a Tailwind-version or import-style issue — verified that 4.1.18 / 4.2.1 / 4.3.3 all emit the block into
@layer themefor a plain input, and it's the presence ofstarlight-tailwind's unlayered@themethat hoists it out.Fix
Register the colour tokens in a non-inline
@themecarrying their real light values, instead of@theme inlineself-references:background-color: var(--color-background)— so consumer--color-*overrides (html-scoped and wrapper-scoped, e.g. governance-studio's brand tokens) are unchanged.:root,:hostblock now holds a plain value, so it's harmless whether it ends up layered or not.Diff is just the generator plus the regenerated
tailwind-tokens.css.Verification (headless Chrome, real built CSS, full 260-token theme-config)
.bg-*utility--color-*overrideBefore the fix, the same Starlight probe returned empty custom properties in light; after,
--color-background=color(display-p3 1 1 1),--color-text-primary=color(display-p3 0 0 0),--color-lilac-400resolves, and the.bg-backgroundutility computes correctly.Downstream
dist/generated/color-vars.cssonce it bumps to the release containing this fix.🤖 Generated with Claude Code