fix(ui): align picker list-header surface and disable stuck gradient in dark mode#888
Conversation
The picker.css override of List's grouped header used `[data-picker-content] [data-slot="list-header"]` (specificity 0,2,0), which loses to list.css's default rule on the longer `[data-component="list"] [data-slot="list-scroll"] [data-slot="list-group"] [data-slot="list-header"]` chain (specificity 0,4,0). The header therefore stayed `position: sticky` inside pickers despite the override, and the default 16px `::after` linear-gradient (--surface-base → transparent) kept painting on top of the first item once GroupHeader flipped `data-stuck="true"` on scroll. In dark mode the visual gap is wider because the header background uses --surface-base (#1a1917) while the picker body uses --surface-raised (#2d2a27), so the sticky band reads as a darker stripe overlapping the first model row. Match the 0,4,0 chain in picker.css so the override actually applies, swap the header background to --surface-raised so it sits flush with the picker surface, and hide the stuck gradient inside pickers where the header is no longer sticky. command-palette.css already uses the same scoped pattern; this brings the generic picker contract in line. Add e2e/snap/model-picker-header.snap.ts as the regression check — captures the open model picker in light and dark so any future drop in selector strength or surface-token drift shows up in the grid.
|
Warning Review limit reached
Your plan includes 1 review of capacity. Refill in 5 minutes and 26 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a new visual regression test for the model picker header and updates the picker's CSS to ensure correct background colors and non-sticky behavior in dark mode. Feedback includes a suggestion to use CSS nesting for the updated styles to improve maintainability and a recommendation to avoid using .first() in E2E locators to ensure test stability.
Perf delta summaryComparator: pass
|
Address Gemini review feedback: nest the ::after { display: none } under
the header rule instead of repeating the four-deep selector chain. Same
compiled CSS, less duplication, matches list.css's nesting style.
Address review P2: the snap-only check screenshots the picker at rest, but the underlying `data-stuck="true"` + ::after gradient bug only paints once the list is scrolled. The fixture renders too few models to overflow the picker's max-h-[400px] body, so a scroll-driven snap would not reliably trigger stuck. Read the computed style directly instead: header.position must be `static`, header.backgroundColor must match the resolved --surface-raised token, and ::after.display must be `none`. Any future selector demotion below list.css's 0,4,0 chain, or a token swap, fails the test even though the screenshot would still look right at rest.
|
Thanks for the review. Both addressed: P2 — snap coverage gap on stuck state: Verified the concern is real — Stronger fix in d0e947d: read the computed styles directly instead of relying on the screenshot.
This is tighter than scrolling: a regression that re-introduces the bug fails the test even on an unscrolled, single-group picker, where a scroll-driven snap might not have been able to trigger it at all. P3 — CSS selector repetition: Already landed in 86dee217c3 (one commit before this review) — |
Summary
Fix two visible bugs in the model picker (and any
<List>rendered inside a[data-picker-content]popover):Why
picker.css already tried to disable List's sticky header behaviour, but its override
[data-picker-content] [data-slot="list-header"](specificity0,2,0) lost to list.css's default chain[data-component="list"] [data-slot="list-scroll"] [data-slot="list-group"] [data-slot="list-header"](specificity0,4,0). The override never applied: the header stayedposition: stickyand the default 16px::afterlinear-gradient(--surface-base → transparent)painted on top of the first item onceGroupHeaderflippeddata-stuck="true"on scroll.In dark mode the bug is doubly visible because List's default header background is
--surface-base(#1a1917) while picker content uses--surface-raised(#2d2a27), so the sticky band reads as a darker stripe overlapping the first model row. In light mode both tokens are#ffffff, which is why bug 1 only manifests in dark.Fix in
packages/ui/src/components/picker.css:0,4,0selector chain so picker.css (imported after list.css) wins on equal specificity.background: var(--surface-raised)so the header sits flush with the picker body.::afterstuck gradient inside pickers — the header is no longer sticky and the gradient has nothing to fade into.command-palette.cssalready uses the same scoped pattern; this aligns the generic picker contract with it.Related Issue
None — surfaced by visual inspection.
Human Review Status
Pending
Review Focus
packages/ui/src/components/picker.css— confirm the selector chain matches list.css's default rule exactly so the override outranks it on cascade order. Anything shorter silently loses.[data-picker-content]and does not change<List>behaviour outside pickers (sidebar session list, dialogs, etc.).packages/app/e2e/snap/model-picker-header.snap.tscaptures the contract in light and dark.Risk Notes
Scope is CSS only, gated on
[data-picker-content]. No JS or other consumers touched. Did not change list.css's default header background —Listis a generic primitive and binding its default to--surface-raisedwould silently flip every non-picker consumer (sidebar, dialogs). The picker-scoped override is the smallest correct fix.How To Verify
Screenshots or Recordings
Checklist
bugapp,uiP2Pending,Approved by @<reviewer>, orNot required: <reason>(default isPending; "not required" is restricted to bot-authored low-risk PRs).dev, and my PR title and commit messages use Conventional Commits in English.