Public: anyone can read this; the crew can change it.
The people list is keyboard-first: j/k move between rows, enter opens one #10367
Check · Review · Land · Live done — Landed and live in production.
The people table at `/settings/people` now joins the shell's j/k row traversal: j and k move between people, and enter opens the focused person's page.
shipped production is serving it deploy abf00b0
The people list is keyboard-first: j/k move between people on /settings/people, and enter opens the focused person. I fixed one of the three failing checks and pushed. I couldn't find the first-view budget test, and I think the third was a flaky timing test.
What changed
peoplePageno longer putsdata-nav tabindex="-1"on each<tr>. Rows are plain<tr>again, and the person's name link carriesdata-nav.userLinkinsrc/web/ui.tstakes a newnavoption for that, so the shared[data-nav]handling insrc/web/client.tsworks unchanged.test/keys-people-list.test.tsnow looks for thedata-navlink inside each row.
The three failures
- Settings › People at 390 px: this failed because
test/people-fit.test.tscounts<tr>tags, and my attributes on the row stopped them matching. With plain<tr>it passes again. - First-view budget: I couldn't find a test with that name in the repo, so I couldn't run it. The row attributes I removed made the people page slightly smaller, which may be enough to fix it.
- Issue listing costs the same for 5 rows as for 50: it passes here and my change doesn't touch issue listings, so I think it was a timing flake.
Checks run before pushing
bun scripts/design-lint.tsis clean.tsc --noEmitis clean.- These pass:
keys-people-list,people-fit,counts,settings-routesandtui-size. Inpeople-fitthe 390 px browser measurement is skipped because there is no working chromium here. - I didn't run
scripts/gate.ts.
🤖 Generated with Claude Code
The failing check is not caused by this pull, and I did not fix it. I merged current main into the branch and pushed, so the check will most likely fail again on the same test.
each page stays inside its first-view budget is in test/attention.test.ts, which came in with main and was missing from the branch. The test does not render /people. It fails the same way on a clean checkout of origin/main:
- The
repositoryandapppages show 197 words against a budget of 189. - Both pages show 29 blocks against a budget of 28.
- This happens at both 390 px and 1280 px.
That change landed on main without updating docs/design/attention-budget.json. Raising a budget needs a note in the pull, and that is a decision for whoever owns that change, so I left the file alone. main needs either a trimmed repository page or a raised budget before this pull can land green.
What the pull changes: the people list is keyboard-first. The name link in each row carries data-nav through a new nav option on userLink. The rows stay plain <tr>. test/keys-people-list.test.ts covers it.
What I ran: bun x tsc --noEmit is clean. test/keys-people-list.test.ts and test/people-fit.test.ts pass (5 tests, 1 skipped). scripts/fast.ts said the diff reaches 1457 test files (about 2 hours), so I ran only the two people-list suites.
To try it: on the preview at /people, press j and k to move between rows and enter to open one.
🤖 Generated with Claude Code
The behaviour works: each person name link on /settings/people carries data-nav, so the shell j/k/enter handling reaches it, with no JS added, no new markup classes and design-lint clean. One thing to fix: the issue asks for a test that also asserts NO data-nav on the empty page, and test/keys-people-list.test.ts only covers the seeded page. Add a second test with a fresh app that has no people list rows (or the empty state the page renders) and assert zero data-nav. Without it, a regression that leaves data-nav on the empty state would pass. Also note: data-nav sits on the link, not the row with tabindex=-1 as box 1 words it; the link is natively focusable so this is fine for keyboard and screen reader, and the people-fit reason is sound. The red check comes from attention-budget on main, not this pull.
Each person name link on /settings/people carries data-nav, so the shell's j/k/enter handling reaches it. No JavaScript or markup classes were added. This push adds the missing empty-page test.
What changed in this push
test/keys-people-list.test.tshas a second test, run on its own fresh app. The only admin is flaggedis_agentafter sign-in, whichlistPeopleexcludes, so the page renders its "Nobody has joined yet." state.- The test asserts that state, then asserts no
data-navand noid="people"table after the "Guests, members and admins" heading. - I scoped the assertion to that block because the settings sidebar on the same page legitimately has
data-navon its own links. A whole-page "no data-nav" check could never pass. bun test test/keys-people-list.test.tspasses (2 tests) andbun x tsc --noEmitis clean. I did not runbun scripts/gate.tsorbun scripts/design-lint.tson this push.
What I did not change
data-navstays on the link rather than on the row withtabindex="-1"as box 1 words it. I agree with your review that the link is natively focusable, so this works for keyboard and screen readers.- The red check from attention-budget on main is not from this pull, so I left it alone.
Approved. The empty-page test I asked for last round is there and scoped correctly: it checks the people block after its heading, because the settings sidebar legitimately carries data-nav. The earlier ask to keep rows as plain <tr> is also met, so people-fit still counts them. data-nav sits on the name link, the same way issues.ts uses the nav option. A link is natively focusable, so j/k/enter work for keyboard and screen reader with JavaScript off. Next, non-blocking: (1) put the unrelated attention-budget failure on main in its own pull instead of leaving it to this one; (2) the people rows have no visible focus style beyond the link default, so check that the focused row is obvious on a phone with a keyboard attached. I did not read the preview signed in, because it answered 303 to my token.
The people table at
/settings/peoplenow joins the shell's j/k row traversal: j and k move between people, and enter opens the focused person's page.I checked it with the new test, which passes and fails without the change.
bun test test/keys.test.ts test/help-keys.test.ts test/people-timing.test.tsalso passes (10 skipped, in the unix-socket mode this sandbox runs). Thetsc,scripts/gate.tsanddesign-lintchecks print no errors, but I only looked at the tail of their output. I did not run the full suite.<tr>insrc/web/pages/people.tscarriesdata-nav tabindex="-1". The table doesn't use therow()helper, so I wrote the attribute directly and importedraw./username). The row's other links are the apps the person made, and I left them alone, so there is no nesteddata-nav. No new JavaScript.test/keys-people-list.test.tssigns in as an admin and checks that the page has onedata-navper person. It also checks that each row's first link is that person's page.- The issue asks for a no-
data-navcheck on the empty page. I didn't add one, because an admin is always listed, so that page can't be reached. - Because the settings sidebar's section links also carrydata-nav, the test counts only inside#people.Try:line in a commit message, so the commit has one.To try it: sign in as an admin, open
/settings/people, press j and k, then enter.🤖 Generated with Claude Code
Closes #10353