fix(web): stop the "Needs response" tag overlapping the session title - #4375
Conversation
|
❌ UI Snapshot doesn't match the committed baseline. If this UI change is intentional, update the baseline — each path renders in the same pinned image, so the result matches this gate:
Diff PNGs ( |
|
f07efca to
5eebc07
Compare
The tag is absolutely positioned, so the row's right padding is the only thing holding the title clear of it. That reserve narrows to make room for the trailing pin/kebab -- but it narrowed on `group-focus-within`, while the tag fades (and the controls appear) on `group-has-[:focus-visible]`. `focus-within` matches a plain mouse click; `:focus-visible` does not. Clicking a row therefore cut the reserve from 116px to 56px with the tag still fully opaque and the controls still hidden, sliding the title 59px underneath it. The tag surface is translucent, so the collision reads as a washed-out opacity glitch rather than the layout problem it is. Key the reserve on `group-has-[:focus-visible]` so it narrows exactly when the tag fades and the controls appear -- the three can no longer disagree about whether that space is free. Measured on the selected row: +59.4px of overlap -> -0.6px, with the idle row's title width byte-identical (120px at every interface font size), so nothing truncates earlier than before. Note this is the selected-state defect only. A row at interface font 15px+ still overlaps in *every* state, including idle, because the 116px reserve is fixed while the tag's width tracks the font size; that is a separate pre-existing bug and is left alone here. Covered two ways: a unit test pinning that the reserve and the tag's fade share their triggers (the class-level contract), and a Playwright test measuring the real painted glyphs against the tag's edge after a click (jsdom reports every box as 0x0, so geometry needs a browser). Both were confirmed to fail with the `focus-within` trigger restored. Also repoints the Inbox count bubble from the shared amber `--warning` to `--brand-accent`, matching the pink the tag and unread dot already use. Signed-off-by: Edwin He <41037314+Edwinhe03@users.noreply.github.com> Co-authored-by: Isaac
0e67fa4 to
60007eb
Compare
… Inbox badge Regenerated in the digest-pinned Playwright image the gate renders in, so the bytes match what CI compares against. Only the populated-sidebar baseline drifts; the other four visual snapshots render identically. The diff is a single 16x16px region at (288,118) -- the Inbox count bubble, amber (218,164,71) -> brand pink (227,87,150). Nothing else in the 1280x800 frame changes, and the row-reserve fix contributes no pixel delta here (the fixture's awaiting row is idle, whose geometry is unchanged). Signed-off-by: Edwin He <41037314+Edwinhe03@users.noreply.github.com> Co-authored-by: Isaac
|
🏷️ Doc impact: Internal CSS/layout bugfix to the sidebar row padding and badge color with tests; it fixes a visual glitch without changing any documented user-facing behavior, config, or integration. Auto-classified on merge. Set the label manually before merging to override. · run |
Related issue
Closes #4373
Summary
The sidebar's "Needs response" tag is absolutely positioned, so the row's right
padding is the only thing holding the title clear of it. That reserve narrows to
make room for the trailing pin/kebab — but it narrowed on
group-focus-within,while the tag fades out (and the controls appear) on
group-has-[:focus-visible].focus-withinmatches a plain mouse click;:focus-visibledoes not. Clickinga row therefore cut the reserve from 116px to 56px with the tag still fully
opaque and the controls still hidden, sliding the title 59px underneath it.
Because the tag surface is translucent (
bg-brand-accent/15), the collisionreads as a washed-out opacity glitch rather than the layout problem it is.
Fix: key the reserve on
group-has-[:focus-visible]so it narrows exactlywhen the tag fades and the controls appear — the three can no longer disagree
about whether that space is free.
Also repoints the Inbox count bubble from the shared amber
--warningto--brand-accent, matching the pink the tag and unread dot already use.Scope
This is the selected-state defect only. A row at interface font 15px+ still
overlaps in every state, idle included, because the 116px reserve is fixed
while the tag's width tracks the font size (97px at 11px → 147px at 18px). That
is a separate pre-existing bug and is deliberately left alone — the obvious
structural fix for it (rendering the tag in the title flow so the browser derives
the reserve) costs real title width, up to 45px at 18px, so it needs its own
change and its own discussion.
ELI5 + diagram
Three things have to agree about whether the space at the row's right edge is
free: the padding that reserves it, the tag that sits in it, and the buttons that
replace the tag. Two of them listened for "keyboard focus"; the padding listened
for "any focus at all," which includes an ordinary click. So a click moved the
title into space the tag had not vacated.
Test Plan
Automated — from
web/:e2e — from the repo root:
uv run pytest tests/e2e_ui/sessions/test_sidebar_awaiting_tag_layout.py --ui-skip-build # 1 passedCovered two ways, because each catches what the other can't:
Sidebar.test.tsx) pins the class-level contract: every trigger thatnarrows the reserve must also fade the tag, and the row must not use
focus-within.since the title clips with
text-overflow: ellipsis) against the tag's leftedge, after an actual click with the pointer moved away. jsdom reports every
box as 0×0, so geometry needs a browser.
Both were confirmed to fail with the
focus-withintrigger restored, soneither passes vacuously. The e2e failure reproduces the reported number exactly:
-0.6px when idle → +59.4px selected.Measured,
mainvs this branch (260px sidebar, interface font 13px):Idle title width is unchanged at 120px across 11/13/18px, so nothing
truncates earlier than it did on
main.Manual: open a session with a pending approval prompt and click its sidebar
row. The title should ellipsise before the pink tag instead of running under it.
Hover and keyboard-tab to the row as well — the tag should fade as the pin/kebab
appear, exactly as before.
Demo
main(left) vs this branch (right), both showing a clicked/selected row,rendered against the real compiled CSS:
Visual baseline
UI Snapshotdrifts on exactly one of the five baselines,test_populated_sidebar— the only frame whose fixture renders an Inbox count(the landing fixture stubs an empty session list). Regenerated in the
digest-pinned Playwright image the gate itself renders in, so the bytes match.
The diff is a single 16×16px region at (288,118): the Inbox bubble, amber
(218,164,71)→ brand pink(227,87,150). Nothing else in the 1280×800 framemoves, and the reserve fix contributes no pixel delta here (the fixture's
awaiting row is idle, whose geometry is unchanged).
Type of change
Test coverage
Coverage notes
Added one unit test (the trigger contract) and one Playwright test (the rendered
geometry), and updated the existing padding-class assertion in
Sidebar.test.tsx. Each was verified to fail with the bug reintroduced.The pre-existing 2536
src/shell+src/componentstests still pass. Theycovered the badge's behaviour (which state renders which marker, precedence of
the approval tag over the unread dot) but could not catch this: it is a
trigger mismatch whose only symptom is geometric, and jsdom reports every box as
0×0.
Changelog
Clicking a sidebar session that needs a response no longer runs its title under the "Needs response" tag, and the Inbox count badge now matches the sidebar's pink accent