cmux NEXT composer: chips press like Codex's (arrow cursor, instant press tone, no stray focus ring, 28 px folder target)
Problem
Clicking the composer's chips (the + menu, model, reasoning and permission chips, and the folder chip on the location strip) felt off next to Codex desktop:
- The pointer hand showed over the chat composer's chips and Plan. Everything else in the pane, and the New Tab composer's chips, use the arrow.
- A press eased into its tone over the 80 ms hover transition, so the button went down a beat late.
- After typing, a mouse click left a focus ring: on the + button, and as a thick system-blue outline around the whole permission and reasoning menus. WebKit draws focus moved by script as keyboard focus once a key has been pressed.
- The strip's folder chip took a press over only 22 px.
Change
- The arrow cursor over the chat composer's chips and Plan.
:activedrops the transition, so the press tone shows the moment the button goes down. Nothing moves; the open chip keeps its wash.- Hover washes apply only under
(hover: hover) and (pointer: fine). - The + menu focuses its button with
focusVisible: falsewhen a mouse press opens it, so keys still reach the menu. An open menu (.ui-popup.ui-menu) never draws an outline around itself; its rows show the highlight. - The folder chip's press target reaches 28 px through a
::beforeon the button, without moving anything.
Footer layout, label step-down and gaps are untouched (composerRowFit.ts and the row geometry belong to op-next-composer-footer-cx). The effort menu's layout lines from #20787 and #20796 are untouched.
Codex reference. In Codex desktop the composer pickers are menu buttons with the arrow cursor, a tonal press, no ring after a click, and they open on the press. references/ has no frame of Codex's composer menus mid-click, so this follows the behaviour rather than a still.
Borrowed from AI Elements. The trigger keeps its wash while the menu is open (aria-expanded:bg-muted), the ring is focus-visible only, menu rows use cursor-default, and touch is gated separately (pointer-coarse). AI Elements nudges a pressed button down 1 px (active:translate-y-px) but not a menu trigger; we keep a tonal step with no shift.
Verified
webviews/test/composer-chip-press.test.ts(new, real pointer events, Chromium and WebKit, 760 and 300 px, on a chat's composer, a new chat's and the New Tab page's): arrow cursor on every chip; the press tone is there the moment the button goes down and the box doesn't move; no ring on the chip or its menu after a mouse click, after typing too, while keyboard focus still draws one; every chip takes a press over at least 24 px; every menu row takes a press at its gutter, its trailing edge, its top and its bottom. Commit cd44a91 adds the test alone: 32 of 50 fail on the base. With the fix: 50 pass, and still 50 after mergingfeat-cmux-next(its own gallery fix, 589f575f5c16, replaces #21020's, which this branch no longer carries).- Related suites pass locally per file, browser lane included (composer-row-, composer-location-, menu-focus, model-picker-, new-tab-, reasoning-own-control, access-chip-pending-warning, popup-*, newtab-quiet-focus, text-field-focus, desktop-feel, ghost-button-balance and others).
row-focus(4) andno-system-blue(1) fail the same way on the base on this Mac. vp checkpasses (types, lint, format).- Tagged build
cg-composerat 7c220e3 (CMUX_SKIP_ZIG_BUILD=1 ./scripts/reload.sh --tag cg-composer) built and launched. In the app,debug.agent_pane clickdelivers native clicks to the New Tab chips (model: pointerdown, mousedown, mouseup, click; reasoning: pointerdown, click). This Mac's display is locked, so the page is hidden (visibilityState: hidden, no animation frames) and menus never finish opening or closing. Menu state, focus rings and the cursor couldn't be observed in the app; the browser tests above cover them.
Stills
WebKit on the gallery stage (the app's engine), after typing a draft. Latte and Mocha at 760 and 400 px.
Permission chip held down (the press tone is there with no ease; the menu opens on the press and is fading in):
| Latte 760 | Mocha 760 | Latte 400 | Mocha 400 | |
|---|---|---|---|---|
| Before | ![]() | ![]() | ![]() | ![]() |
| After | ![]() | ![]() | ![]() | ![]() |
Open permission menu after a click. Before: a system-blue ring around the whole menu. After: none.
| Latte 760 | Mocha 760 | Latte 400 | Mocha 400 | |
|---|---|---|---|---|
| Before | ![]() | ![]() | ![]() | ![]() |
| After | ![]() | ![]() | ![]() | ![]() |
Open menu with the pointer over a row (Chromium; headless WebKit's synthetic mouse moves report no movement, so Base UI ignores them and draws no row hover):
| Latte 760 | Mocha 760 | Latte 400 | Mocha 400 |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
One-press switch (model menu open, press on the permission chip held, released). One press switches; it already did on the base.
Latte 760
Latte 400
Mocha 760
Mocha 400
🤖 Generated with Claude Code



















