Skip to content

fix(web): Cool Darker glass reaches the surfaces it missed - #94

Merged
NoahHendrickson merged 2 commits into
customfrom
t3code/cool-darker-glass-second-pass
Aug 16, 2026
Merged

fix(web): Cool Darker glass reaches the surfaces it missed#94
NoahHendrickson merged 2 commits into
customfrom
t3code/cool-darker-glass-second-pass

Conversation

@NoahHendrickson

Copy link
Copy Markdown
Owner

PR #93 gave Cool Darker its wallpaper glass, but only the surfaces in the chat column were converted. Everything else inside the inset kept painting an opaque fill, so the design-mode panel, the preview's browser bar and the right panel's cards each read as a rectangle of missing wallpaper against a translucent window.

Each one missed for a different reason, and none of them were reachable by the existing .bg-background clear:

  • The design panel paints bg-[var(--fork-design-surface)] — an arbitrary value the class match cannot see. It now paints nothing at all: it sits on the 95% stage, and alpha only compounds toward opaque, so transparent is the most glass available to it. A wash would have made it less glassy than the column it docks into. Its fields become white washes at the context chips' 12%, since with the surface painting nothing they sit on the bare stage. The layers rail beside it was already transparent, by accident of using the bg-background utility.
  • .surface-subheader is declared as @apply ... bg-background, which inlines the fill into its own rule so the element never carries the class. Naming it explicitly beside .bg-background also fixes the diff and file panel headers and the PR code tab, which share the utility.
  • .bg-card was never in the glass set at all. Washed to 5% white — matching the changed-files card so the two read as one kind of surface — and matched by class so the other ~30 users come along.

The floating sidebar keeps its opaque fill, because what's behind it is the transcript rather than the desktop, but it takes a neutral #1f1f1f instead of the palette's cool #181b1e. That value is the docked panel's own resolved colour (88% of the neutral tint over the material's ~99 sample), so only its transparency changes when it lifts off the column. Its seam is ungated from overlay for a related reason: the tint asks what is behind the panel, which overlay changes, while the seam asks how loud its edge is, which it does not.

Guards

.surface-subheader and .bg-card are the third and fourth surfaces to be found opaque after the fact, so the new assertions derive their lists rather than hardcode them: the @apply-ed utilities are read back out of index.css, and cards are matched by class. Also added — the design chrome must stay washes with a field-above-accent ladder, and the floating panel must be opaque and R=G=B.

Two rules now target the v2 panel, so a shared selector picks the tinted one by its overlay gate rather than by source order; three existing guards were relying on it happening to come first in the file. The overlay guard finds the translucent rule by having an alpha, since asserting the gate on a rule defined by that gate would be circular.

353 fork guards pass. The neutrality guard was verified non-vacuous by breaking the value to #1f2022 and confirming the failure before reverting.

Not covered

Screenshots — the reviewer should pull this and look, since I could not capture the running window. Two known artifacts were left deliberately: the design-mode toggle's variant="secondary" pill stays opaque in the browser bar, and the stage's 95%→85% ramp spans the full inset width so the design panel's lower third fades with it. Both are one-liners if wanted.

This only neutralises the cool cast under glass. Cool Darker's base palette is still blue-tinted for web, Linux and any non-macOS window.

Written by Claude Opus 5 (1M context) in Claude Code.

🤖 Generated with Claude Code

PR #93 gave Cool Darker its wallpaper glass but only converted the surfaces
in the chat column. Everything else in the inset kept painting an opaque
fill, so the design-mode panel, the preview's browser bar and the right
panel's cards each read as a rectangle of missing wallpaper.

Each one missed for its own reason, and none were reachable by the existing
`.bg-background` clear:

- The design panel paints `bg-[var(--fork-design-surface)]`, an arbitrary
  value the class match cannot see. It now paints nothing at all — sitting
  on the 95% stage, alpha only compounds toward opaque, so transparent is
  the most glass available to it. Its fields become white washes. The layers
  rail was already transparent by accident of using the utility class.
- `.surface-subheader` is `@apply ... bg-background`, which inlines the fill
  into its own rule so the element never carries the class. Named explicitly
  beside it, which also fixes the diff and file panel headers and the PR
  code tab.
- `.bg-card` was never in the glass set at all. Washed to 5% white, matching
  the changed-files card, by class so the other ~30 users come along.

The floating sidebar keeps its opaque fill — behind it is the transcript,
not the desktop — but takes a neutral #1f1f1f instead of the palette's cool
#181b1e. That is the docked panel's own resolved value, so only transparency
changes when it lifts off the column. Its seam is ungated from overlay for a
related reason: the tint asks what is behind the panel, the seam asks how
loud its edge is, and only the first depends on overlay.

Guards: the design chrome must stay washes, every `@apply`-ed fill must be
named in the clear, cards must be washed by class, and the floating panel
must be opaque and neutral. Two rules now target the v2 panel, so the tinted
one is picked by its overlay gate rather than by source order.

Written by Claude Opus 5 (1M context) in Claude Code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Aug 16, 2026

@NoahHendrickson NoahHendrickson left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review result: changes requested

There is one blocking finding:

  • apps/web/src/theme.custom.palettes.css:502-506 — The new unlayered, high-specificity .bg-card rule fixes background-color for every exact bg-card descendant of the inset. That also wins over Tailwind state utilities such as hover:bg-accent/60. For example, the available “Open a surface” buttons in RightPanelTabs.tsx carry both bg-card and hover:bg-accent/60; under Cool Darker glass they will now remain at the 5% wash on hover, so this presentation change removes their existing interaction feedback. The same issue can affect other exact bg-card controls in Settings. Please preserve the intended state backgrounds (for example with glass-aware state washes or narrower scoping) and add a guard that covers the state precedence.

Validation performed: git diff --check passed; vp test run apps/web/src/__fork_guards__ passed (41 files, 353 tests). I did not run browser-based visual verification because the repository instructions require explicit permission for browser/computer use.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 11.3 KiB 11.3 KiB 0 B (0.0%) 15.1 KiB
Codex Thread snapshot wire 5.5 KiB 5.5 KiB +3 B (+0.1%) 7.3 KiB
Codex Live turn WebSocket wire 5.9 KiB 5.9 KiB −3 B (−0.0%) 7.8 KiB
Codex Live turn WebSocket decoded 49.7 KiB 49.7 KiB 0 B (0.0%) 66.4 KiB
Codex Live turn messages 16 16 0 (0.0%) 21
Claude Total thread wire 11.3 KiB 11.3 KiB +7 B (+0.1%) 15.1 KiB
Claude Thread snapshot wire 5.5 KiB 5.5 KiB +2 B (+0.0%) 7.3 KiB
Claude Live turn WebSocket wire 5.9 KiB 5.9 KiB +5 B (+0.1%) 7.8 KiB
Claude Live turn WebSocket decoded 50.6 KiB 50.6 KiB 0 B (0.0%) 66.4 KiB
Claude Live turn messages 16 16 0 (0.0%) 21

Baseline: feaf628 · PR result: c47948f · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 94.6 KiB
  • Claude decoded thread snapshot: 95.4 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

The new `.bg-card` wash was written like every other rule in the glass set —
unlayered and marker-scoped — so it outranked `hover:bg-accent/60` as well as
the base fill. The "Open a surface" tiles stayed at the base wash under the
pointer, and any focus or active background on an exact `bg-card` element
went the same way. That is interaction feedback disappearing silently, not a
tint being slightly wrong.

The wash now joins Tailwind's cascade instead of sitting above it: `@layer
utilities` puts it in the same layer as `.bg-card`, and `:where()` drops the
marker to zero specificity so the selector is (0,1,0) — a tie with `.bg-card`
that source order wins, and a loss to every (0,2,0) state variant. Hover,
focus and active keep working without being enumerated.

Redefining `--card` was the tidier-looking fix and is the same trap as
`--background`: index.css derives --surface-raised, --code-background and a
ring inset from it, so a 5% wash there would take the transcript's code
blocks with it.

Guards: the wash must sit in the utilities layer and use :where(), the tile
must still carry both classes, and no inset-scoped glass rule may declare
--card. Verified non-vacuous by restoring the unlayered form and confirming
the failure.

Written by Claude Opus 5 (1M context) in Claude Code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@NoahHendrickson

Copy link
Copy Markdown
Owner Author

Confirmed and fixed in c47948f — thanks, this was a real regression and the diagnosis was exact.

Verified before fixing: RightPanelTabs.tsx:230 carries both bg-card and hover:bg-accent/60, and theme.custom.palettes.css has no @layer at all, so the rule sat above Tailwind's layers entirely — it would have won on layer precedence regardless of the specificity gap.

The fix. The wash now joins Tailwind's cascade instead of sitting above it: @layer utilities puts it in the same layer as .bg-card, and :where() around the marker drops the whole selector to (0,1,0). That ties .bg-card and wins on source order (main.tsx imports this file after index.css), while every (0,2,0) state variant now beats it. Hover, focus, active and data-state backgrounds all keep working, and none of them have to be enumerated — which matters given this rule exists precisely to avoid enumeration.

On the alternative. I tried redefining --card under the marker first, since it preserves state precedence naturally and is much easier to read. It's a trap: index.css derives --surface-raised, --code-background and a ring inset from var(--card), so a 5% wash there would have taken the transcript's code blocks with it — the same failure the --background note in this file already documents. Guarded now: no inset-scoped glass rule may declare --card.

Guards added, per your request for state-precedence coverage:

  • the card wash must live in @layer utilities and must use :where()
  • RightPanelTabs.tsx must still carry both classes, so the protected case can't quietly disappear
  • no inset-scoped glass rule may declare --card

Verified non-vacuous by reverting the rule to its unlayered form and confirming the failure (expected [] to include '@layer utilities') before restoring. 353 fork guards pass.

One thing I deliberately did not fix. The pre-existing .bg-background clear has the identical precedence shape — ThreadTerminalDrawer.tsx and KeybindingsSettings.tsx both carry bg-background alongside a hover background, and both lose that hover under glass today. It arrived with #93 rather than this PR, and the same layer treatment would fix it, but that clear is load-bearing for the stage and I can't verify it visually from here. Recorded in the manifest as known-and-unfixed; happy to take it in this PR if you'd rather it not linger.

@NoahHendrickson
NoahHendrickson merged commit 0b85fd1 into custom Aug 16, 2026
11 checks passed
@NoahHendrickson
NoahHendrickson deleted the t3code/cool-darker-glass-second-pass branch August 16, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant