Skip to content

Improve onboarding and shell accessibility - #1097

Merged
kody-bot merged 6 commits into
mainfrom
cursor/w3-onboarding-remix-a11y-6972
Jul 31, 2026
Merged

kody-bot merged 6 commits into
mainfrom
cursor/w3-onboarding-remix-a11y-6972

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 31, 2026 •

Copy link
Copy Markdown
Owner

Part of #1069

Summary

  • replace the hand-rolled onboarding client tabs with remix/ui/tabs, full arrow/Home/End keyboard behavior, and theme-token styling
  • add a skip link, route-change focus management, active-list semantics, shared focus rings, and correct owned auth-page heading levels
  • enable oxlint jsx-a11y checks and add axe scans for six representative routes in light and dark themes

Verification

  • npm run validate — passed on the rebased branch (format, lint, typecheck, 1,705 unit tests, 22 Playwright tests, MCP E2E, backup build, primitives, migrations, docs)
  • npm run test:e2e:a11y — 3 tests passed; 12 route/theme scans reject critical and serious violations
  • manual Chromium walkthrough — ArrowRight/Home/End select only the matching MCP instructions; skip-link and SPA route focus both move through <main id="main">
  • post-merge validation and production deployment passed; https://heykody.dev/health reports merge SHA c4b0261a47d5460140d7aa10ab4d1e2b9eaee3b5

keyboard_accessibility_walkthrough_clean.mp4

Community route focus walkthrough final state

Known sibling-owned exclusion

The /login scan excludes only a[aria-pressed]: the signup-gateway-owned login toggle still has invalid aria-pressed on an anchor on current main. The rest of /login remains scanned. This track did not edit login.tsx or waitlist-banner.tsx as directed.

System recap — extends an existing primitive (medium risk)

Mode: recap · Base: main @ 775c58e · Head: 9df7262

Classification: extends — this PR changes browser-app keyboard, focus, and accessibility behavior; no new system primitive is introduced.

Primitives touched

Primitive Group Impact
app-ui surfaces extends — framework tabs, shell navigation focus, semantic selection, and automated accessibility coverage

System map

Browser-app routes and the persistent shell now share accessible navigation behavior and are guarded by lint and browser checks.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	appUi["app-ui<br/>Browser app (Remix 3)"]:::extended
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Conductor report

  • Status: merged as c4b0261a; post-merge validation and production deployment are green
  • What I verified: framework tab keyboard behavior and panel exclusivity, skip-link focus, SPA route focus, both theme token sets, critical/serious axe results on six representative routes, the full repository gate, split PR CI, post-merge CI, and production health SHA
  • Scope spill: none; login.tsx and waitlist-banner.tsx remain untouched. Their signup-gateway-owned <h2>, invalid login-link aria-pressed, and waitlist input outline suppression are explicitly deferred
  • Kent decisions: use remix/ui/tabs instead of hand-rolled ARIA; retain exact sibling ownership; enforce semantic jsx-a11y rules while disabling three Remix-mix false positives (autocomplete-valid, label-has-associated-control, role-supports-aria-props); keep route focus keyed to pathname so query-only filters do not steal focus (CodeRabbit's pathname+search suggestion was intentionally not applied)
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added a keyboard-accessible “Skip to content” link and improved focus management after navigation.
    • Improved visible focus indicators across forms, switches, tabs, and interactive controls.
    • Active account navigation items now announce their current state to assistive technologies.
    • Improved heading structure on authorization, verification, onboarding, and password reset pages.
  • Tests

    • Added automated accessibility testing across public, account, and admin routes in light and dark themes.
    • Added accessibility linting rules to identify common interface issues.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The client adds shared focus styles, skip-link and navigation focus handling, semantic heading and tab updates, active-state accessibility attributes, Oxlint rules, and Axe-based Playwright tests for public and authenticated routes.

Changes

Accessibility improvements

Layer / File(s) Summary
Shared accessibility style primitives
packages/worker/client/styles/style-primitives.ts
Added reusable focus-ring and visually hidden style primitives.
Navigation focus management
packages/worker/client/client-router.tsx, packages/worker/client/app.tsx
Added navigation-end listeners, skip-to-content behavior, and focus targeting for the main content container.
Accessible component semantics
packages/worker/client/editable-text.tsx, packages/worker/client/routes/*, packages/worker/client/styles/style-primitives.ts
Updated focus styles, active navigation semantics, heading levels, and onboarding tabs.
Accessibility linting and end-to-end scans
.oxlintrc.json, tools/oxlint/oxlint-rules.json, package.json, e2e/a11y.spec.ts
Configured jsx-a11y rules and added themed Axe scans for public, account, and admin routes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant App
  participant Router
  participant Main
  User->>App: Activate Skip to content
  App->>Main: Focus `#main`
  App->>Main: Scroll into view
  Router->>App: Emit navigationend
  App->>Main: Focus after pathname change
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the pull request's main accessibility improvements to onboarding and the application shell.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/w3-onboarding-remix-a11y-6972

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

cursoragent and others added 6 commits July 31, 2026 05:49
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@cursor
cursor Bot force-pushed the cursor/w3-onboarding-remix-a11y-6972 branch from 3ea578e to 9df7262 Compare July 31, 2026 05:49
@kody-bot
kody-bot marked this pull request as ready for review July 31, 2026 06:02
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-1097.kody-a99.workers.dev

Worker: kody-pr-1097
D1: kody-pr-1097-db
KV: kody-pr-1097-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/client/app.tsx`:
- Line 53: Update the focus-reset tracking around lastFocusManagedPathname to
include the URL search string, using a pathname-plus-search identifier such as
lastFocusManagedDocumentPath. Compare this full document location when deciding
whether to reset focus, so redirects and query-filtered same-path navigations
are handled while preserving focus when the visible main content is unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 16bdaa0c-5b41-4638-82ae-f1b60d07594f

📥 Commits

Reviewing files that changed from the base of the PR and between 775c58e and 9df7262.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (15)
  • .oxlintrc.json
  • e2e/a11y.spec.ts
  • package.json
  • packages/worker/client/app.tsx
  • packages/worker/client/client-router.tsx
  • packages/worker/client/editable-text.tsx
  • packages/worker/client/routes/account-management-components.tsx
  • packages/worker/client/routes/account-remote-connectors.tsx
  • packages/worker/client/routes/oauth-authorize.tsx
  • packages/worker/client/routes/oauth-callback.tsx
  • packages/worker/client/routes/onboarding-mcp-client-tabs.tsx
  • packages/worker/client/routes/reset-password.tsx
  • packages/worker/client/routes/verify.tsx
  • packages/worker/client/styles/style-primitives.ts
  • tools/oxlint/oxlint-rules.json

let lastSessionRefreshAt = 0
let sessionMaybeStale = false
let currentPathname = readRouterPathname(handle)
let lastFocusManagedPathname = currentPathname

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 \
  -e 'navigate\(' \
  -e 'readRouterSearch' \
  -e 'URLSearchParams' \
  -e 'searchParams' \
  packages/worker/client --glob '*.ts' --glob '*.tsx'

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Files needed =="
fd -a '^(app|router-scroll-state|client-router|routes/(account-memories|account-jobs|admin-users|account-remote-connectors|account-package-invocation-tokens)).*\.(tsx?|ts)$' packages/worker/client | sed 's#^\./##' | sort

echo
echo "== app.tsx focus handling =="
sed -n '1,150p' packages/worker/client/app.tsx

echo
echo "== router-scroll-state.contract =="
sed -n '1,80p' packages/worker/client/router-scroll-state.ts

echo
echo "== navigator event details =="
rg -n -C 8 'function buildRouterNavigationEvent|interface RouterNavigationEventDetail|targetUrl|location.search|nextPathname' packages/worker/client/client-router.tsx packages/worker/client/app.tsx packages/worker/client/router-scroll-state.ts

echo
echo "== query-only navigation helper =="
sed -n '150,210p' packages/worker/client/routes/account-memories.tsx
sed -n '370,430p' packages/worker/client/routes/account-package-invocation-filters.tsx 2>/dev/null || sed -n '390,445p' packages/worker/client/routes/account-package-invocation-tokens.tsx
sed -n '255,285p' packages/worker/client/routes/admin-users.tsx
sed -n '410,430p' packages/worker/client/client-router.tsx

echo
echo "== structural search for navigate(to) in routes =="
rg -n -C 4 'navigate\(\s*\`?\$\{[^`]+\$\{[^`]+search|navigate\(nextPath|navigate\(\s*build' packages/worker/client/routes --glob '*.ts' --glob '*.tsx' | head -n 240

Repository: kentcdodds/kody

Length of output: 22955


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== client-router navigation creation/fire/call implementation =="
sed -n '60,105p' packages/worker/client/client-router.tsx
sed -n '175,205p' packages/worker/client/client-router.tsx
sed -n '350,370p' packages/worker/client/client-router.tsx
sed -n '560,615p' packages/worker/client/client-router.tsx
sed -n '820,870p' packages/worker/client/client-router.tsx

echo
echo "== search/filter helpers and route render fragments =="
sed -n '1,120p' packages/worker/client/routes/account-memories.tsx
sed -n '515,555p' packages/worker/client/routes/account-memories.tsx
sed -n '1,115p' packages/worker/client/routes/account-package-invocation-tokens.tsx
sed -n '465,505p' packages/worker/client/routes/account-package-invocation-tokens.tsx
sed -n '1,110p' packages/worker/client/routes/admin-users.tsx
sed -n '710,750p' packages/worker/client/routes/admin-users.tsx

echo
echo "== focus/main anchor references =="
rg -n '"`#main`"|id=["'\'']main|getElementById\(["'\'']main|navigate\(["'\'']`#main`' packages/worker/client --glob '*.tsx' --glob '*.ts'

Repository: kentcdodds/kody

Length of output: 20822


Track navigation content, not just pathnames, for focus resets.

nextPathname does not cover redirects, query-filtered same-path navigations, or path changes that keep the same main content. Use an equivalent of lastFocusManagedDocumentPath (pathname + search) or skip focus reset only when the route’s visible <main> content did not change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/client/app.tsx` at line 53, Update the focus-reset tracking
around lastFocusManagedPathname to include the URL search string, using a
pathname-plus-search identifier such as lastFocusManagedDocumentPath. Compare
this full document location when deciding whether to reset focus, so redirects
and query-filtered same-path navigations are handled while preserving focus when
the visible main content is unchanged.

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.

3 participants