Skip to content

[a11y][mobile] 40 interactive elements per screen are under 40px — 12px checkboxes on Behaviour, 16px Remove on Repo, 24px message actions #389

Description

@serge-ivo

Measured, on the live console, in WebKit at phone widths

A sweep of 21 routes at 320px and 390px counted every interactive element inside <main> whose
rendered box is under 40px in either axis. Apple HIG says 44×44pt; Material says 48×48dp. 40 is
the lenient floor, and these are the counts against it:

route targets < 40px smallest examples
Assistant / Board 40 24×24 Copy message · 24×24 Delete this turn · 30px Load earlier
Terminals 39 26px agent links
Settings (instance) 30 28px Save · 26px Refresh
Preferences 30 30px Reconnect / Disconnect
Browse 28 32px Subscribe · 34px Open →
Behaviour 27 12×12 checkboxes
Knowledge 10 38px sub-tabs
Data 9 24px Records / Runs
Repo 5 16px Re-index · 16px Remove
tmux / Terminal 7 32×22 view switch

Identical at both widths — this is not a narrow-viewport artefact, it is the sizing everywhere.

The three that are not borderline

  1. 12×12px checkboxes on the Behaviour tab (BehaviourTab.tsx:379 — a bare
    <input type="checkbox" className="mt-1">, so it renders at the UA default). That is a quarter
    of the recommended area, on the tab whose entire job is toggling how an agent talks. Everything
    else on that tab is a slider or a select; these are the only controls, and they are the smallest
    thing on the page.
  2. 16px-tall buttons on the Repo tab — Re-index and Remove. Remove deletes an indexed
    repository.
    A destructive action at 16px, next to a 16px non-destructive one, on a touch
    surface, is a mis-tap waiting to happen.
  3. 24×24px message actions (Copy / Delete this turn / speaker) on every chat bubble. These are
    also the controls a hands-free user reaches for when voice has gone wrong — the moment when
    precision is lowest.

Why this is not simply "make the buttons bigger"

The controls are hand-authored, one at a time — #366 measured 291 distinct control class strings in
14 shapes, with no Button primitive anywhere. So "40px minimum" cannot be applied as one decision;
today it is 291 edits, and the next control added starts the count again.

That makes this a strong argument for #366's component layer landing first: a Button with
sm/md sizes whose smallest variant still clears 40px of hit area (padding, not necessarily
visual size — an icon button can look 24px and be tappable at 44 via padding or a ::before
expansion) fixes the whole class in one place, and the next 291 sites inherit it.

Suggested order: #367 (the system, shipped) → #366 (the primitives, with the tap-target floor
baked into them) → this ticket becomes "adopt the primitive", not "resize 291 things".

What to decide

  • The floor. 44×44 (Apple) is the safer number; 40 is what this audit measured against. Pick one
    and put it in DESIGN-SYSTEM.md so it is checkable — that document is descriptive today and this
    would be its first prescriptive rule, which is worth doing deliberately.
  • Hit area vs visual size. An icon button does not have to look 44px. Deciding this once is
    what stops the fix from making the console look like a tablet app.
  • Whether to guard it. The e2e mobile sweep already visits every route; a "no interactive element
    under Npx" assertion is the same shape as the overflow guard and would hold the line.

Measured 2026-08-08 against production (post-#384), WebKit, 320px and 390px, signed in.
Files: store/console/src/tabs/BehaviourTab.tsx:379, store/console/src/tabs/RepoTab.tsx,
store/console/src/pages/InstanceDetail.tsx:54, and ~291 other control sites (#366).

Metadata

Metadata

Assignees

No one assigned

    Labels

    frontendFrontend / UI work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions