Workspace tabs are a real 7.css tablist

The tab strip was a hand-rolled lookalike: bg-accent for the active tab and
an absolutely-positioned bg-primary underline, competing with a tab
component 7.css already ships. Replaced with its actual contract —
menu[role=tablist] plus one button[role=tab][aria-selected] per workspace —
so 7.css draws the joined inactive tabs and the raised white active one.

Per the agreed design the per-tab X is gone: real Win7 tab controls had
none, and a button nested in a button is invalid HTML. Closing keeps
Ctrl/Cmd+W and gains middle-click (auxclick, guarded on button 1 so the
right button never closes a tab). Drag-reorder moved onto the tab button
itself and still works.

Three things 7.css does that had to be accommodated, each found by looking
rather than by reasoning:

- Its selector is the DESCENDANT `menu[role=tablist] button`, so the +,
  update pill and right-hand tools deliberately stay outside the menu.
- The strip is a .w7-toolbar, whose neutralizer strips background, border
  and both pseudo-elements to make toolbar buttons flat. That flattened the
  tabs to nothing, so the rule now exempts :not([role=tab]) — tabs want the
  full Aero slab, their neighbours want flat.
- overflow-x:auto on the scroller forces computed overflow-y from visible
  to auto, so the strip clips vertically. 7.css raises the active tab with
  margin-top:-2px, which put its top border outside the scroll box and
  silently deleted it. The strip now reserves 2px of padding-top, mirroring
  the padding-left:3px 7.css already reserves for the same tab's -3px left
  margin. Caught by pixel-scanning the tab's top edge against 7.css's own
  reference markup in the harness: reference had #8e8f8f, ours jumped
  straight from toolbar grey to tab fill.

Also: tabs.css hardcodes color:#222 on every tablist button, outside 7.css's
var system, leaving labels near-invisible in dark. Routed through a new
--w7-tab-c token declared light in :root.win7 and overridden in BOTH dark
blocks (attribute and prefers-color-scheme), matching the ListView and menu
literals. And its `display: block` outranks Tailwind's .flex, which jammed
the icon against the label — flex/gap restored in skinfix.

role="tabpanel" on the panel area was considered and skipped: 7.css gives it
padding:14px, margin-bottom:9px and its own border, which would fight the
split-pane layout for a join that cannot read anyway with the panel toolbar
row in between.

The harness grew a section 5 mirroring this markup, because the live app
starts with exactly one workspace and there is no way to click "+" from a
headless capture — multi-tab state is only checkable there. Verified in
both themes headless and on a live window capture.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-16 03:56:19 +02:00
co-authored by Claude
parent 01739e4cf4
commit aaa12c3e23
3 changed files with 140 additions and 42 deletions
+46 -1
View File
@@ -231,7 +231,7 @@
</div>
</figure>
<figure class="cell" style="margin:0">
<figcaption>tabs — menu[role=tablist]</figcaption>
<figcaption>reference — menu[role=tablist]</figcaption>
<menu role="tablist">
<button role="tab" aria-selected="true">local</button>
<button role="tab">server-a</button>
@@ -240,5 +240,50 @@
<div role="tabpanel" style="padding:10px">Tab body</div>
</figure>
</div>
<h2>5 — Workspace tab strip (ours, as tab-bar.ts renders it)</h2>
<!--
The live app only ever has ONE workspace at startup and there is no way to
click "+" from a headless capture, so multi-tab state can only be checked
here. This mirrors tab-bar.ts exactly: the tabs sit in a menu[role=tablist]
inside a .w7-toolbar row, and the "+" / right-hand tools sit OUTSIDE the
menu so 7.css's descendant selector does not style them as tabs.
-->
<div class="pair">
<figure class="cell" style="margin:0">
<figcaption>ours — 3 workspaces, 2nd active</figcaption>
<div class="w7-toolbar relative flex h-9 items-center gap-0.5 border-b border-border bg-chrome pt-1 pr-1.5 pl-1.5">
<menu role="tablist" class="flex min-w-0 items-center overflow-x-auto">
<button role="tab" class="flex max-w-52 min-w-0 shrink-0 items-center gap-1.5">
<span class="truncate">Local</span>
</button>
<button role="tab" aria-selected="true" class="flex max-w-52 min-w-0 shrink-0 items-center gap-1.5">
<span class="truncate">build-server</span>
</button>
<button role="tab" class="flex max-w-52 min-w-0 shrink-0 items-center gap-1.5">
<span class="truncate">a-very-long-workspace-name-that-truncates</span>
</button>
</menu>
<button class="ml-0.5 grid size-6 place-items-center rounded-md">+</button>
<div class="flex-1"></div>
<button class="grid h-6 px-2 py-1 place-items-center rounded-md text-[11px]">Connections</button>
</div>
<div style="height:60px;background:var(--w7-tab-bg,#fff);border:1px solid #8e8f8f;border-top:0"></div>
</figure>
<figure class="cell" style="margin:0">
<figcaption>ours — single workspace (the live startup state)</figcaption>
<div class="w7-toolbar relative flex h-9 items-center gap-0.5 border-b border-border bg-chrome pt-1 pr-1.5 pl-1.5">
<menu role="tablist" class="flex min-w-0 items-center overflow-x-auto">
<button role="tab" aria-selected="true" class="flex max-w-52 min-w-0 shrink-0 items-center gap-1.5">
<span class="truncate">Local</span>
</button>
</menu>
<button class="ml-0.5 grid size-6 place-items-center rounded-md">+</button>
<div class="flex-1"></div>
<button class="grid h-6 px-2 py-1 place-items-center rounded-md text-[11px]">Connections</button>
</div>
<div style="height:60px;background:var(--w7-tab-bg,#fff);border:1px solid #8e8f8f;border-top:0"></div>
</figure>
</div>
</body>
</html>