Skip to content

editor: align connected tab geometry across wrapped states - #339028

Merged
Cherry Wang (chryw) merged 4 commits into
mainfrom
dev/cherwan/agents/consistent-tab-height-alignment
Oct 1, 2026
Merged

Cherry Wang (chryw) merged 4 commits into
mainfrom
dev/cherwan/agents/consistent-tab-height-alignment

Conversation

@chryw

@chryw Cherry Wang (chryw) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Connected tabs drift between single-row, wrapped, pinned, and scrolled states: wrapped rows use different visible heights, shoulder/body joins can cross the separator, and the separate-row Unpin action sits closer to its pill edge than an ordinary Close action. This aligns those states around shared geometry while preserving each tab style's intended shape.

Changes

  • Keep wrapped tab hitboxes at the same height on every row, with upper rows remaining separate pills and only the bottom row joining the editor.
  • Use the Inner radius tier for connected caps and upper pills, preserve the editor frame's Outer radius, and keep action controls on the Control tier.
  • Share the separator slot and bottom-row connected geometry between wrapped and single-row tabs.
  • Keep sticky/clipped viewport masks opaque and rounded without seams or content leaking behind compact pinned tabs.
  • Share upper-pill action reservation and positioning across wrapped and separate pinned rows so Unpin, Close, and modified targets have matching clearance.
  • Curate the production tab fixtures around layout/configuration contracts rather than transient hover/focus states.

Testing

  1. Enable Modern UI and select the Connected editor tab style.
  2. Open enough editors to wrap. Verify every tab row has the same hitbox height, upper rows remain separate pills, and the bottom selected tab joins the editor with a continuous separator/shoulder tangent.
  3. Switch between default and compact tab height. Verify close and modified targets remain centered without changing row height.
  4. Enable normal pinned tabs and show Unpin actions. Compare inline, wrapped, and separate pinned rows; Unpin and Close use equal 20px targets with matching edge and vertical clearance.
  5. Move tab actions to the left. Verify the same clearance contract on the leading edge.
  6. Switch to Pill and Legacy tab styles. Verify their existing disconnected/legacy geometry is unchanged.
  7. Exercise sticky compact tabs and horizontal scrolling. Verify the clipped selected cap is rounded and scrolled content does not leak through the sticky viewport.
  8. Repeat in Dark, Light, Dark High Contrast, and Light High Contrast themes.

Validation

  • 119 targeted Modern UI and multi-editor tab tests
  • ESLint, stylelint, and git diff --check
  • 98 curated tab fixtures stable and error-free
  • The targeted editor-tabs Playwright fixture spec passes all 3 interaction tests
  • Expanded theme coverage is applied consistently to all three Tab Styles references and the Wrapped/Default density reference; other layouts use default Dark and Light
  • Native Code OSS scenario covering wrapped upper/bottom rows, dirty state, and pinned action geometry

Related work

Before: wrapped upper-row tab geometry

After: equal-height upper pill with 6px Inner-tier corners

Before: wrapped bottom connected tab

After: wrapped bottom tab sharing the single-row separator and shoulder structure

Connected separate pinned row: Unpin and Close targets with matching clearance

Native Code OSS: pinned action validation

Share separator, shoulder, and action geometry across single-row, wrapped, and pinned tabs while preserving pill and legacy behavior. Add regression coverage and fixture matrices for themes, tab styles, action locations, and pinned layouts.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 00:55
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Screenshot Changes

Base: 5262c381 Current: f0949b4c

Changed (22)

sessions/editorHeader/editorHeader/ConnectedChangesCard/Light
Before After
before after
sessions/editorHeader/editorHeader/ConnectedSecondTabCard/Light
Before After
before after
sessions/editorHeader/editorHeader/FullHeader/Light
Before After
before after
sessions/editorHeader/editorHeader/CompactFullHeader/Light
Before After
before after
sessions/editorHeader/editorHeader/BreadcrumbsAndAction/Light
Before After
before after
sessions/editorHeader/editorHeader/BreadcrumbsAndSecondaryAction/Light
Before After
before after
sessions/editorHeader/editorHeader/BreadcrumbsOnly/Light
Before After
before after
sessions/editorHeader/editorHeader/PrimaryActionOnly/Light
Before After
before after
sessions/editorHeader/editorHeader/SecondaryActionOnly/Light
Before After
before after
sessions/editorHeader/editorHeader/LayoutActionsOnly/Light
Before After
before after
sessions/accountMenu/WeeklyAndFiveHourLimits/Light
Before After
before after
sessions/chatCompositeBar/StickyMessageFrame/Light
Before After
before after
sessions/chatCompositeBar/StickyMessageFrameNarrow/Light
Before After
before after
sessions/chatCompositeBar/FirstChatActive/Light
Before After
before after
sessions/chatCompositeBar/TwoChats/Light
Before After
before after
sessions/chatCompositeBar/MixedStatuses/Light
Before After
before after
sessions/chatCompositeBar/LongTitles/Light
Before After
before after
sessions/chatCompositeBar/Renaming/Light
Before After
before after
sessions/chatCompositeBar/WithDraftChat/Light
Before After
before after
sessions/chatCompositeBar/CompactTabs/Light
Before After
before after
sessions/chatCompositeBar/InactiveSession/Light
Before After
before after
sessions/chatCompositeBar/OverflowingTabs/Light
Before After
before after

2 insignificant change(s) omitted (≤20 px, Δ≤2). See CI logs for details.

Added (22)

editor/tabs/Density/PinnedSeparateRow/Default/Light

current

editor/tabs/Density/PinnedSeparateRow/Compact/Light

current

editor/tabs/Density/SingleRow/Default/Light

current

editor/tabs/Density/SingleRow/Compact/Light

current

editor/tabs/Density/Wrapped/Default/Light

current

editor/tabs/Density/Wrapped/Compact/Light

current

editor/tabs/FileIcons/Disabled/Light

current

editor/tabs/FileIcons/Minimal/Light

current

editor/tabs/Layout/ActionsLeading/Light

current

editor/tabs/Layout/CloseActionsHidden/Light

current

editor/tabs/Layout/LongNamesFit/Light

current

editor/tabs/Layout/LongNamesFixed/Light

current

editor/tabs/Layout/LongNamesShrink/Light

current

editor/tabs/Layout/ModifiedAndMultiSelected/Light

current

editor/tabs/Layout/PinnedIconOnly/Light

current

editor/tabs/Layout/SingleEditor/Light

current

editor/tabs/Scrolling/Breadcrumbs/Light

current

editor/tabs/Scrolling/ClippedActiveTab/Light

current

editor/tabs/Scrolling/StickyPinnedTabs/Light

current

editor/tabs/TabStyles/Connected/Light

current

editor/tabs/TabStyles/Legacy/Light

current

editor/tabs/TabStyles/Pill/Light

current

Removed (129)

editor/editorTabBar/editorTabBar/ModernUIOn/ActiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/BottomWrappedCloseActionHovered/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/EditorActionsDefault/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/BottomWrappedConnected/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/Breadcrumbs/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/ClippedLeft/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/ClippedRight/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/FirstTabActive/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/FirstTabActiveAdjacentHover/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/FocusedCloseAction/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/HoveredTab/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/InactiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/LastTabActive/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/LastTabCloseActionHovered/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/LeftViewportEdge/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/MinimumIdentity/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/MinimumIdentityHovered/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/MultiSelect/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/NarrowWindow/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/RightShoulderAtViewport/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/RightViewportEdge/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/SingleTab/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/SingleTabCloseActionHovered/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/StickyViewport/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/Stroke/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/UpperPinnedPills/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/UpperWrappedCloseActionHovered/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/UpperWrappedHover/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/UpperWrappedHoverCompact/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/UpperWrappedPills/Light

baseline

editor/editorTabBar/editorTabBar/ConnectedSurface/WrappedRowStart/Light

baseline

editor/editorTabBar/editorTabBar/FileIconThemes/Minimal/Light

baseline

editor/editorTabBar/editorTabBar/FileIconThemes/None/Light

baseline

