fix(search): stop the results bar clipping its own controls on phones - #1672
Conversation
The one-line results band (#1615) left the inline utilities group `shrink`, so an over-subscribed line paid the shortfall out of the *controls* instead of the truncating query. The sort group was severed mid-glyph by the utility track's `overflow-x-auto` and its trailing option was then washed out by the 28px overflow mask, which reads as a rendering fault rather than as "swipe for more". Measured on the live app before the fix: clipped at 320/375/390/393/402 and 430/440px, and by 41.9px at 540px with a four-word query — so this was never bounded by phone width, and the query truncated a 7-character word alongside it. - Inline utilities are `shrink-0`. The band's own contract is that the query is what gives way when the line runs out; `truncate` + `min-w-[2rem]` on the heading already exist to absorb exactly this. - Below 414px one line cannot hold count + query + sort + filter even with the query fully truncated, so the utilities take their own full-width row rather than overflow a band that is `overflow-hidden` and would clip the pinned Filter. - Sort options drop to `px-2.5` below `sm`, returning the 8px that clears the one-line widths. Tap height is untouched. - The Filter wordmark now hides only across 414–429px, the one genuinely single-line-and-short band, instead of everywhere below 430px. Also fixes the Filter trigger reading as a different component from the sort group it sits flush against. It composed `floatingControl` plus an override string, and `cn` is a plain join, not tailwind-merge — so `font-semibold` (600 against the sort group's 470/560) and `--border-lux` (a visibly darker stroke than `--border`) both reached the DOM and stylesheet order picked the winner. It now uses the band's own control recipe, with the active and resting colours as mutually exclusive branches. Gates: `ui-smoke` sweeps the rail across 320–540px as geometry, not as a class — verified to fail (`sortClipped: 16, masked: true` at 414px) with `shrink` reinstated, and to pass with the fix. A single-width check at the existing 390px viewport passes against the broken code, which is why the sweep crosses 414px. `expectNoPageHorizontalOverflow` cannot see an internal scroller, which is how this shipped green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BkKbgdhhAg8REq2wudR8V
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in:30 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe document search header now prevents utility shrinking, wraps utilities below 414px, adjusts sort spacing, and applies explicit active or inactive filter styling. DOM and UI smoke tests cover responsive layout and overflow across phone widths. ChangesSearch-results responsive layout
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015BkKbgdhhAg8REq2wudR8V
There was a problem hiding this comment.
Pull request overview
Fixes a mobile layout regression in SearchResultsHeaderBand where the horizontally scrollable utility rail could clip/fade its own controls (notably the Sort group) instead of forcing the query to truncate, and aligns the document Filter trigger styling with the band’s established control recipe. The change is concentrated in the shared results header UI used across modes, with added DOM + Playwright coverage and supporting docs.
Changes:
- Make inline utilities refuse to shrink (
shrink-0) and wrap utilities to a dedicated row below 414px to prevent control clipping on phones. - Tighten Sort option horizontal padding below
sm, and rework the document Filter trigger to avoid conflicting utility overrides (sincecndoes not merge Tailwind classes). - Add/extend tests (DOM +
ui-smoke) to assert the “no clipped controls” geometry across multiple phone widths; update docs and ledger entry.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/ui-smoke.spec.ts | Adds a multi-viewport geometry sweep ensuring the utility rail does not overflow/clip/mask its own controls. |
| tests/search-results-header-band.dom.test.tsx | Pins the key class-level contracts for inline utilities (shrink-0 + <414px wrap) in jsdom. |
| src/components/clinical-dashboard/search-results-header-band.tsx | Implements the layout fixes: inline utility group shrink-0, wrap behavior below 414px, and reduced Sort padding below sm. |
| src/components/clinical-dashboard/document-search-results.tsx | Rebuilds the Filter trigger classes to match band control styling without relying on conflicting overrides; adjusts label hiding to a narrower width range. |
| docs/search-results-bar-decisions.md | Updates the documented rationale/constraints to match the measured overflow behavior and new layout contract. |
| docs/branch-review-ledger.md | Appends the review/verification record for this change set. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Behind-but-clean (merge-tree clean). Clears GitHub DIRTY/mergeability blocker after required CI settled on the pre-sync tip.
The shrink-0 / max-[413px] child checks were green even if the parent lost max-[413px]:flex-wrap, and the ui-smoke rail-fit sweep could not see wrap failure when overflow-hidden clipped the utilities off-screen. Assert the parent wrap class in DOM, and require the utilities group to stay in the viewport below 414px.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/clinical-dashboard/search-results-header-band.tsx`:
- Around line 294-302: Define semantic --breakpoint-* and --spacing-* `@theme`
tokens in src/app/globals.css for the search-band thresholds and padding, then
replace the raw utilities in search-results-header-band.tsx (294-302 and
435-454) and document-search-results.tsx (713-749) with the named utilities.
Update the related comments and DOM assertions in
tests/search-results-header-band.dom.test.tsx (273-307) to use those names,
preserving the 413px wrapping boundary and 414–429px label range.
🪄 Autofix
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7bf31595-83d3-4746-bd4f-9a17d666212d
📒 Files selected for processing (6)
docs/branch-review-ledger.mddocs/search-results-bar-decisions.mdsrc/components/clinical-dashboard/document-search-results.tsxsrc/components/clinical-dashboard/search-results-header-band.tsxtests/search-results-header-band.dom.test.tsxtests/ui-smoke.spec.ts
Uh oh!
There was an error while loading. Please reload this page.
Cursor review-and-fix summary — PR #1672Tip: Sync
Review findings
Disposition table
Required CI (this tip |
Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found. |
BigSimmo
commented
Aug 7, 2026
Unblock summary — PR #1672Tip: Sync / merge-tree
What blocked / what was done
Required CI (this tip)
Residual risks
Merge left to you / auto-merge. |
Summary
shrink, so an over-subscribed line paid the shortfall out of the controls rather than out of the truncating query. The sort group was severed mid-glyph by the utility track'soverflow-x-auto, and its trailing option was then washed out by the 28px overflow mask — which reads as a rendering fault, not as "swipe for more". Measured on the live app before the fix: clipped at 320/375/390/393/402 and 430/440px, and by 41.9px at 540px with a four-word query, so this was never bounded by phone width. The query truncated a 7-character word ("Lithiu…") alongside it.shrink-0. The band's own contract is that the query is what gives way when the line runs out;truncate+min-w-[2rem]on the query heading already exist to absorb exactly this.overflow-hidden, so overflowing would clip the pinned Filter instead.px-2.5belowsm, returning the 8px that clears the one-line widths. Tap height is untouched —min-h-tapis the floor that matters.floatingControlplus an override string, andcnis a plain join, not tailwind-merge — sofont-semibold(600 against the sort group's 470/560) and--border-lux(a visibly darker stroke than--border) both reached the DOM and stylesheet order, not intent, picked the winner. It now uses the band's own control recipe (the same oneSave searchandRetryuse), with active and resting colours as mutually exclusive branches.docs/search-results-bar-decisions.mdclaimed the library button was "the sole reason the phone rail could overflow at all". That was measured wrong — with Library gone the rail still overflowed at every common phone width. The claim is amended in the doc and in theui-smokecomment that repeated it.Verification
npm run lint— exit 0, no findingsnpm run typecheck— exit 0, no findingsnpx prettier --check .—All matched files use Prettier code style!tests/search-results-header-band.dom.test.tsx+tests/document-search-scope-zero-results.dom.test.tsx—Test Files 2 passed (2) / Tests 52 passed (52)contractsstage, 9 files) —Test Files 9 passed (9) / Tests 118 passed (118)ui-smoke+ui-toolson Chromium against a production build — 185 passed, 1 failednpm run verify:pr-localUI verification not run in full:
npm run verify:phone-chromestops at its ownlock-parityprecondition in this container —playwright: installed 1.62.0 does not match locked 1.62.1— which is pre-existing environment drift, not a property of this diff. Its remaining stages were run directly instead: the contract set above, and thechanged-browserPlaywright stage viaPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH(the container ships Chromium 1194, not the pinned build).The one
ui-smokefailure is the known pre-existing PDF-canvas test (document viewer puts the PDF preview first with pinned evidence after it on mobile). Confirmed pre-existing by stashing this branch's changes and re-running it on the clean baseline: it fails identically, same test and sametoBeVisibleassertion. The same failure is recorded against PR #1555 in the branch review ledger with the same Chromium-build cause.The new gate was proven to fail, not just to pass.
ui-smokenow sweeps the utility rail across 320/375/390/402/414/430/440/540px and asserts fit as geometry. Withshrinkreinstated it fails at 414px withsortClipped: 16, masked: true; with the fix it passes. A single-width assertion at the test's existing 390px viewport passes against the broken code — at 390 the band wraps and the rail has width to spare — which is why the sweep has to cross 414px.expectNoPageHorizontalOverflowcannot see an internal scroller, which is how the original defect shipped green.Risk and rollout
shrink-0(which redirects flex shortfall to the query heading that already truncates) and themax-[413px]wrap (which restores the pre-Results bar: one line, one filter, nothing unreachable #1615 two-row geometry below 414px). No data, retrieval, auth, or API path is touched. The eleven modes that usemobileControlsPlacement="row"are untouched — both new rules are inside theinlinebranch, which onlydocumentsandtherapy-compassopt into.git revertthe single commit. No migration, no config, no stored state.Notes
classifyPullRequestFilesreturnsclinicalRisk: false,operationalRisk: false,ragRanking: false,ui: true, so neither the Clinical Governance Preflight nor a mandatoryRAG impact:line applies; the line is stated here for the record rather than because the gate requires it.Generated by Claude Code
Summary by CodeRabbit
Bug Fixes
Tests