Skip to content

Keep the keyboard where it was through sends, removals, answers and a forming step group - #61

Merged
WaylandYang merged 1 commit into
devfrom
fix/focus-and-transitions
Oct 29, 2026
Merged

WaylandYang merged 1 commit into
devfrom
fix/focus-and-transitions

Conversation

@WaylandYang

Copy link
Copy Markdown
Collaborator

Why

A keyboard pass over Solid Glass's own components found four places where a state change dropped the focus to the page, and three where the keyboard could not be seen:

  • Composer: the send mark was disabled whenever there was nothing to send, so sending from the keyboard, or a turn ending while the stop mark had the focus, left the focus on body. Its focus-within:border-ring/60 pointed at a border the pane sets to none, so typing showed no focus at all.
  • Tag: removing a tag on its own x unmounted the focused button; the next Tab started from the top of the page.
  • AskCard: once answered, both choices became disabled on the very press that answered, dropping the focus; and the card did not show which choice was taken.
  • StepGroup: a second call joining the first remade the row (direct StepRow → group with StepList), so an open row closed and lost its focus. Keep single-step details and focus through status updates #56 covered the same call finishing, not a group forming.
  • TurnStatus: when children went away between steps the <button> became a <div>, remounting under the keyboard.
  • ContextRing's detail button had outline-none and no focus style; the status line, reasoning summary, tag x and composer picks showed the keyboard as a change of grey alone, which is not visible in either theme; step rows showed the browser's default ring.

What changes

  • One rule in solid-glass.css, outside the layers: the lines of text that are buttons (turn status, step row and group, ask card, tag, reasoning summary, context-ring-detail, composer-pick) get a 1px --focus-line outline 2px off the words on :focus-visible.
  • Composer: the mark is aria-disabled and inert instead of disabled; mousedown on it is prevented so the pointer never takes the focus from the text; sent or stopped from the keyboard, the focus returns to the textarea. The pane draws its hairline in the focus line while the text is focused (focus-within:outline-1 -outline-offset-1).
  • Tag: a layout-effect cleanup, which runs while the tag is still in the page, hands the focus to the next tag's x, else the previous, else the composer's textarea. Merges a caller's ref.
  • AskCard: new chosen prop; answered choices are aria-disabled and stay reachable; the chosen one keeps its pane with aria-current, the others fade to words.
  • StepGroup holds its rows' open state by id and passes it through StepList/StepRow (openRows, onRowOpenChange; open, onOpenChange). When a second call joins an open single call the group opens with it. A row remade in the same commit for the same call takes the focus back (module-level id, cleared on the next microtask).
  • TurnStatus: once it has had children it stays a button; with none it is aria-disabled with no chevron.
  • Docs for ask-card, composer, tag, tool-steps and turn-status; the ask-card demo passes chosen; the tool-steps demo gains "Second call after 2s".

Left alone, deliberately: the tag narrowing when an upload ends (the count is gone; nothing to reserve for).

Validation

  • npm run docs (registry.json unchanged), npm run typecheck, npm run build pass.
  • /dev/examples: 389/389 mounted, 0 failed, dark and light.
  • Chrome, keyboard: Tab to Send + Enter → focus on the textarea, text cleared, mark aria-disabled. Shift+Tab to a tag's x + Enter → focus on the previous tag's x; again → focus on the textarea. Ask card option + Enter → focus stays on it, aria-current="true", the other option aria-disabled and faded. Context ring detail button: outline: solid 1px. Tool steps: open the single running call, "Second call after 2s" → "Running 2 steps" opens, the row stays open, focus is still on its button.
  • Focus line looked at on the step row, status line, reasoning summary and tag x in both themes; the composer's focus-within outline confirmed in place.

🤖 Generated with Claude Code

… question answered or a group of calls forms

Four of Solid Glass's own components dropped the keyboard's focus to the
page at the moment of a state change: the composer's send mark became
disabled as the text went or the turn ended, a tag's x went with the tag, an
ask card's choices became disabled on the press that answered, and one call
becoming the first of a group remade its row. The context ring's detail
button had no focus style at all, the composer's focus-within class pointed
at a border the pane does not have, and the lines of text that are buttons
showed the keyboard three different ways, one of them invisible. They now
share one hairline in the focus line, drawn by the stylesheet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Wayland Yang <wayland@WaylanddeMacBook-Pro.local>
@WaylandYang
WaylandYang merged commit 286511b into dev Oct 29, 2026
4 checks passed
@WaylandYang
WaylandYang deleted the fix/focus-and-transitions branch October 29, 2026 04:58
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.

1 participant