cmux NEXT menus and pickers: checkmarks, icons, text and shortcuts on one menu grid (draft: red measurement first)
HOLD: landing waits until base is restored (webview reds from #27559, #27570 and #27580); the grid coordinator lifts this line.
Leo, 2026-10-29: "we need to have better fing grids. We need to have the fing stuff aligned on f***ing grids and lines" (and, the night before, misaligned checkmarks).
Puts every webview menu outside the Claude Code pickers on AppKit's menu metrics (schemas/menu-metrics, --cmux-menu-*; spec: leo-workspace design/grid.md 5.3): the highlight 5 from the popup edge, radius 5, 28 rows, the checkmark centred on the state column at 10 in every row of a menu that has one, icons on one column, and the text on one column. One shared rule in popupSurface.css replaces each menu's own row padding.
An icon column is reserved only when a row in the menu carries an icon: the text starts at 15 with no state column, at 22 right after the check (the select keeps #28530's 22, design/settings.md), and at 35 or 42 when there are icons.
Kept as they are, because a newer settled design puts the chosen row's check in its number's place at the trailing edge: the model picker and its side menus (#29791, design/model-picker.md), its effort menu, and the mode menu (Claude Code's look, Leo 2026-10-19; composer-claude-split.test.ts, access-grants-columns.test.ts). Whether grid.md 5.3's leading state column should also cover them is a follow-up for the picker owner.
Measured (Chromium, gallery, light; px from the popup's leading edge)
| Menu | highlight inset before → after | check x before → after | text x before → after | row h before → after |
|---|---|---|---|---|
| composer + (palette) | 0 → 5 | none | 32 → 35 | 46 → 46 (two-line rows) |
| chat ⋯ menu | 4 → 5 | none | 35 → 35 | 28 → 28 |
| ui.menu (shared) | 4 → 5 | 18 → 10 | 12 / 30 → 22 | 28 → 28 |
| ui.select | 5 → 5 | 10 → 10 | 22 → 22 | 28 → 28 |
| context menu | 4 → 5 | none | 12 → 15 | 28 → 28 |
| combobox | 4 → 5 | none | 12 → 15 | 24 → 28 |
| project chooser | 4 → 5 | 284 (trailing) → 10 | 32 → 42 | 28 / 46 → 28 / 44 |
Right-to-left, every column above measures the same from the menu's leading edge, headings included.
Before / after (gallery, Linux Chromium, 2x, light and dark)
| Menu | Before | After |
|---|---|---|
| ui.menu (light) | ![]() | ![]() |
| ui.menu (dark) | ![]() | ![]() |
| ui.select (light) | ![]() | ![]() |
| ui.select (dark) | ![]() | ![]() |
| context menu (light) | ![]() | ![]() |
| context menu (dark) | ![]() | ![]() |
| combobox (light) | ![]() | ![]() |
| combobox (dark) | ![]() | ![]() |
| project chooser (light) | ![]() | ![]() |
| project chooser (dark) | ![]() | ![]() |
| chat ⋯ menu (light) | ![]() | ![]() |
| chat ⋯ menu (dark) | ![]() | ![]() |
| composer + (light) | ![]() | ![]() |
| composer + (dark) | ![]() | ![]() |
I opened every after image: the checks sit on one state column, icons and titles on one column each, headings on the titles' column, and the project chooser's hairline back on the separator inset. The composer + menu's filled search field keeps its own inset (a separate field style, follow-up #30142).
Viewed after image: yes (2x, light and dark, above)
Tests (Linux, real Chromium/WebKit via cmux-ci test):
- Head:
checkmark-alignment,project-menu-geometry,picker-row-geometry,quick-model-menu-layout,model-menu-density,access-grants-columns,composer-claude-splitand 16 other menu suites pass (336 pass);bun scripts/check-design-tokens.tsandcheck-menu-metrics.py's web check pass. The only reds aregrid-linestranscript.actions/after-steer(warn-level), which fail the same way on base. - Base with this PR's tests:
checkmark-alignmentandproject-menu-geometryfail on every menu (17 tests).
Follow-ups from review: #30142.
Gallery: ui.menu/open, ui.select/open, ui.context-menu/open, ui.combobox/open, agent-pane.composer-plus-menu/balanced, agent-pane.header-tools/menu-open, agent-pane.project-chooser/open-current
Agent session: agent session
🤖 Generated with Claude Code



























