fix: the message box's send button shows its arrow

Merged
DA
9d ago
+400-159

Dean saw the message box's round send button as an empty circle: no arrow. Base is coding-agents.

Root cause

The utilities the AI Elements components are written in sit in Tailwind's utilities cascade layer, and base.css's element resets sat outside every layer. An unlayered rule outranks a layered one whatever its specificity, so button { color: inherit } beat the submit's text-primary-foreground: the arrow took the panel's primary white, the same colour as the bg-primary disc, and vanished into it. Every utility was compiled and present; none of them could win. The Stop square only showed because #1919 had added an unlayered .agent-composer-stop { color } beside it, which was this bug worked around for one of the two glyphs. The same cascade also silently beat cursor-default and the muted text-* colours on the transcript's other buttons (the copy button, the tool output's "show more", the scroll-to-bottom button).

Fix

  • base.css puts its element resets (*, button, button:disabled, textarea) in @layer base, which index.css imports before the Tailwind entry so it ranks under utilities, as Tailwind's own preflight would. Every other hand-written rule stays outside the layers and still wins over a utility.
  • PromptInputSubmit is drawn like Claude's and ChatGPT's: active is the primary disc with the glyph in primary-foreground (the surface's own colour, so a dark arrow on the light disc in Luke's dark palette); dim is disabled:bg-secondary disabled:text-muted-foreground (the raised ground with a muted arrow) instead of a 45% opacity over the filled one; the button is disabled and says aria-disabled too; it has no hover. Stop is unchanged in behaviour and now reads by the same rule, so the .agent-composer-stop workaround and its class are deleted. Size (28px) and place never change between states.
  • A knock-on of the layer: the textarea's own text-[13px] leading-[1.45] now applies rather than the inherited font. The app stage is 13px already, so the size is the same; the line height is now the 1.45 the box's min/max height were already computed from.

Tests

  • agent-composer.test.ts holds a stylesheet contract on the sheet assembled as the build assembles it (every sheet index.css names, inlined in order, the Tailwind entry compiled over the same sources): every reset-shaped rule (button, *, textarea) sits in a layer ranked under utilities; in the dim, active, and Stop states the button's resolved color differs from its resolved background-color, the dim disc is not the active one, the svg carries no colour of its own and resolves to a non-zero pixel size, the button is 28px round, its class list is the same in every state, and nothing on it answers :hover. Fails on coding-agents with "* { box-sizing } ranks under the utilities (no layer)".
  • The one-button test now also holds aria-disabled in step with disabled in every state (dim with an empty box while idle, active with text).
  • The parser the transcript layout test wrote for the Tailwind sheet moves to src/testing/css-rules.ts now that two tests read it, and learns the layer a rule sits in.

Screenshots

Headless Chrome over the real AgentComposer and the renderer stylesheet (Linux fonts), on pr-assets.

Before (coding-agents)After
beforeafter

Top to bottom: idle and empty (dim), idle with text (active), running and empty (Stop).

Verification

./scripts/check.sh exits 0 locally. CI could not verify the Mac: no macOS job runs on a pull request, and Dean waived ./scripts/verify.sh for this work, so the Mac rendering is unverified beyond the headless captures above.

šŸ¤– Generated with Claude Code


Open workspace in Conductor


Devin Review