Skip to content

fix(ui): reveal button group chrome only on interaction - #387

Open
astyfx wants to merge 1 commit into
mainfrom
fix/button-group-resting-chrome
Open

fix(ui): reveal button group chrome only on interaction#387
astyfx wants to merge 1 commit into
mainfrom
fix/button-group-resting-chrome

Conversation

@astyfx

@astyfx astyfx commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

ButtonGroup (added in #383) carried a permanent hairline outline. In the composer toolbar — a row of otherwise bare ghost buttons — that made the advisor and worker mode toggles the only bordered controls in the row, so they read as form fields rather than as peers of the buttons beside them.

The outline is now an interaction state, not the control's identity: at rest a group weighs exactly what the single-segment ghost button next to it weighs, and hovering, keyboard-focusing, or opening its menu draws the halves together.

Key changes

Single file: src/components/ui/button-group.tsx (+45/−8, mostly rationale comments). No consumer changes.

  • Outline and segment divider move to hover / focus-within / has-[[aria-expanded=true]], wired through a named group/button-group so the divider tracks the same three conditions as the border.
  • border-transparent rather than no border, matching the shared Button base — the box is always laid out, so revealing it cannot shift the toolbar.
  • Revealed weight is border/70, up from the /55 this carried while permanent. An outline that exists only during interaction can afford to be decisive; the earlier weight was heavy because it was always on, not because it was dark.

Why /70 specifically

/45 was the first candidate and it fails. Computing border-on-card OKLab lightness delta across all 16 built-in themes:

candidate worst-case theme delta
border/45 solarized-light 0.0246 — below where a hairline reads
border/70 solarized-light 0.0382 ✓

solarized-light puts border only 0.055 from card, so any further transparency erases it. This is not something eyeballing the default light/dark pair would have caught.

Verification

  • bun run typecheck clean
  • Gates: reliability-gates, doc-paths, max-lines-ratchet, switch-exhaustiveness all pass
  • tests/prompt-input-advisor-mode.test.ts + tests/advisor.test.ts — 101 pass / 0 fail
  • Runtime computed-style probe confirmed all three reveal conditions actually fire on both the group border and the divider (named-group + has-[] composition under Tailwind v4 was the part worth doubting):
    PROBE[dark]  rest={border:0, sep:0}  hover={0.7,0.7}  focus={0.7,0.7}  open={0.7,0.7}
    PROBE[light] rest={border:0, sep:0}  hover={0.7,0.7}  focus={0.7,0.7}  open={0.7,0.7}
    
  • Keyboard focus ring verified intact — the pre-existing overflow-hidden does not clip it
  • Zero theme-file changes, zero new tokens: reuses the existing border token

Notes

Deliberately no group background behind the reveal. A muted/25 wash measured under 0.02 lightness against both composer surfaces — imperceptible — while each segment's own hover tint is what actually reads. The enclosing border is what says "one object".

🤖 Generated with Claude Code

ButtonGroup carried a permanent hairline outline, which made the advisor
and worker mode toggles read as form fields dropped into a composer
toolbar of otherwise bare ghost buttons — the only bordered controls in
the row.

Move the outline and the segment divider to interaction states
(hover / focus-within / an open menu), so at rest a group weighs exactly
what the single-segment ghost button beside it weighs. The border box is
kept laid out via border-transparent, matching the shared Button base, so
revealing it cannot shift the toolbar.

The revealed weight goes to border/70 rather than the /55 this carried
while permanent: an outline that only exists during interaction can
afford to be decisive, and /70 is what keeps it legible in the
lowest-contrast built-in theme (solarized-light, where border sits 0.055
OKLab lightness from card — /45 measured 0.025, below the threshold where
a hairline reads at all).

No new theme tokens; no consumer changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@astyfx
astyfx requested a review from a team as a code owner August 24, 2026 01:25
@astyfx
astyfx requested review from heath-s and png-1105 and removed request for a team August 24, 2026 01:25
@upwind-code-us

upwind-code-us Bot commented Aug 24, 2026

Copy link
Copy Markdown

Upwind Upwind Code Scan - ✅ Proceed with Deployment

0 newly introduced vulnerabilities · 0 resolved · 81 total in this PR vs main

🔴 1 Critical | 🔶 30 High | 🟡 47 Medium | 🟢 3 Low

View full analysis in Upwind Console

Scan completed in 19s

Scan history (1 scan)
Commit Scanned at New Resolved Net
f409441 < 2026-08-24 01:25 UTC 0 0 0

Last scanned: f409441 · 2026-08-24 01:25 UTC

@upwind-code-us

upwind-code-us Bot commented Aug 24, 2026

Copy link
Copy Markdown

Upwind Upwind IaC Scan - ✅ Proceed with Deployment

0 newly introduced misconfigurations · 0 resolved · 0 total in this PR vs main

View full analysis in Upwind Console →

Scan completed in 2s

Scan history (1 scan)
Commit Scanned at New Resolved Net
f409441 < 2026-08-24 01:25 UTC 0 0 0

Last scanned: f409441 · 2026-08-24 01:25 UTC

@heath-s heath-s left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants