refactor(header): collapse the panel header by container width, not viewport - #5241
Merged
tlgimenes merged 2 commits intoJul 25, 2026
Merged
Conversation
…ewport The header strip lives inside one panel, so viewport width says nothing about the room it actually has: with chat open, a 1180px screen leaves the main header at 605px. Every control in it collapsed on viewport media queries, so on a laptop the labels stayed at full width while the strip ran out of space, and the same screen behaved differently depending on the splitter. PanelHeader now declares `@container/panel-header` and every control queries that instead: - Tab / Chat labels collapse to icon + tooltip below 768px of header. Buttons with a fixed, distinctive icon (Chat, system tabs, Library, Tasks, CMS) go at 768px; dynamic tabs, whose icon can resolve to the generic fallback and would be mutually indistinguishable, hold their text to 576px. - The branch pill, branch picker, Develop/Live toggle and the publish actions move off `max-lg:`/`lg:hidden` onto the same 768px cut, so the whole strip collapses at one width instead of in two unrelated stages. - Preview's URL group (CMS / refresh / page select / open-in-new-tab) hides below 448px of header. Deliberately later than the old JS rule (~668px): between ~448 and ~530px the page selector shows with less than HEADER_W.middle and degrades toward a bare chevron, judged better than losing the controls outright. HeaderTabButton also gains the Tooltip its docstring already claimed it had — without it an icon-only tab had nothing identifying it at all. preview.tsx's external Tooltip is replaced by the new `tooltip` prop so the two don't nest. headerLayout keeps only maxTabs; showPageSelector is gone, since CSS owns that now and leaving it would be dead code. Its tests are inverted rather than appended to, and HEADER_W.middle's docstring no longer claims to gate the centre's visibility. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Edit05 is the new-chat affordance in the sidebar (task-groups-list) and the shell breadcrumb, so the header's CMS toggle rendered the identical pencil-square — two unrelated actions with one glyph, and the collision got worse once the label collapses and the icon is all that identifies the button. PuzzlePiece01 is the page-builder "blocks" metaphor, which is what the toggle opens, and it is unused elsewhere. Avoided LayoutAlt01 (already the page icon in this same toolbar's page-selector), Grid01 (means card/grid view in collections) and Rows01 (a rows-view toggle in the sidebar). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tlgimenes
added a commit
that referenced
this pull request
Jul 25, 2026
…iewport (#5241) * refactor(header): degrade the panel header by container width, not viewport The header strip lives inside one panel, so viewport width says nothing about the room it actually has: with chat open, a 1180px screen leaves the main header at 605px. Every control in it collapsed on viewport media queries, so on a laptop the labels stayed at full width while the strip ran out of space, and the same screen behaved differently depending on the splitter. PanelHeader now declares `@container/panel-header` and every control queries that instead: - Tab / Chat labels collapse to icon + tooltip below 768px of header. Buttons with a fixed, distinctive icon (Chat, system tabs, Library, Tasks, CMS) go at 768px; dynamic tabs, whose icon can resolve to the generic fallback and would be mutually indistinguishable, hold their text to 576px. - The branch pill, branch picker, Develop/Live toggle and the publish actions move off `max-lg:`/`lg:hidden` onto the same 768px cut, so the whole strip collapses at one width instead of in two unrelated stages. - Preview's URL group (CMS / refresh / page select / open-in-new-tab) hides below 448px of header. Deliberately later than the old JS rule (~668px): between ~448 and ~530px the page selector shows with less than HEADER_W.middle and degrades toward a bare chevron, judged better than losing the controls outright. HeaderTabButton also gains the Tooltip its docstring already claimed it had — without it an icon-only tab had nothing identifying it at all. preview.tsx's external Tooltip is replaced by the new `tooltip` prop so the two don't nest. headerLayout keeps only maxTabs; showPageSelector is gone, since CSS owns that now and leaving it would be dead code. Its tests are inverted rather than appended to, and HEADER_W.middle's docstring no longer claims to gate the centre's visibility. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(preview): give the CMS toggle its own icon Edit05 is the new-chat affordance in the sidebar (task-groups-list) and the shell breadcrumb, so the header's CMS toggle rendered the identical pencil-square — two unrelated actions with one glyph, and the collision got worse once the label collapses and the icon is all that identifies the button. PuzzlePiece01 is the page-builder "blocks" metaphor, which is what the toggle opens, and it is unused elsewhere. Avoided LayoutAlt01 (already the page icon in this same toolbar's page-selector), Grid01 (means card/grid view in collections) and Rows01 (a rows-view toggle in the sidebar). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
guitavano
pushed a commit
that referenced
this pull request
Jul 25, 2026
…iewport (#5241) * refactor(header): degrade the panel header by container width, not viewport The header strip lives inside one panel, so viewport width says nothing about the room it actually has: with chat open, a 1180px screen leaves the main header at 605px. Every control in it collapsed on viewport media queries, so on a laptop the labels stayed at full width while the strip ran out of space, and the same screen behaved differently depending on the splitter. PanelHeader now declares `@container/panel-header` and every control queries that instead: - Tab / Chat labels collapse to icon + tooltip below 768px of header. Buttons with a fixed, distinctive icon (Chat, system tabs, Library, Tasks, CMS) go at 768px; dynamic tabs, whose icon can resolve to the generic fallback and would be mutually indistinguishable, hold their text to 576px. - The branch pill, branch picker, Develop/Live toggle and the publish actions move off `max-lg:`/`lg:hidden` onto the same 768px cut, so the whole strip collapses at one width instead of in two unrelated stages. - Preview's URL group (CMS / refresh / page select / open-in-new-tab) hides below 448px of header. Deliberately later than the old JS rule (~668px): between ~448 and ~530px the page selector shows with less than HEADER_W.middle and degrades toward a bare chevron, judged better than losing the controls outright. HeaderTabButton also gains the Tooltip its docstring already claimed it had — without it an icon-only tab had nothing identifying it at all. preview.tsx's external Tooltip is replaced by the new `tooltip` prop so the two don't nest. headerLayout keeps only maxTabs; showPageSelector is gone, since CSS owns that now and leaving it would be dead code. Its tests are inverted rather than appended to, and HEADER_W.middle's docstring no longer claims to gate the centre's visibility. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(preview): give the CMS toggle its own icon Edit05 is the new-chat affordance in the sidebar (task-groups-list) and the shell breadcrumb, so the header's CMS toggle rendered the identical pencil-square — two unrelated actions with one glyph, and the collision got worse once the label collapses and the icon is all that identifies the button. PuzzlePiece01 is the page-builder "blocks" metaphor, which is what the toggle opens, and it is unused elsewhere. Avoided LayoutAlt01 (already the page icon in this same toolbar's page-selector), Grid01 (means card/grid view in collections) and Rows01 (a rows-view toggle in the sidebar). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
guitavano
added a commit
that referenced
this pull request
Jul 25, 2026
…s below lg (#5230) * feat(header): branch label, truncation priority, and icon-only actions below lg - Branch button always shows the current branch (truncated), and yields its width BEFORE the centered address bar (its own `min-w-0` shrink slot; the fixed publish/⋯ actions never clip and remain what `rightRef` measures). - Below `lg` (< 1024px) the header collapses to icon-only + tooltip: the branch button, the primary action button (icon keyed off `button.action`), the side Publish button (Upload01), and the Develop/Live toggle (Code01 / Globe01). Loading pills → spinner-only; plain status pills keep their text. Desktop unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(header): collapse the panel header by container width, not viewport (#5241) * refactor(header): degrade the panel header by container width, not viewport The header strip lives inside one panel, so viewport width says nothing about the room it actually has: with chat open, a 1180px screen leaves the main header at 605px. Every control in it collapsed on viewport media queries, so on a laptop the labels stayed at full width while the strip ran out of space, and the same screen behaved differently depending on the splitter. PanelHeader now declares `@container/panel-header` and every control queries that instead: - Tab / Chat labels collapse to icon + tooltip below 768px of header. Buttons with a fixed, distinctive icon (Chat, system tabs, Library, Tasks, CMS) go at 768px; dynamic tabs, whose icon can resolve to the generic fallback and would be mutually indistinguishable, hold their text to 576px. - The branch pill, branch picker, Develop/Live toggle and the publish actions move off `max-lg:`/`lg:hidden` onto the same 768px cut, so the whole strip collapses at one width instead of in two unrelated stages. - Preview's URL group (CMS / refresh / page select / open-in-new-tab) hides below 448px of header. Deliberately later than the old JS rule (~668px): between ~448 and ~530px the page selector shows with less than HEADER_W.middle and degrades toward a bare chevron, judged better than losing the controls outright. HeaderTabButton also gains the Tooltip its docstring already claimed it had — without it an icon-only tab had nothing identifying it at all. preview.tsx's external Tooltip is replaced by the new `tooltip` prop so the two don't nest. headerLayout keeps only maxTabs; showPageSelector is gone, since CSS owns that now and leaving it would be dead code. Its tests are inverted rather than appended to, and HEADER_W.middle's docstring no longer claims to gate the centre's visibility. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(preview): give the CMS toggle its own icon Edit05 is the new-chat affordance in the sidebar (task-groups-list) and the shell breadcrumb, so the header's CMS toggle rendered the identical pencil-square — two unrelated actions with one glyph, and the collision got worse once the label collapses and the icon is all that identifies the button. PuzzlePiece01 is the page-builder "blocks" metaphor, which is what the toggle opens, and it is unused elsewhere. Avoided LayoutAlt01 (already the page icon in this same toolbar's page-selector), Grid01 (means card/grid view in collections) and Rows01 (a rows-view toggle in the sidebar). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(header): drop the Preview URL group's cutoff to 384px The group's min-content is 112px — 36 (CMS) + 28 (refresh) + 24 (page chevron) + 28 (open-in-new-tab). Measured against the live header, a 384px container still leaves it 112-128px once the left group (66px; the tab budget is one tab at that width) and the actions (178px) take their share, so the icons render without clipping. One breakpoint lower and they clip. The page name is already gone by ~448px — the selector collapses to a bare chevron there — so this buys the 384-448px band four usable icons rather than nothing. Losing the controls outright is worse than losing the label. Because container queries measure the content box, `px-1.5` makes this fire around 396px of rendered width, leaving a ~16px margin above the floor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(preview): centre the page selector in the mobile toolbar On the inline (mobile) path every control lived in one left-packed group, so the page selector sat wherever Edit and refresh happened to push it — off centre on a phone, where that bar is the full width of the screen. Split the group into `cmsToggle` and `urlGroup` so each layout can place them independently. Desktop composition is unchanged: Edit leads, divider, then the view controls, portaled into the header's centre slot. Mobile now runs three zones — Edit left, the view controls centred, ⋯ right. Both side zones are `flex-1` so the selector centres on the bar rather than between two unequal neighbours (Edit is ~9px wider than ⋯, which was the original offset). On the narrowest phones the left zone cannot shrink below the Edit button, so ~5px returns; closing that fully would mean pinning the sides to a fixed width and taking the space from the selector instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(branch-picker): mark the current branch and open focused on it The picker relied on cmdk's default first-item highlight, which made an unrelated branch look "selected" while the branch actually in use had no indication. Seed the active item with the current branch so it opens focused on it, and render a check on the current branch in each group. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Deco Studio <studio@deco.cx> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: guitavano <tavano62@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #5230 — review that one first; this PR is the two commits on top.
The problem, for anyone not on a big monitor
The panel header collapsed its controls on viewport media queries (
lg, 1024px). But the header doesn't span the viewport — it lives inside one panel. So screen width tells you almost nothing about the room it actually has:That second row is the bug. At a 1180px screen the viewport rule says "plenty of room, show every label" — while the strip is actually 605px and out of space. The labels stayed full-width, the tab budget got squeezed, and view tabs fell into the ⋯ overflow menu earlier than they needed to.
It also meant the same screen behaved differently depending on where you dragged the splitter, which is impossible to reason about — and it's why this looked fine on a wide monitor and bad on a laptop.
Reported as: "the screen is 882px wide and I'm still seeing the chat and preview labels."
What changed
PanelHeadernow declares@container/panel-headerand every control inside queries its own panel's width:Two tiers rather than one, because the icon has to carry the button once the label goes. Chat/Preview/Code/Library/Tasks have fixed, distinctive icons and collapse first. Dynamic tabs can resolve to a generic fallback glyph (
resolve-tab-icon.ts), so several open files would become mutually indistinguishable — they hold their text longest.Small screens keep the Preview controls much longer: the old JS rule hid the whole URL group below ~668px of header; it now survives to 448px.
Tabs finally have tooltips
HeaderTabButtonhad no Tooltip at all, while its own docstring claimed "every button is wrapped in a Tooltip." Belowmdthe tabs were already collapsing to bare icons — so on a small screen you got an unlabelled icon with nothing identifying it butaria-label. That's the part that actually hurt. Tooltips are now real, which is what makes collapsing labels safe in the first place.preview.tsxwrapped one of these in its own Tooltip; that's replaced by atooltipprop so the two don't nest.Screenshots
Trade-off worth a reviewer's opinion
The URL group's 448px cutoff is deliberately later than the old ~668px. Between roughly 448 and 530px the page selector has less than
HEADER_W.middle(232px) and degrades toward a bare chevron instead of showing the page name — measured 220px at a 530px header, 150px at 460px.That squish is the accepted cost of keeping the controls reachable on narrow panels; the old code hid them instead. Documented at the call site. Easy to move to 576px (
@xl) if it reads badly in practice.Dead code removed
headerLayoutno longer returnsshowPageSelector— CSS owns that now, so keeping it would be dead.maxTabsstays in JS, because it changes which items render in the bar vs the overflow popover and CSS can't express that. Its tests were inverted rather than appended to, plus one asserting the field doesn't come back.HEADER_W.middle's docstring no longer claims to gate the centre's visibility.Testing
bun run checkclean across all workspaces;bun run lint0 errors;bun run fmtapplied.apps/weblayouts, sandbox, chat).BranchPill,BranchPicker,DevAgentControl,HeaderActions) render only inside aPanelHeader, so the container always resolves. Outside one the query finds no container and the label simply stays — the safe direction.MobileTaskWorkspace) doesn't render the branch selector or publish actions at all; Preview's inline fallback bar declares the same container so its queries still work on that path.Also in here: the CMS toggle gets its own icon
Edit05was doing double duty as the new-chat affordance (task-groups-list.tsx,shell-breadcrumb.tsx) and the header's CMS toggle — two unrelated actions, one pencil-square. That collision matters more once the label collapses and the icon is the only thing identifying the button. NowPuzzlePiece01(the page-builder "blocks" metaphor, unused elsewhere), applied to both entry points: the toolbar button and the ⋯ menu fallback.🤖 Generated with Claude Code
Summary by cubic
Collapse the panel header by its own container width instead of the viewport to fix cramped layouts and inconsistent behavior when chat narrows the header. Adds tooltip-backed icon-only states and simplifies
headerLayoutto only manage tab count.Refactors
@container/panel-header; labels collapse by panel width with two tiers (Chat/system tabs/CMS and right actions at 768px; dynamic tabs at 576px). Preview’s URL group hides at 448px; the mobile inline toolbar declares the same container.headerLayoutto return{ maxTabs }only; center visibility now uses a container query. Updated tests and docs.PuzzlePiece01) to avoid the pencil icon collision.Bug Fixes
previewuses the newtooltipprop to avoid nested tooltips.Written for commit 41158c5. Summary will update on new commits.