Skip to content

viewer: Pages stepper UX — labeled Prev/Next, scalable progress, keyboard nav, nested hierarchy - #293

Merged
ivanmkc merged 2 commits into
masterfrom
viewer/pages-stepper-ux
Jul 29, 2026
Merged

viewer: Pages stepper UX — labeled Prev/Next, scalable progress, keyboard nav, nested hierarchy#293
ivanmkc merged 2 commits into
masterfrom
viewer/pages-stepper-ux

Conversation

@ivanmkc

@ivanmkc ivanmkc commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Improves the Pages renderer's step-through ("debugger") experience. Draft for review — authoring contract is unchanged.

UX review of the current stepper

Reviewed as a generic component against the live example (emberwood/synthbench, page 2 "Algorithm Debugger") and local renders:

  1. Step affordance is weak. A row of numbered buttons + tiny unlabeled chevrons reads as archive pagination, not step through. For a debugger the numbers are meaningless — nobody wants "page 5", they want next stage. The primary action (next) has the smallest hit target on the bar.
  2. Position feedback is an afterthought. The only statement of where you are — 2/5 · Algorithm Debugger (step through) — is small, dimmed, and to the right of the buttons. The current page title (the one label that says what you're looking at) is the least prominent text on the bar.
  3. Nested pagers are indistinguishable. Outer and inner pagers render identically (same size, same numbered row). On the live debugger board you get up to four identical button rows stacked (outer top, inner top, inner bottom, outer bottom) — it is genuinely unclear which row steps the algorithm and which flips the board page. (before-live-debugger.png, before-nested.png)
  4. No keyboard support. Arrowing through steps is the whole point of a step-through UI; there was none (the viewer has arrow/type-ahead nav for the board list, but not for pagers).
  5. Doesn't scale past ~10 pages. At 20 pages Mantine renders 1 2 3 4 5 6 7 … 20 — no sense of progress, and the collapsed middle is exactly where a debugger user lives. (before-scale20.png)
  6. No transition/context between steps. Content swaps with zero feedback; on similar-looking steps (the common case in an iteration stepper) it's easy to miss that anything changed.
  7. Long titles wrap awkwardly in the dimmed suffix and are duplicated verbatim on the top and bottom bars. (before-longtitle.png)
  8. Accessibility. No aria-current on the active step, no live announcement on step change, and — found by the new e2e — reaching an end while keyboard-stepping dropped focus entirely (the focused button became disabled).

What changed (packages/viewer/src/client/renderers/pages.tsx)

  • Labeled ‹ Prev / Next › buttons at both ends of the bar, disabled-styled at the ends but still focusable (data-disabled/aria-disabled, not disabled) so arrow-key stepping survives hitting page 1 / page N. Each button carries the neighboring page's title as a tooltip hint.
  • Current title is the bar's headline — bold, truncated with a full-text tooltip — with a dimmed N of M count beside it.
  • Progress affordance that scales: clickable dots (title-tooltipped, aria-current="page") up to 16 pages; beyond that a slim progress bar plus a jump-by-title <select> (3/20 · Step 3), which keeps random access at any page count.
  • Keyboard: ArrowLeft/ArrowRight/Home/End while focus is anywhere in a pager bar. stopPropagation keeps a nested pager's keys from also driving the outer one.
  • Nested hierarchy: a depth context renders inner pagers with compact controls and smaller dots, and CSS insets nested pagers behind a left rule — outer vs inner is now legible at a glance.
  • Subtle step transition (160ms fade + 4px rise) on page change, disabled under prefers-reduced-motion.
  • Aria: bars are labeled role="group"s; the top bar's title/count is a polite live region so a step change is announced once; dots and the jump select have focus-visible rings.
  • Single-page boards render the title + content without stepper chrome.

Contract preserved: {"type":"Pages","props":{"pages":[{title,node}...]}} is untouched; off-page trees are still never resolved (lazy pages), clamping on live-patch shrink kept, no changes to core validation or the resolver.

Deliberately not done: auto-advancing an outer pager when an inner one hits its end (predictability over cleverness); swipe gestures; URL/hash persistence of the current page (worth a separate discussion — it would make deep links into a specific step shareable).

Before / after

Before After
Nested debugger before nested after nested
20-page scale case before scale after scale
Long title before long after long
Narrow (420px) before narrow after narrow

More in reviews/pages-stepper/ — incl. the live debugger board before (before-live-debugger.png), first-page disabled state (after-p1.png), and dark theme (after-dark.png).

Test plan

  • npm run build clean (monorepo)
  • npm test — 734/734 pass (6 new Pages unit tests: registration, lazy off-page content, focusable-disabled ends, dots→progress+select switchover at 16, single-page chrome, empty/untitled fallbacks)
  • node e2e/keyboard.e2e.mjs — 19/19 (5 new: arrow step fwd/back, nested isolation, Home/End; the Home/End check caught the focus-loss-on-disabled bug)
  • Playwright before/after screenshots (light + dark, 1200px + 420px, 5-page top-level + nested 8-step + 20-page)
  • Deploy to Cloud Run and re-check the emberwood synthbench debugger board

… nav, nested hierarchy

The Pages pager is used heavily as a step-through debugger (nested
Pages inside Pages). Replace the numbered Pagination row with a
stepper built for that: labeled Prev/Next with focusable end-disabled
states, the current title shown prominently with an 'N of M' count,
clickable title-tooltipped dots (<=16 pages) or a progress bar plus a
jump-by-title select (>16), arrow/Home/End keys while a bar has focus
(stopPropagation keeps nested pagers independent), a subtle page-in
transition honoring prefers-reduced-motion, and depth-context sizing +
inset so inner pagers read as subordinate. Authoring contract
unchanged: {pages:[{title,node}...]} renders one lazy page at a time.

Evidence: reviews/pages-stepper/ before/after Playwright shots.
6 unit tests + 5 keyboard e2e checks added.
@ivanmkc
ivanmkc marked this pull request as ready for review July 29, 2026 15:06
@ivanmkc
ivanmkc merged commit e5fd93a into master Jul 29, 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.

2 participants