fix(layout): topbar grid auto-expanded center column hid right buttons at 780-1280
Some checks failed
Build & Push Docker Images / lint (push) Successful in 2m37s
Build & Push Docker Images / build-and-push (push) Has been cancelled

User reported the search bar dropping to a second row + the top-right
buttons (+ New / Inbox / Avatar) going missing as they resized the
browser. Playwright probe confirmed: at every width 780-1280 the
search bar's intrinsic `max-w-2xl` (672px) forced the topbar's
center grid column to expand to that width, leaving the right
column too narrow to hold "+ New + Inbox + Avatar" without
overlapping the search OR going off-screen.

Two coordinated fixes:

1. Grid template `auto_1fr_auto` instead of `1fr_minmax(280,800)_1fr`.
   Side columns now size to their actual content (logo + breadcrumbs
   on the left; New + Inbox + Avatar on the right); the center
   column takes whatever's left. No more "intrinsic content forces
   the column to grow" behaviour.

2. Search wrapper max-width scales by tier: max-w-md (448px) at
   base, lg:max-w-xl (576px), xl:max-w-2xl (672px). Generous enough
   on wide screens, restrained enough on narrow ones so the side
   columns always get the space they need.

Verified via Playwright probe at 780/900/1023/1024/1100/1280 —
"+ New" button now lands inside the header at every width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 14:22:29 +02:00
parent 9ae7940a04
commit 355f242b8f

View File

@@ -60,12 +60,16 @@ export function Topbar({ ports, user, leadingSlot }: TopbarProps) {
// The brand logo lives in the sidebar header (per design feedback) so the // The brand logo lives in the sidebar header (per design feedback) so the
// topbar center is dedicated to the global search bar. // topbar center is dedicated to the global search bar.
// //
// Center column min-width: 280 at tablet, 420 at lg+. The earlier // Grid is `auto auto 1fr` instead of three fr-tracks: the left + right
// single 420 min starved the left column to ~100px at 768 viewport // columns size to their actual content (logo trigger + breadcrumbs on
// width (when the sidebar is hidden under a Sheet on tablet) — that // the left; New / Inbox / Avatar on the right), and the search column
// was hiding the new logo-trigger leadingSlot AppShell mounts. Two // soaks up the rest. The earlier `minmax(280px,800px)` center column
// breakpoints split the difference cleanly. // auto-grew to the search bar's intrinsic `max-w-2xl` (672px), which
<header className="grid h-14 grid-cols-[minmax(0,1fr)_minmax(280px,800px)_minmax(0,1fr)] items-center border-b border-border bg-background gap-3 px-4 shrink-0 lg:grid-cols-[minmax(0,1fr)_minmax(420px,800px)_minmax(0,1fr)]"> // squeezed the right column below the width of "+ New + Inbox +
// Avatar" and pushed the New button off-screen at every tablet +
// narrow-desktop width. With the center as a single fr-track, the
// right column always gets the space it needs.
<header className="grid h-14 grid-cols-[auto_1fr_auto] items-center border-b border-border bg-background gap-3 px-4 shrink-0">
{/* LEFT: optional sidebar trigger (tablet) + optional back button + breadcrumbs */} {/* LEFT: optional sidebar trigger (tablet) + optional back button + breadcrumbs */}
<div className="min-w-0 flex items-center gap-1.5"> <div className="min-w-0 flex items-center gap-1.5">
{leadingSlot} {leadingSlot}
@@ -86,18 +90,18 @@ export function Topbar({ ports, user, leadingSlot }: TopbarProps) {
<Breadcrumbs /> <Breadcrumbs />
</div> </div>
{/* CENTER: global search - capped width and visually centered {/* CENTER: global search. Caps scale by viewport tier so the search
against the FULL viewport (not the post-sidebar area) via a bar doesn't crowd the side columns at narrow widths:
translate-X that shifts left by half the sidebar width. base: max-w-md (28rem / 448px) — fits tablet 768-1023 with
Without the translate the topbar's grid centers inside the ~150-200px left for the right column.
area-after-the-sidebar, so the search visually drifts right lg: max-w-xl (36rem / 576px) — narrow desktop with sidebar.
by half the sidebar width. xl: max-w-2xl (42rem / 672px) — full desktop, plenty of room.
The translate is gated to `lg:` because at tablet (768-1023) The lg: translate-X visually centers the search against the FULL
the sidebar is HIDDEN behind a Sheet — translating left there viewport (compensating for the sidebar's 256px on the left). It
shifts the search into the leading-slot column, hiding the stays gated to lg+ so tablet (sidebar hidden behind Sheet)
AppShell-mounted logo trigger. */} doesn't get an unnecessary shift. */}
<div className="flex items-center justify-center min-w-0"> <div className="flex items-center justify-center min-w-0">
<div className="w-full max-w-2xl mx-auto min-w-0 lg:-translate-x-[calc(var(--width-sidebar)/2)]"> <div className="w-full max-w-md mx-auto min-w-0 lg:max-w-xl xl:max-w-2xl lg:-translate-x-[calc(var(--width-sidebar)/2)]">
<CommandSearch /> <CommandSearch />
</div> </div>
</div> </div>