Composer: the location strip stands a few px apart from the card
The location strip on top of the chat composer (folder · computer · branch, the "top bump") merged into the composer card. It shared the card's fill and edge, sat flush on the card's top edge, and the card's rim was masked out under it, so the tab and the card read as one blob. Leo asked for a bit of separation, not a heavy border.
Now the strip stands 4px above the card inside its own closed hairline rim, and the card keeps its whole rim. The queued-prompts tray still sits flush on the strip, so the queue and the strip read as one tab over the card. The same change applies to the New Tab composer, which uses the same strip.
What changed
composerLocation.css: the strip gets a bottom margin (--acpmux-strip-gap, 4px), one inset hairline ring on all four sides in--acpmux-composer-edge(the existing hairline token,--cmux-hairlinewide, clear underappearance.borders = none), and small bottom corners (5px). Under a queue, its top corners stay square and its top edge gives way to the queue as before, but it keeps its bottom hairline and corners.- The seam mask is removed: the card no longer draws its rim on a
::beforelayer with a cutout under the strip. It draws its normal--acpmux-card-ring, including focus and shell-mode states. - Fill, inset, height, type, and the strip's controls are unchanged. The bottom footer row is untouched.
- Comments in
composerStates.cssandnewtab/screen.cssupdated to match.
Stills (before → after)
Catppuccin Latte / Mocha, chat composer with a project:
| Before | After | |
|---|---|---|
| light 760 px | ![]() | ![]() |
| light 400 px | ![]() | ![]() |
| light 300 px | ![]() | ![]() |
| dark 760 px | ![]() | ![]() |
| dark 400 px | ![]() | ![]() |
| dark 300 px | ![]() | ![]() |
With queued prompts (the queue and strip are one tab, now separated from the card):
| Before | After | |
|---|---|---|
| light 760 px | ![]() | ![]() |
| light 400 px | ![]() | ![]() |
| light 300 px | ![]() | ![]() |
| dark 760 px | ![]() | ![]() |
| dark 400 px | ![]() | ![]() |
| dark 300 px | ![]() | ![]() |
Apple System Colors light / dark (after):
| 760 px | 300 px | |
|---|---|---|
| apple-light | ![]() | ![]() |
| apple-light, queued | ![]() | ![]() |
| apple-dark | ![]() | ![]() |
| apple-dark, queued | ![]() | ![]() |
Borrowed from AI Elements
From the shadcn AI Elements study (cmux-cgc-notes/ai-elements.md):
- Spacing: the gap is 4px, on their 4px grid. It is the smallest step that reads as a separate surface without pushing the strip away from the card it belongs to.
- Restraint: their composer group is one 1px
border-inputrim with no shadows layered on top. Here the strip uses one hairline ring in the existing composer edge token. There is no heavier border, no new color, and no fill step, so the strip and the card are still the same material. - Nesting: AI Elements shows hierarchy with a single rule and spacing, not boxes inside boxes. The strip sits next to the card as a sibling tab instead of being folded into the card's outline.
- Type weights: unchanged. The strip's labels stay normal weight and muted, which matches their rule that only buttons and titles get
font-medium.
Verification
test/composer-location-strip.test.ts: the strip now sits 2–6px above the card (chat, new chat, New Tab at 420/720px). The old "no hairline across the seam" pixel probe is replaced with a check that the strip draws one whole inset hairline ring and the card draws its own unmasked rim.test/agent-pane-bottom-stack.test.ts: new boundary test at 760 and 480px pane heights. With the queue and the strip both shown, the queue sits flush on the strip and the tab stands 2–6px above the card.- These tests fail against the old CSS (10 failures) and pass with the change. I ran the focused browser suites locally with
CMUX_BROWSER_TESTS=1(composer-location-strip, agent-pane-bottom-stack, composer-edges, composer-rim-hairline, new-tab-composer-layout): 26 pass. bun run checkpasses (layers, type-scale, status-glyphs, system-blue, text-focus, fmt, lint, typecheck).- No localization impact: no user-facing strings changed.
🤖 Generated with Claude Code































