Skip to content

fix(web): improve entry and recovery accessibility#23

Merged
johannesjo merged 4 commits into
mainfrom
task/beautify-02-entry-routes
Jul 21, 2026
Merged

fix(web): improve entry and recovery accessibility#23
johannesjo merged 4 commits into
mainfrom
task/beautify-02-entry-routes

Conversation

@johannesjo

Copy link
Copy Markdown
Contributor

Summary

  • improve document titles, heading focus, busy/status/error semantics, and autocomplete behavior on entry routes
  • distinguish field-specific verification failures from global operational failures
  • improve recovery, unavailable, contact, DSA notice, and legal-page states

Why

The public entry and recovery flows exposed errors inconsistently and did not reliably move focus as views changed.

Impact

Keyboard and assistive-technology users receive clearer state transitions and actionable error feedback without changing the underlying flows.

Validation

  • 48 focused route tests
  • web TypeScript check
  • full stack: 205 web tests and 230 server tests
  • ESLint, Prettier, and production build

Stack 2 of 6. Base: task/beautify-01-foundations. Review and merge this stack from the bottom upward.

Base automatically changed from task/beautify-01-foundations to main July 21, 2026 09:00
@johannesjo
johannesjo force-pushed the task/beautify-02-entry-routes branch from b6cb92d to 22ced02 Compare July 21, 2026 10:01
Give public routes meaningful titles and landmarks, move focus with form state, expose busy and live feedback, and distinguish invalid verification codes from operational failures. Disable autofill for Space links that may carry claim credentials.
aria-busy tells assistive tech to withhold a live region's content, so
pairing it with the progress messages this branch added suppressed exactly
what they were meant to announce:

- Join's loading hero carried role="status" *and* aria-busy="true", and the
  flag never cleared (the node unmounts instead), so the message was
  permanently withheld. A test asserted the flag, locking it in.
- Connect set aria-busy on <main>, which gates the role="status" spinners
  nested under it; it only clears once the message is already gone.

Form-level aria-busy is kept: it flips back within the same submit and does
not wrap a persistent live region.

Also in this pass:

- Extract useDocumentTitle. document.title was hand-rolled in six places
  with two different mechanisms (onMount vs createEffect); the effect form
  is the correct one, so make it the only one. Fixes a latent bug in
  LegalPage, where onMount read props.title once and would not track it.
- DSA notice: the content-location field used autocomplete="url", which per
  spec means the *submitter's* own home page — browsers would offer to fill
  it in as the reported location. Now "off".
- Home no longer moves focus to the page heading on first paint; focus
  already sits at the document start, and stealing it undid the browser's
  scroll restoration. Focus still follows every view change.
- NotFound is also the fallback for a missing Space and the catch-all route,
  so "This Space doesn't exist" contradicted its own "Page not found"
  heading on any other bad path.

Verified: 168 web tests, typecheck, ESLint, Prettier, production build.
- Give managed-focus headings a focus ring. Routes move focus to their h1
  when a view swaps, but nothing indicated where it went. Scoped to headings
  so it can't catch Dialog's tabindex="-1" panel and outline a whole modal;
  :focus-visible keeps it off mouse-driven view swaps. Documented in the
  styleguide, which is where this pattern was missing.

- Keep Connect's copy-confirmation live region mounted so AT sees the text
  change instead of a region appearing with its content already in it. Free
  here specifically because .visually-hidden is positioned out of flow, so it
  costs no gap in the flex column; the in-form status messages are left alone
  for that reason and want a CSS pass of their own.

- Relabel the 404 action. "Browse Spaces" promised a public directory that
  does not exist — /spaces is the same Home component, which shows onboarding
  to a first-time visitor. The href stays /spaces: / bounces straight back
  into the last open Space.

- Name the server route behind each code-vs-global error split. Connect keys
  on 401 and Join on 400 because public-auth.ts and auth.ts disagree; that is
  correct today but reads like a typo without the reference.

Verified: 169 web tests, typecheck, ESLint, Prettier, production build, and
the built CSS keeps the :is() selector intact.
@johannesjo
johannesjo force-pushed the task/beautify-02-entry-routes branch from 22ced02 to 22eddbf Compare July 21, 2026 10:07
@johannesjo
johannesjo marked this pull request as ready for review July 21, 2026 10:25
@johannesjo
johannesjo merged commit 115c720 into main Jul 21, 2026
5 checks passed
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