refactor(menu): drop redundant caller-level menu height caps - #7760
Conversation
b82ed81 to
af61e5b
Compare
GPT 5.6 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
UX Review (Fable 5, fork) — ✅ PASSUX-level review of The base primitive already carries UX-Verdict: PASS Pure short-viewport robustness fix: dropdowns now defer to the primitive's viewport cap, so no menu clips off-screen and every picker keeps its compact height. [UX-REVIEWED] 6ac9cf3 |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed |
Design Review (Fable 5, fork) — ✅ PASSDesign-level review of The patch checks out against the base: the primitive ( Design-Verdict: PASS Redundant caps removed and intentional ones correctly composed via Suggestions
[DESIGN-REVIEWED] 6ac9cf3 |
First Principles Review (Fable 5, fork) —
|
af61e5b to
553d496
Compare
|
Both review findings were legitimate and are fixed at root cause in Design Review (CONCERNS) — the tailwind-merge override: Correct. First Principles (advisory) — the residual Re-verified on the new head: |
553d496 to
269771a
Compare
269771a to
07c83d8
Compare
Open PR relationship auditThis is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion. Relationship findings
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
The shared Radix menu primitives now cap content to max-h-[var(--radix-*-content-available-height)] and scroll, so caller-level viewport-fraction caps that only restated "do not overflow the viewport" are now redundant second spellings. Remove the two vh-based caps (InstanceTabBar switcher 60vh, ChatSidebar filter menu 70vh) whose sole job the primitive now does; they fully defer to the primitive (which keeps the never-clip viewport floor). For the genuinely tighter fixed-pixel caps (folder/crew/MCP/artifact list submenus), compose the cap with the primitive's available-height var via max-h-[min(<px>,var(--radix-dropdown-menu-content-available-height))] -- the codebase's existing pattern (AgentSelector.tsx). A bare max-h-[<px>] would OVERRIDE the primitive's cap (cn()'s tailwind-merge dedupes max-h-*), losing the never-clip floor on a short viewport; the min() form stays tighter while preserving that floor. Drop the now- redundant overflow-y-auto from those sites (the primitive supplies it), and annotate each with the accurate model.
07c83d8 to
6ac9cf3
Compare
|
Thanks @bolichen97 – will keep this open per your suggestion. Confirming that this is ready for review |
bolichen97
left a comment
There was a problem hiding this comment.
Approving — className-only cleanup, no interaction or layout redesign.
Verified the premise directly against website/src/components/ui/dropdown-menu.tsx on this head: both DropdownMenuContent (line 28) and DropdownMenuSubContent (line 133) already carry max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overscroll-contain, and Radix publishes that same var name on Content and SubContent alike. So every caller cap this PR removes really is a second spelling of the primitive's, and every dropped overflow-y-auto really is supplied below.
Per-site check:
InstanceTabBar.tsxandChatSidebar.tsxfilter menu: pure viewport-fraction caps (60vh/70vh) removed, nothing else touched. Themax-w-[calc(100vw-1rem)]phone-fit constraint is correctly left in place — the primitive supplies no horizontal cap.- The five kept fixed-pixel caps are spelled
max-h-[min(<px>,var(--radix-dropdown-menu-content-available-height))], which is the right composition rather than a baremax-h-[<px>]:cn()'s tailwind-merge dedupesmax-h-*, so a bare pixel cap would replace the primitive's and drop the never-clip viewport floor on a short viewport. Matches the existing precedent inAgentSelector.tsx:264(correctly using the popover var for its own primitive). - The
ChatSidebarfolder-ordering comment is updated to cite the primitive instead of the deleted callermax-h— the comment no longer describes code that is gone. - No component, handler, aria attribute, item, or ordering changed anywhere in the diff; the only removals are
max-h/overflow-y-autoutilities. Consistent with<!-- no-visual-delta -->.
The one real behavioral delta is the acknowledged edge case: on a tall viewport the two uncapped menus may grow past the old fixed fraction before scrolling. Still viewport-bounded and still scrollable via the primitive, and it is exactly the primitive default this PR is aligning to, so it is in scope rather than scope creep.
No new tests, which is fine here: min() against a Radix runtime CSS var is not computable in jsdom, so the only possible assertion would be on the class string, and menuContentOverflow.test.tsx from #7186 already pins the primitive contract these sites now inherit.
All checks pass (61 pass, 6 skipped, 0 failing).
Problem / Motivation
Now that the shared Radix menu primitives cap content height to
max-h-[var(--radix-*-content-available-height)] overflow-y-auto overscroll-containand scroll (PR #7186), several dashboard callers still carry their own
per-menu
max-h-[…] overflow-y-autopoint patches that predate theprimitive-level fix. Those are redundant second spellings that can drift
from the primitive over time.
Why it matters
A caller cap whose only purpose was "don't let the menu overflow the
viewport, scroll instead" now duplicates behavior the primitive
guarantees. Leaving the duplicates in place invites drift: a future
change to the primitive default silently stops applying at those sites,
and a reader can't tell an intentional tighter cap from a leftover patch.
What changed
A per-site pass over the caps the issue lists, splitting them by intent:
Removed the two caps whose sole job the primitive now does — pure
"don't exceed the viewport" viewport-fraction caps:
InstanceTabBar.tsx— instance switcher menumax-h-[60vh] overflow-y-autoChatSidebar.tsx— session filter menumax-h-[70vh] overflow-y-auto(The
max-w-[calc(100vw-1rem)]phone-fit cap on the filter menu is ahorizontal constraint the primitive does not provide, so it stays.)
Kept, but composed correctly — the genuinely tighter fixed-pixel
caps that hold a growable list submenu compact. Because
cn()runstailwind-merge, which dedupesmax-h-*, a baremax-h-[<px>]wouldoverride the primitive's
max-h-[var(--radix-*-content-available-height)]and lose the never-clip viewport floor on a short viewport. So each kept
cap is spelled
max-h-[min(<px>,var(--radix-dropdown-menu-content-available-height))](the codebase's existing pattern in
AgentSelector.tsx) — genuinelytighter and still floored to the viewport. Their now-redundant
overflow-y-autois dropped (the primitive supplies it), and each isannotated with the accurate model:
CronJobMoveMenu.tsxfolder picker (240px)CronRowActions.tsxmove-to-folder submenu (240px)ArtifactDetailPage.tsxfolder picker (300px)ChatPage.tsxMCP tools submenu (340px)ChatSidebar.tsxnew-chat-in-folder / new-chat-on-crew submenus (300px)Updated the
ChatSidebarfolder-scroll rationale comment to referencethe primitive cap instead of the removed caller
max-h.The plain
<div className="max-h-… overflow-y-auto">inner scrollregions in these files are not menu-content primitives and are out of
scope — left untouched.
This matches the shape the triage comment suggested: a mechanical removal
of the caps the primitive default subsumes, plus a short annotated list
of the intentionally-tighter caps that remain. Behavior at the two
removed sites now follows the primitive (cap to available viewport
height, scroll) instead of a fixed viewport fraction; both still scroll
and never clip.
Tests
npx tsc -b— clean.npx eslinton all six changed files — 0 errors, 0 new warnings.npx vitest runon the suites that exercise the touched menus —67/67 pass:
menuContentOverflow.test.tsx(the primitive cap/scroll test from fix(menu): cap dropdown/context menus to viewport height and scroll #7186)InstanceTabBar.test.tsxcronFoldersSchedulePage.test.tsxChatSidebar.createMenu.test.tsxkept caps now compose via
min()so the primitive's viewport floor ispreserved, and the duplicate
overflow-y-autois removed.Screenshots / video
Why no screenshot: there is no rendered delta at rest or in the
common case — the change only swaps two menus' redundant fixed
viewport-fraction caps (
60vh/70vh) for the primitive's ownmax-h-[var(--radix-*-content-available-height)], which Radix alreadyapplies, so a short menu renders at content height and a full one still
scrolls exactly as before. The only reachable difference is an edge case
(a menu whose content exceeds the fraction on a tall viewport where the
primitive's available-height is larger): it may grow marginally taller
before scrolling instead of scrolling at the fixed fraction — still
capped to the viewport, still scrollable, never clipped. A screenshot at
rest would show no difference and would be misleading evidence for that
edge case; the four kept caps and all comment/annotation changes have no
visual effect at all.
Pattern harvest
Not generalizable: a one-off cleanup of duplicated CSS caps against a
primitive default; no reusable rule.
Related Issues
Fixes #7227