Skip to content

fix(frontend): a11y + clarity punch-down (focus-trap · 44px targets · MoS labels · warning hierarchy)#355

Merged
dackclup merged 2 commits into
mainfrom
claude/sharp-newton-8pj6p
Jun 1, 2026
Merged

fix(frontend): a11y + clarity punch-down (focus-trap · 44px targets · MoS labels · warning hierarchy)#355
dackclup merged 2 commits into
mainfrom
claude/sharp-newton-8pj6p

Conversation

@dackclup
Copy link
Copy Markdown
Owner

@dackclup dackclup commented Jun 1, 2026

A11y + clarity punch-down (from $impeccable audit + critique)

Implements the full P1+P2+P3 punch-down the user chose from the whole-app audit + critique (read-only review; this PR is the fixes). Placeholder hero tiles kept as reserved per the PR #344 decision. The critique snapshot is committed (.impeccable/critique/…) so $impeccable polish can read it next session.

P1

  • FilterDrawer focus-trap — traps Tab/Shift+Tab within the dialog, moves focus in on open, restores to the trigger on close (WCAG 2.4.3; both assessments agreed Tab escaped behind the backdrop). + type="button" on the close X.
  • Touch targets ≥ 44px (min-h-[44px] / h-11 w-11) — Sidebar nav + mobile-close + brand link, AppShell hamburger, ThemeToggle, FilterDrawer buttons + search, RankingTable Filters/search/pagination, PriceTimePeriodSelector, DualRange (44px grab zone + larger thumbs), detail back-link.

P2

  • MoS-anchor labelsFairPriceCard "Margin of safety (vs fair value)"; FairPriceBarChart "vs today's price" — the two different %s on one page no longer read as a contradiction.
  • Warning-card hierarchyTier2EventCard + RiskSummaryCard <h2> take a severity tone (rose veto / amber annotate, else neutral) so they outweigh the flat neutral data-section eyebrows.

P3

  • DualRange min/max aria-label; Disclaimer role="alert"role="note" + aria-expanded on the more/less toggle; price-chart <section> aria-label + an sr-only latest-price summary (the Recharts SVG is opaque to AT).

Skipped as verified false positives

dark loss-chance "pink-salmon" (the !important text-red-700 remap wins in dark → already soft) · period aria-pressed (already the correct role="radio"+aria-checked) · sort first-click (the chevron does move) · StockLogo alt (alt="" aria-hidden, correct decorative) · loss-chance "color-only" (the badge aria-label carries the band + mobile prints it as text).

Verified: tsc --noEmit clean + next build 506 routes. 11 component/page files + the committed snapshot; no compute / schema / scoring / valuation change. CLAUDE.md §Gotchas + AGENTS.md record the touch-target / focus-trap / warning-heading standards. Design + browser review gate running.

https://claude.ai/code/session_012xxKfyR939bZDmbxxqMFZi


Generated by Claude Code

Implements the full P1+P2+P3 punch-down from the whole-app audit+critique
(read-only review; this PR is the fixes). Placeholder hero tiles kept as
reserved per the user's PR #344 decision.

P1:
- FilterDrawer: trap Tab/Shift+Tab within the dialog, move focus in on open,
  restore to the trigger on close (WCAG 2.4.3); + type="button" on close X.
- Touch targets >= 44px (min-h-[44px] / h-11 w-11): Sidebar nav + mobile-close
  + brand link, AppShell hamburger, ThemeToggle, FilterDrawer buttons + search,
  RankingTable Filters/search/pagination, PriceTimePeriodSelector, DualRange
  (44px grab zone + h-5 thumbs), detail back-link.

P2:
- MoS-anchor labels: FairPriceCard "Margin of safety (vs fair value)" +
  FairPriceBarChart "vs today's price" -- the two %s on one page no longer
  read as a contradiction.
- Warning-card hierarchy: Tier2EventCard + RiskSummaryCard <h2> take a severity
  tone (rose veto / amber annotate, else neutral) so they outweigh the neutral
  data-section eyebrows.

P3:
- DualRange min/max aria-label; Disclaimer role=alert -> role=note +
  aria-expanded on more/less; price-chart section aria-label + sr-only
  latest-price summary (the Recharts SVG is opaque to AT).

Skipped as verified false positives: dark loss-chance (the !important
text-red-700 remap wins in dark -> already soft); period aria-pressed (already
role=radio+aria-checked); sort first-click (chevron moves); StockLogo alt
(alt="" aria-hidden); loss-chance color-only (badge aria-label carries the band).

Verified: tsc --noEmit clean + next build 506 routes. Also commits the critique
snapshot so $impeccable polish can read it next session. No compute / schema /
scoring / valuation change.

https://claude.ai/code/session_012xxKfyR939bZDmbxxqMFZi
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
quantrank Ready Ready Preview, Comment Jun 1, 2026 12:03pm

…px targets

Two touch-target gaps the design + browser review caught on the punch-down:
- stock-detail happy-path back-link (page.tsx) was missing min-h-[44px] (the
  earlier replace_all only caught the not-found branch — the two were not
  byte-identical); the primary back-link was ~20px.
- PriceTimePeriodSelector period buttons (1M/6M/YTD/1Y/5Y) were 24px; added
  min-h-[44px] to the base class (items-center already centers the label, so
  the visual chip height is unchanged, only the hit area grows).

tsc --noEmit clean + next build 506 routes.

https://claude.ai/code/session_012xxKfyR939bZDmbxxqMFZi
@dackclup dackclup marked this pull request as ready for review June 1, 2026 12:11
@dackclup dackclup merged commit 93231b7 into main Jun 1, 2026
4 checks passed
@dackclup dackclup deleted the claude/sharp-newton-8pj6p branch June 1, 2026 12:11
dackclup pushed a commit that referenced this pull request Jun 1, 2026
…uch target

$impeccable critique re-run follow-up. Normalize the too-light secondary-text
token text-slate-400 dark:text-slate-500 (fails WCAG AA in both modes: ~2.6:1
light / ~3.75:1 dark) to the project standard text-slate-500 dark:text-slate-400
(~4.8:1 / ~7:1) across 16 components/pages. Add min-h-[44px] lg:min-h-0 to the
FilterDrawer selection chips (the touch target #355 missed). Flip the detail-page
price-chart placeholder text to the standard token.

Verified: tsc --noEmit clean + next build 506 routes. slate-* is outside the
globals.css soft-override allowlist so contrast is on raw hex; disabled controls
+ decorative aria-hidden icons correctly left faint. No compute/schema/scoring change.

https://claude.ai/code/session_012xxKfyR939bZDmbxxqMFZi
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