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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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.

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.ts also passes (10 skipped, in the unix-socket mode this sandbox runs). The tsc, scripts/gate.ts and design-lint checks print no errors, but I only looked at the tail of their output. I did not run the full suite.

  • The change: each <tr> in src/web/pages/people.ts carries data-nav tabindex="-1". The table doesn't use the row() helper, so I wrote the attribute directly and imported raw.
  • Enter: it follows the row's first link, which is the person's own page (/username). The row's other links are the apps the person made, and I left them alone, so there is no nested data-nav. No new JavaScript.
  • Test: test/keys-people-list.test.ts signs in as an admin and checks that the page has one data-nav per person. It also checks that each row's first link is that person's page.
  • Left out:

- The issue asks for a no-data-nav check 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 carry data-nav, the test counts only inside #people.

  • Push: the repo's pre-push hook wanted a 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

commented

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

  • peoplePage no longer puts data-nav tabindex="-1" on each <tr>. Rows are plain <tr> again, and the person's name link carries data-nav.
  • userLink in src/web/ui.ts takes a new nav option for that, so the shared [data-nav] handling in src/web/client.ts works unchanged.
  • test/keys-people-list.test.ts now looks for the data-nav link inside each row.

The three failures

  • Settings › People at 390 px: this failed because test/people-fit.test.ts counts <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.ts is clean.
  • tsc --noEmit is clean.
  • These pass: keys-people-list, people-fit, counts, settings-routes and tui-size. In people-fit the 390 px browser measurement is skipped because there is no working chromium here.
  • I didn't run scripts/gate.ts.

🤖 Generated with Claude Code

commented

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 repository and app pages 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

Changes requested — before the latest push

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.

commented

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.ts has a second test, run on its own fresh app. The only admin is flagged is_agent after sign-in, which listPeople excludes, so the page renders its "Nobody has joined yet." state.
  • The test asserts that state, then asserts no data-nav and no id="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-nav on its own links. A whole-page "no data-nav" check could never pass.
  • bun test test/keys-people-list.test.ts passes (2 tests) and bun x tsc --noEmit is clean. I did not run bun scripts/gate.ts or bun scripts/design-lint.ts on this push.

What I did not change

  • data-nav stays on the link rather than on the row with tabindex="-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

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs