fix(ui): use runtime :root color vars in shimmer gradient - #434
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes the chat Shimmer text gradient by replacing references to Tailwind v4 @theme inline variables (--color-*, not available as runtime custom properties) with coss-ui’s runtime :root/.dark CSS variables (--background, --muted-foreground), ensuring the background-image computes correctly in both themes.
Changes:
- Update
Shimmer’sbackgroundImageto usevar(--background)andvar(--muted-foreground)so the gradient no longer resolves tononeat computed-value time. - Add an inline comment documenting why
--color-*variables are not safe to use at runtime.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
ℹ️ The fix is correct and I verified the premise independently. One comment-accuracy nit inline, plus a stacking question below.
Reviewed changes — the single commit 8f7e4fb0, one file.
- Shimmer gradient reads the
:rootvars —packages/presentation/ui/src/chat/shimmer.tsxswapsvar(--color-background)/var(--color-muted-foreground)forvar(--background)/var(--muted-foreground)in the inlinestyle.backgroundImage, and adds a one-line trap comment.
I confirmed the premise rather than taking it on faith, by compiling Tailwind 4.3.3 through its compile() API against a minimal repro of coss-ui's @theme inline block. The A/B was decisive: with no candidates the alias is not emitted; with a bg-muted utility candidate it is still not emitted (the value is inlined); only an arbitrary-value candidate like [background:var(--color-muted)] forces it into :root. A var(--color-*) written inside a JS inline style string is never a scanned candidate, so nothing forced --color-background to be emitted — the declaration was invalid at computed-value time and background-image fell back to none.
Worth recording that the blast radius was larger than "rendered with no colors": because the component itself sets bg-clip-text text-transparent, a background-image of none leaves nothing to clip, so every in-progress label was fully invisible — tool.tsx:85, reasoning.tsx:92, activity-run.tsx:131,138, compaction-marker.tsx:42, terminal.tsx:46 all sit behind an isStreaming ? <Shimmer>x</Shimmer> : x ternary, so the text vanished while running and popped in on completion. Only the reduceMotion early-return branch was spared.
I also reproduced the sweep: var(--color- has no remaining non-vendor hits. The packages/vendor/coss-ui hits are correctly out of scope — --color-white / --color-zinc-* come from Tailwind's non-inline default @theme and are emitted normally, and skeleton.tsx:14's arbitrary value does force emission.
ℹ️ A standalone user-visible fix is gated behind an unmerged branch
The base is ruocheng/code-525, not master, and master still carries the broken string. Every streaming label in chat is invisible until this lands, so stacking it behind an unrelated agent-adapter branch delays a fix that has no dependency on that work.
Technical details
# Shimmer fix is stacked behind `ruocheng/code-525`
## Affected sites
- PR base ref — `ruocheng/code-525` (`a730f54f`), whose recent commits are opencode/MCP
agent-adapter work unrelated to the shimmer gradient.
- `packages/presentation/ui/src/chat/shimmer.tsx` on `origin/master` — still has
`var(--color-background)` / `var(--color-muted-foreground)`.
## Required outcome
- A conscious decision about whether this one-line fix should reach `master` on its own
schedule rather than inheriting the parent branch's merge timeline.
## Open questions for the human
- Does this change actually depend on anything in `ruocheng/code-525`? If not, retargeting
the base to `master` makes it mergeable immediately.
- If the stack is intentional (e.g. CODE-575 is scoped as a follow-up to CODE-525), no
action needed — this is purely a sequencing observation.Claude Opus | 𝕏
| { | ||
| backgroundImage: | ||
| 'linear-gradient(90deg, transparent 35%, var(--color-background), transparent 65%), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))', | ||
| // --color-* are @theme-inline-only (no runtime custom properties); use the :root vars. |
There was a problem hiding this comment.
This is the only guard against someone reintroducing the bug, so it's worth making it exact — as written it over-generalizes twice. Tailwind's default palette --color-* (--color-white, --color-zinc-500) comes from a non-inline @theme and does exist at runtime, which is precisely what globals.css:68 relies on; and the @theme inline aliases are emitted when a Tailwind-scanned candidate references them (coss-ui's skeleton.tsx:14 uses [background:…var(--color-muted)…] and gets a real custom property). The actual invariant is that a JS inline style string is never scanned, so nothing forces the alias to be emitted.
Technical details
# Trap comment states the wrong invariant
## Affected sites
- `packages/presentation/ui/src/chat/shimmer.tsx:30` — "`--color-*` are @theme-inline-only
(no runtime custom properties)" is false for Tailwind's default palette and false for
`@theme inline` aliases that a scanned candidate references.
## Required outcome
- The comment should name the real rule: Tailwind emits a theme variable only when
something **it scans** references it via `var()`; a JS inline `style` string is not a
scanned candidate, so coss-ui's semantic `--color-*` aliases never materialize on
`:root` for this call site. Keep it to the repo's 1–2 line comment budget.
## Suggested approach
- Apply the inline suggestion, or any wording that distinguishes "not emitted here" from
"never exists".
## Verification performed
- Compiled `tailwindcss@4.3.3` via its `compile()` API against a minimal repro of
`packages/vendor/coss-ui/src/styles/globals.css`:
- no candidates → `--color-muted:` absent from output
- candidate `bg-muted` → still absent (value inlined into the utility)
- candidate `[background:var(--color-muted)]` → **present** in `:root`
- `--color-white:` / `--color-zinc-100:` → present (non-inline default `@theme`)| // --color-* are @theme-inline-only (no runtime custom properties); use the :root vars. | |
| // coss-ui's semantic --color-* aliases are @theme inline and Tailwind never scans a JS | |
| // style string, so they are not emitted to :root — read the underlying vars directly. |
lucas77778
left a comment
There was a problem hiding this comment.
No blocking findings. Independently verified the Tailwind 4.3.3 behavior: @theme inline utilities inline var(--background) / var(--muted-foreground) and do not emit the corresponding --color-* runtime declarations for this JS inline-style usage. Also confirmed coss-ui defines both replacement variables in :root and .dark, and both renderer entries import the shared stylesheet.
Checked the complete one-commit diff, all Shimmer call sites, reduced-motion behavior, applicable repository/frontend guidance, and current CI results. git diff --check is clean and all required GitHub checks are green. The non-master base affects sequencing only; it does not introduce a correctness dependency in this change.

Summary
The chat
Shimmergradient referencedvar(--color-background)/var(--color-muted-foreground)— Tailwind v4@theme inlinenames that never exist as runtime custom properties, so the wholebackground-imagewas invalid at computed-value time and the shimmer silently rendered with no colors in both themes. Switch to the:root/.darkvars coss-ui actually defines (--background,--muted-foreground), matching the existingcolor-mix(in oklab, var(--foreground) …)pattern inpackages/presentation/ui/src/styles.css.Swept
packages/presentation/ui,packages/client/workbench, and both app renderers for othervar(--color-*)usages — this was the only one.Closes CODE-575
Verification
dev:mockwebview, on a liveShimmer("Running a command" during the showcase's streaming playback): computedbackground-imageresolves to a white sweep over a muted-gray fill in light, and a near-black sweep over a lighter muted fill with thedarkclass toggled. Before the fix the computed value wasnone.pnpm format:check,pnpm lint,pnpm typecheckclean;vitest run2722 passed. The singlegit-mutations.test.tsfailure is the signing-machine environment (productiongit commitpath hits headless gpg pinentry) — the file passes 5/5 withGIT_CONFIG_GLOBAL=/dev/null.Checklist
pnpm check:ciandpnpm testboth pass (pluscargo fmt/clippy/testfor Rust changes)WIRE_PROTOCOL_VERSIONis bumped