fix(reports): make drill-down reachable by keyboard and the page fit a phone - #152
Merged
Conversation
This was referenced Sep 2, 2026
KenTaniguchi-R
force-pushed
the
fix/145-cash-flow-tiles
branch
from
September 2, 2026 12:57
c7c33df to
c5c787f
Compare
…a phone The access and layout half of #147. Charts and information design follow separately. Every drill-down target measured `tabIndex: -1` with no role — 18 table rows, 9 legend entries and the Sankey nodes. Drill-down was mouse-only. Table rows, the donut legend and the Sankey rects now take focus, announce what they do, and activate on Enter or Space through a shared `activateOnKey`. A target with nothing to activate stays out of the tab order rather than becoming an empty stop. The donut's own sectors are still mouse-only — Recharts sectors cannot hold focus — but the legend beside them offers the same targets, so nothing is now reachable only by mouse. At 555px the page overflowed to 569 and scrolled sideways. Two causes. The tablist computes `overflow-x: visible` and simply widened the page, leaving Trends and Net Worth off-screen with no scroll affordance; it sits in a scroll container now. And `report-summary-bar` set `gridTemplateColumns` as an inline style, which no breakpoint can reach, so a phone kept the desktop column count and clipped the third tile to its icon; it is grid classes now, one column on a phone, and each tile is `min-w-0` so `1fr` stops yielding to a long label (the tiles measured 173 / 133 / 180 rather than equal thirds). The donut legend painted a stray horizontal scrollbar — `overflow-y-auto` alone makes CSS compute `overflow-x: auto`, and 306 > 302 was enough to show one. Pinned to `overflow-x-hidden`. The AI assistant button is `fixed bottom-6 right-6` and floats over whatever the page ends with, so `<main>` reserves its height. This clears content at rest; a fixed button still passes over content mid-scroll, which is inherent to the pattern and a design question rather than a layout bug. Not changed: #147 also reports the sidebar avatar overlapping page content. That element is `NEXTJS-PORTAL`, the dev-tools indicator, which ships in no build — there is no product bug there. Verified at 390px and 1440px: page scrollWidth now equals clientWidth, the tablist scrolls, summary tiles are equal width, and focusing a table row, a legend entry or a Sankey node and pressing Enter opens the drill-down. Refs #147
KenTaniguchi-R
force-pushed
the
fix/147-access-layout
branch
from
September 2, 2026 13:02
9be7810 to
d27603e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The access & layout third of #147. Charts (PR B) and information design (PR C) follow separately, each with a mock first.
Stacked on #151 → #150. GitHub retargets as those merge. The diff below is only this change.
Drill-down was mouse-only
Every target measured
tabIndex: -1with no role — 18 table rows, 9 legend entries, and the Sankey nodes. Spending table rows, the Income vs Expense table, the donut legend and the Sankey rects now take focus, announce what they do (Show Groceries transactions, $1,891.26), and activate on Enter or Space via a sharedactivateOnKey. A target with nothing to activate returns no handler, so it stays out of the tab order instead of becoming an empty stop.The donut's own sectors remain mouse-only — Recharts sectors cannot hold focus — but the legend beside them offers the same targets, so nothing is reachable only by mouse.
The page overflowed sideways
At 555px client width,
scrollWidthwas 569. Two causes:The tablist. It computes
overflow-x: visible, so instead of scrolling it widened the page, and Trends and Net Worth sat off-screen with no affordance to reach them. Now in a scroll container. (Fixed locally rather than intabsListVariants, which every tab list in the app shares.)The summary bar.
report-summary-bar.tsxsetgridTemplateColumns: repeat(n, 1fr)as an inline style, which no breakpoint can reach — a phone kept the desktop column count and clipped the third tile to its icon. Grid classes now, one column on a phone. Each tile also getsmin-w-0, so1frstops yielding to a long label: tiles measured 173 / 133 / 180px and are now equal.Stray scrollbar on the donut legend
overflow-y-autoalone makes CSS computeoverflow-x: auto, andscrollWidth 306 > clientWidth 302was enough to paint one. Pinned tooverflow-x-hidden.FAB clearance
The AI assistant button is
fixed bottom-6 right-6and floats over whatever the page ends with, so<main>now reserves its height. This clears content at rest. A fixed button still passes over content mid-scroll — inherent to the pattern, and moving or fading it is a design decision, so it goes to PR B/C with a mock rather than being decided here.One claim in #147 is not a bug
The element over the sidebar is
NEXTJS-PORTAL— the Next.js dev-tools indicator, which ships in no build. Confirmed withdocument.elementFromPoint. The FAB half is real and handled above.Tests
src/lib/a11y.test.ts— 4 unit tests onactivateOnKey: returns nothing without an action (so the element stays untabbable), Enter and Space activate and callpreventDefault(Space would otherwise scroll), and every other key includingSpacebaris left alone.Colocated unit suite: 698 passed across 75 files.
Verification
Measured live at 390px and 1440px:
scrollWidth/clientWidthoverflow-xvisibleauto(561 scrollable in 531)tabIndex -1, no roletabIndex 0,role=button, labelledtabIndex -1, no roletabIndex 0,role=button, labelledoverflow-xauto, 306 > 302hiddenFocusing a table row, a legend entry and a Sankey node and pressing Enter each opened the correct drill-down sheet.
🤖 Generated with Claude Code