Skip to content

feat(header): one plane of chrome, and menus a keyboard can reach - #75

Merged
thethinkmachine merged 1 commit into
mainfrom
feat/header-one-plane-of-chrome
Sep 8, 2026
Merged

feat(header): one plane of chrome, and menus a keyboard can reach#75
thethinkmachine merged 1 commit into
mainfrom
feat/header-one-plane-of-chrome

Conversation

@thethinkmachine

Copy link
Copy Markdown
Owner

What this changes

A restructure and finishing pass over the app's persistent chrome — the header and the frame around the canvas.

The header

  • One toolbar ground. The canvas actions, history pair and file actions each carried their own soft pill with hairline separators between them — two devices doing one job, so the right third of the header read as three unrelated widgets. They are one ground now, with rules inside it where one kind of control ends and the next begins.
  • The model picker joins the wizard button in the right cluster as one control. Both are about the machine rather than the canvas; beside the wordmark the picker read as branding and sat as far as possible from the controls that act on it.
  • The Tools dropdown is gone. The three auxiliary views (Algorithms, Grammar, Reference) are rows in the More menu, which removes a second header menu whose only job was to be mutually exclusive with the first.
  • Save is a split button — the wide half performs the act, the caret half offers the variants — and its menu is anchored and measured against the split rather than hard-coded, so it no longer clamps off-window or clips its shortcut hint.

Every menu closes the same ways

dismissHeaderMenus() in js/ui.js is one shared closer for all four header menus — More, tab overflow, save, model picker — run by Escape (ahead of the rest of its ladder) and by window blur, so a menu can't sit open behind the window. The model picker's outside-click no longer races its own opening click (guardNextClose over a requestAnimationFrame instead of setTimeout(0)), and a second press of its trigger closes it.

Keyboard access

The menu rows were <div onclick>s — invisible to Tab and untriggerable without a mouse. They now carry tabindex, Enter/Space activation, ↑/↓ roving focus, and aria-selected/aria-disabled where those apply (the tab overflow list is a proper listbox, the model list proper options). Focus rings that were mixed from --focus-ring — an alpha .08–.14 token, invisible against the header's ground — are mixed from the accent instead, and bare-key canvas shortcuts stand down while a menu row has focus.

One height

The tab strip, the machine pill and the toolbar all stand at 36px, where the pill was 4px shorter than both neighbours.

Around the canvas

The well's radius and sill widen to 14px and all four corners curve. The top two used to be squared on the reasoning that the workspace tabs stand on that edge — but the uncovered quarter is the concave corner each panel appears to have, and the artifact under the tabs was the grid, which is now masked with a short ramp at its top edge.

css/chrome-polish.css is new: the finishing layer for this pass (toolbar plane, tab recessing, section-header focus cues, panel scrollbars). The panel-tab seam it briefly duplicated is consolidated back into css/panels.css as the one declaration for both sidebars, and CLAUDE.md is updated to match.

Verification

  • npm test — 1946/1946 passing
  • npm run build — clean

The right third of the header read as three unrelated widgets rather
than as a toolbar: the canvas actions, the history pair and the file
actions each carried their own soft pill, with hairline separators
between them as well -- two devices doing one job. They are one ground
now, with rules inside it where one kind of control ends and the next
begins.

The model picker moved from beside the wordmark into the right cluster,
joined with the wizard button into one control -- both are about the
machine rather than the canvas, and next to the logo the picker read as
branding, leaving the app's one piece of standing information as far as
possible from the controls that act on it. The Tools dropdown is gone:
the three auxiliary views are rows in the More menu now, which removes
a second header menu whose only job was to be mutually exclusive with
the first.

Every menu in the header closes the same ways. Escape and a window blur
run one shared dismissHeaderMenus() across the More menu, the tab
overflow list, the save menu and the model picker. The picker's outside
click no longer races its own opening click (guardNextClose over a
requestAnimationFrame, rather than setTimeout(0)), and a second press of
its trigger closes it -- a regression the first rewrite introduced,
where the second press was a no-op.

The rows are keyboard-reachable. The .ctx-i rows, the model items and
the tab overflow listbox carried no focus at all; they now take
tabindex, Enter/Space activation, arrow-key roving focus, and
aria-selected/aria-disabled where those apply. Focus rings that were
mixed from --focus-ring (alpha .08-.14, invisible against the header's
ground) are mixed from the accent instead, and bare-key canvas
shortcuts stand down while a menu row has focus.

The three clusters stand at the same height. The tab strip, the machine
pill and the toolbar are all 36px, where the pill was 4px shorter than
both its neighbours -- the toolbar had only arrived at 36 incidentally
(30px buttons, 2px padding and the new 1px border) and the pill was
never raised with it.

Around the canvas, the well's radius and sill widen to 14px and all
four corners curve. The top two used to be squared on the reasoning
that the workspace tabs stand on that edge, but the uncovered quarter
is the concave corner each panel appears to have, and the artifact
under the tabs was the grid, which is fixed where the grid is -- a
short mask ramp at the top of it.

css/chrome-polish.css is the finishing layer for this pass: one toolbar
plane, tab recessing, section-header focus cues and panel scrollbars.
The panel-tab seam it briefly duplicated is consolidated back into
css/panels.css as the one declaration for both sidebars, with CLAUDE.md
updated to match, and the dead .hdr-tools rule (its element went with
the Tools dropdown) is deleted.
Copilot AI lite review requested due to automatic review settings September 8, 2026 10:57
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@thethinkmachine
thethinkmachine merged commit 4c1f45a into main Sep 8, 2026
5 checks passed
@thethinkmachine
thethinkmachine deleted the feat/header-one-plane-of-chrome branch September 8, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants