Skip to content

Form buttons grow and their spinner sits high while they wait #636

Closed

Description

@richshaw

What happens

While a form button waits for its answer, it shows a spinner and a word like "Closing…" or "Saving…". On those buttons the spinner and the word sit above the middle of the button, and on smaller buttons the button also grows taller for as long as it waits. The answer usually comes quickly, so people see a jump: the button grows, its contents move up, then it all snaps back.

Measured in a local copy, with the request held open so the waiting state stays on screen:

  • Close signup (12px text, in the ⋯ panel after Ask before closing a signup, and move Close signup into the ⋯ menu #571): 30px tall, then 34px while it says "Closing…". The spinner sits 2px above the middle. "Not yet" beside it stays 30px, so the two buttons no longer line up.
  • Save on a signup's Settings tab (14px text): 36px, then 39px. The spinner sits 1.5px above the middle.
  • Close signup in the phone's More actions sheet (15px text, at least 48px tall): the height holds, but the spinner and the word sit about 2px high.

Where: AsyncSubmitButton in src/components/ui/async-submit-button.tsx. It wraps the spinner and the word in an inline-flex span. That span's first item is the spinner, an empty inline-block with no text, so the browser takes the span's baseline from the spinner's bottom edge. The span then sits on the text's baseline with all of its 16px above it, which lifts it, and the descent below the baseline adds to the line's height.

Every AsyncSubmitButton does this, since none of them makes the button itself a flex box:

Publish and share's "Checking…" from #580 doesn't do it, because its spinner sits in a block-level grid.

Fix, in outline

  • In AsyncSubmitButton, make the wrapper a block-level flex box: flex items-center justify-center gap-2 instead of inline-flex items-center gap-2. justify-center keeps the contents centred in buttons wider than their text, such as the consent screen's full-width ones.
  • Tried in place in the browser: the Close signup and Save buttons then keep their height while waiting, and the spinner sits exactly in the middle.

Watch for

  • Every user of the component changes at once. Look at each one while it waits, on a computer and at phone width. They're listed above. A tophat can hold the request open (for example, delay any POST carrying a Next-Action header in Playwright's page.route) so the waiting state stays on screen.
  • Left-aligned buttons. If any user relies on the contents sitting at the start of a wide button, justify-center moves them. None does today.
  • The word still changes the button's width. "Closing…" is a little wider than "Close signup", so "Not yet" moves left by about 4px. That's separate. Publish and share avoids it by keeping both labels in one grid cell, if that's wanted here too.

Done when

  • None of the buttons listed above changes height while it waits, and the spinner sits in the middle, checked in the running app on a computer and at 390px wide.
  • pnpm lint && pnpm typecheck && pnpm knip && pnpm test pass.

Found while tophatting #571 on 2026-10-25, after the owner saw the Close signup button jump. Checked by measuring the waiting buttons in a local copy and trying the fix in the browser. The component is the same on main at 35288fa.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomers

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions