feat(compare): readable comparison chart on mobile #11

Merged
tudor merged 1 commits from feat/compare-chart-mobile-readability into main 2026-07-05 21:26:01 +00:00
Owner

On mobile the compare chart was unreadable when schools score closely: the y-axis was pinned 0-100 so clustered values squashed into a thin band, and the in-chart title plus a legend row per school consumed ~40% of a 300px card.

Changes

  • Y-axis auto-fits the data on all viewports (computeYBounds in lib/utils): percentages get padding with a minimum span (never widened past 0-100 for non-negative data; pct-named trend metrics with negative deltas are not zero-clamped), progress scores stay symmetric around 0, scores (Attainment 8 etc.) fit the range.
  • Each school gets a distinct point style (circle/triangle/rect/rectRot/star) as secondary encoding — converging lines stay tellable apart, and it helps colour-blind readers. Desktop's built-in legend shows the shapes via usePointStyle.
  • Mobile (max 640px): in-chart title/legend/axis titles removed; a chip row above the canvas (colour dot + truncated name) doubles as the legend and a tap-to-focus control — tapping highlights that school's line and dims the rest. Chart card grows 300px to 340px, nearly all of it now plot area.
  • Fixes a latent colour mismatch: datasets were built from Object.entries(comparisonData), whose integer-like URN keys enumerate in ascending numeric order — line colours desynced from the school-card colours whenever the basket wasn't in ascending-URN order. The chart now receives the ordered school list. (Line colours may visibly change for some existing share URLs — that's this fix, not a regression.)
  • Years are unioned across schools (previously only the first school's years were plotted).
  • PerformanceChart's matchMedia pattern extracted into shared hooks/useIsMobile.

Tests: unit tests for metricKind/computeYBounds; new e2e journey asserts the mobile chip legend renders per school and the focus toggle works. After staging deploy, screenshot check: /compare?urns=101038,101022,101036 at 390x844 (three schools all at 86% in 2024/25 — must show three separable lines).

🤖 Generated with Claude Code

On mobile the compare chart was unreadable when schools score closely: the y-axis was pinned 0-100 so clustered values squashed into a thin band, and the in-chart title plus a legend row per school consumed ~40% of a 300px card. **Changes** - Y-axis auto-fits the data on all viewports (`computeYBounds` in `lib/utils`): percentages get padding with a minimum span (never widened past 0-100 for non-negative data; pct-named trend metrics with negative deltas are not zero-clamped), progress scores stay symmetric around 0, scores (Attainment 8 etc.) fit the range. - Each school gets a distinct point style (circle/triangle/rect/rectRot/star) as secondary encoding — converging lines stay tellable apart, and it helps colour-blind readers. Desktop's built-in legend shows the shapes via `usePointStyle`. - Mobile (max 640px): in-chart title/legend/axis titles removed; a chip row above the canvas (colour dot + truncated name) doubles as the legend and a tap-to-focus control — tapping highlights that school's line and dims the rest. Chart card grows 300px to 340px, nearly all of it now plot area. - Fixes a latent colour mismatch: datasets were built from `Object.entries(comparisonData)`, whose integer-like URN keys enumerate in ascending numeric order — line colours desynced from the school-card colours whenever the basket wasn't in ascending-URN order. The chart now receives the ordered school list. (Line colours may visibly change for some existing share URLs — that's this fix, not a regression.) - Years are unioned across schools (previously only the first school's years were plotted). - `PerformanceChart`'s matchMedia pattern extracted into shared `hooks/useIsMobile`. **Tests**: unit tests for `metricKind`/`computeYBounds`; new e2e journey asserts the mobile chip legend renders per school and the focus toggle works. After staging deploy, screenshot check: `/compare?urns=101038,101022,101036` at 390x844 (three schools all at 86% in 2024/25 — must show three separable lines). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tudor added 1 commit 2026-07-05 21:03:19 +00:00
feat(compare): readable comparison chart on mobile
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m36s
PR Checks / Backend Smoke (pull_request) Successful in 5s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 44s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 2m49s
22769b6295
The compare chart squashed clustered schools into a thin band (y pinned
0-100) under an in-chart title + per-school legend that ate ~40% of a
300px card, leaving converging lines indistinguishable on phones.

- Auto-fit the y-axis to the data on all viewports (computeYBounds in
  lib/utils: padded + min-span for percentages, symmetric around 0 for
  progress, fitted for scores; negative pct-named trend metrics are not
  zero-clamped).
- Distinct point style per school (circle/triangle/rect/rectRot/star)
  as secondary encoding for convergence and colour-blindness.
- Mobile: drop in-chart title/legend/axis titles; add a chip row (colour
  dot + name) that doubles as tap-to-focus — highlights one school's
  line and dims the rest. Chart card 300px -> 340px, nearly all plot.
- Fix a latent colour mismatch: datasets were built from Object.entries
  whose integer-like URN keys enumerate in ascending numeric order,
  desyncing line colours from card colours; the chart now receives the
  ordered school list.
- Union years across schools instead of taking the first school's.
- Extract PerformanceChart's matchMedia pattern into hooks/useIsMobile.

Unit tests for metricKind/computeYBounds; e2e journey covers the mobile
chips and focus toggle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🤖 AI Code Review (Claude Code)

This PR reworks the compare-page chart to auto-fit its y-axis to the data instead of pinning 0-100, adds distinct point shapes and a mobile tap-to-focus chip legend, fixes a real color/label desync bug (datasets now driven by the ordered schools array instead of Object.entries(comparisonData)), and adds solid unit/e2e test coverage. The change is well-tested and the core logic (computeYBounds, metricKind, useIsMobile) checks out correctly against its own test cases.

🟡 Minor

  • nextjs-app/components/ComparisonView.module.css: The taller mobile chartContainer height (340px) is scoped to the existing @media (max-width: 768px) block, but the JS mobile behavior it's sized for (hiding the in-chart legend/axis titles, showing chips) only kicks in at 640px via useIsMobile and the chip CSS. For viewports between 641px and 768px, the chart gets the extra height intended for the stripped-down mobile layout while still rendering the full desktop legend and axis titles, leaving unused vertical space.
## 🤖 AI Code Review (Claude Code) This PR reworks the compare-page chart to auto-fit its y-axis to the data instead of pinning 0-100, adds distinct point shapes and a mobile tap-to-focus chip legend, fixes a real color/label desync bug (datasets now driven by the ordered schools array instead of Object.entries(comparisonData)), and adds solid unit/e2e test coverage. The change is well-tested and the core logic (computeYBounds, metricKind, useIsMobile) checks out correctly against its own test cases. ### 🟡 Minor - **nextjs-app/components/ComparisonView.module.css**: The taller mobile chartContainer height (340px) is scoped to the existing @media (max-width: 768px) block, but the JS mobile behavior it's sized for (hiding the in-chart legend/axis titles, showing chips) only kicks in at 640px via useIsMobile and the chip CSS. For viewports between 641px and 768px, the chart gets the extra height intended for the stripped-down mobile layout while still rendering the full desktop legend and axis titles, leaving unused vertical space.
tudor merged commit 18cd805c6c into main 2026-07-05 21:26:01 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tudor/school_compare#11