editor/editorTabBar/editorTabBar/FileIconThemes/Seti/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/ActiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/AlwaysShowEditorActionsActiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/AlwaysShowEditorActionsInactiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/BreadcrumbsFilePathLast/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/BreadcrumbsIconsOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/CannotCloseActive/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/CannotCloseDirty/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/CannotCloseSticky/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/DecorationsOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/Default/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/DropTargetBetweenTabs/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/EditorActionsHidden/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/EditorActionsTitleBar/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/HighlightModifiedTabs/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/InactiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/InactiveGroupDirty/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/LabelFormatLong/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/LabelFormatMedium/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/LabelFormatShort/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/LongLabelsShrink/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/MultiSelect/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/PinnedSeparateRowCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/PinnedTabSizingCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/PinnedTabSizingShrink/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/PinnedTabsOnSeparateRowAllPinned/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/PinnedTabsOnSeparateRowAllUnpinned/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/PinnedTabsOnSeparateRowMixed/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/ShowIconsOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/ShowTabIndex/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/ShowTabsNone/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/ShowTabsSingle/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/ShowTabsSingleCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/SingleTabDirty/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/StickyCompactNoIcons/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabActionCloseHidden/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabActionLocationLeft/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabActionReserveSpaceOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabActionReserveSpaceOn/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabActionUnpinHidden/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabHeightCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabSizingFixed/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TabSizingShrink/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TitleScrollbarLarge/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/TitleScrollbarVisible/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOff/WrapTabs/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/AlwaysShowEditorActionsActiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/AlwaysShowEditorActionsInactiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/BreadcrumbsFilePathLast/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/BreadcrumbsIconsOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/CannotCloseActive/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/CannotCloseDirty/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/CannotCloseSticky/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/DecorationsOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/Default/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/DropTargetBetweenTabs/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/EditorActionsDefault/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/EditorActionsHidden/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/EditorActionsTitleBar/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/HighlightModifiedTabs/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/InactiveGroup/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/InactiveGroupDirty/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/LabelFormatLong/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/LabelFormatMedium/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/LabelFormatShort/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/LongLabelsShrink/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/MultiSelect/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/Pill/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/PinnedSeparateRowCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/PinnedTabSizingCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/PinnedTabSizingShrink/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/PinnedTabsOnSeparateRowAllPinned/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/PinnedTabsOnSeparateRowAllUnpinned/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/PinnedTabsOnSeparateRowMixed/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ShowIconsOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ShowTabIndex/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ShowTabsNone/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ShowTabsSingle/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ShowTabsSingleCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/SingleTabDirty/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/StickyCompactNoIcons/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabActionCloseHidden/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabActionLocationLeft/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabActionReserveSpaceOff/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabActionReserveSpaceOn/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabActionUnpinHidden/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabHeightCompact/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabSizingFixed/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TabSizingShrink/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ThemeColors/ActiveAction/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ThemeColors/ActiveHover/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ThemeColors/SelectedAction/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/ThemeColors/TabStates/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TitleScrollbarLarge/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/TitleScrollbarVisible/Light

baseline

editor/editorTabBar/editorTabBar/ModernUIOn/WrapTabs/Light

baseline

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Parent wrapping state can be incorrectly cleared in separate pinned-row layouts, and new CSS uses prohibited !important overrides.

Review effort: Balanced
Findings: 1 Medium severity · 2 Low severity

Open (3)
What changed in this PR

Aligns connected editor-tab geometry across wrapped, pinned, and scrolled states.

Changes:

  • Unifies connected-tab radii, spacing, separators, clipping, and action clearance.
  • Adds wrapping and sticky-edge state markers.
  • Expands unit and visual fixture coverage.
File Description
multiEditorTabsControl.test.ts Tests wrapping, clipping, and action geometry.
editorTabBar.fixture.ts Adds pinned-action and connected-surface fixtures.
editorTabBar.fixture.css Adds fixture hover and HC styling.
modernUI.contribution.test.ts Updates CSS geometry expectations.
connectedEditorTabs.css Implements the geometry and clipping changes.
multiEditorTabsControl.ts Tracks wrapping and sticky adjacency.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts Outdated
Comment thread src/vs/workbench/contrib/modernUI/browser/media/connectedEditorTabs.css Outdated
Comment thread src/vs/workbench/contrib/modernUI/browser/media/connectedEditorTabs.css Outdated
Reduce editor-tab fixtures to distinct layout and configuration contracts, keep expanded theme coverage on two reference scenarios, and wait for wrapped layouts to settle deterministically. Also align iconless tab spacing when the file icon theme is disabled.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Aggregate wrapped state across multi-row tab controls and let the controller own overflow instead of CSS overrides. Reorganize editor tab fixtures, update Playwright fixture paths, and add regressions for pinned-row relayouts.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@chryw
Cherry Wang (chryw) marked this pull request as ready for review October 1, 2026 18:12
@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

Benjamin Christopher Simmonds (@benibenj)

Matched files:

  • src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The added wrapper padding is omitted from the available-height wrapping calculation.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (3)

@mrleemurray

Copy link
Copy Markdown
Contributor

Cherry Wang (@chryw) can you resolve the CCR comment please!

Measure the complete wrapped title against available height so Connected tab padding cannot overflow constrained editor groups. Cover both sides of the boundary with a regression test.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants