feat(frontend): fluid root font-size for app-wide responsive scaling#325
Merged
Conversation
Text and layout were a flat 16px at every viewport width (414 -> 1280, measured)
— phone-tuned sizes sat unchanged on desktop, so text looked tiny and the hero
content drifted apart in the wide canvas. The app is rem-based (Tailwind), so a
fluid ROOT font-size scales every text + spacing + gap + chart dimension
proportionally with the viewport in one rule, preserving all proportions + the
design system + tabular-nums.
globals.css: html { font-size: clamp(1rem, 0.89rem + 0.45vw, 1.25rem) } —
~16px on phones (clamp floor, mobile unchanged) -> ~20px on desktop (ceiling).
The rem terms (not pure vw) keep browser zoom / user font-size prefs working
(pure-vw font-size breaks WCAG 1.4.4 resize-text).
Verified (Playwright, dark mode): root scales 16.1->17.7->18.8->20px across
414->768->1024->1280, price 24->30px; detail page + home ranking table both
read well with no horizontal overflow (scrollW == docW) at every width. Mobile
(<=414) unchanged. Documented as a CLAUDE.md §Gotcha + AGENTS.md mirror.
Frontend-only; no schema/compute/scoring/valuation change.
https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Follow-up to the fluid root font-size (a3e496c), per the frontend-design-reviewer audit: ~44 arbitrary text-[10px]/text-[11px] classes across 14 components (chip/ badge labels, table column headers, chart legend, and the FairPriceBarChart headline delta % — the one primary numeric) were FIXED px and would not follow the fluid root, drifting relatively smaller on desktop. Converted to rem equivalents — text-[10px]->text-[0.625rem], text-[11px]-> text-[0.6875rem] — pixel-identical at the 16px base (zero mobile change) but now scaling with the root on desktop. Only the Recharts tick fontSize SVG number + StockLogo px-prop letter-avatar remain px (self-contained coordinate systems, intentional). Reviewer verdict on the root-font change: PASS on all five axes (WCAG 1.4.4 sound, no compounding font-size, layout safe, design tokens intact). tsc + next build (506) clean; 1280 screenshot confirms labels render + scale. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
…ow-up) After two parallel read-only audits (expert-user-explorer empirical render across 10 widths + frontend-design-reviewer code review) of the fluid-scaling change, fix the whitespace-balance imbalances they converged on — all side-effects of the rem root growing 16->20px on desktop: - Detail hero broke at exactly 1024px: the 2-col lg:flex-row fired when the sidebar left only ~666px content, crushing the left block to ~156px. Raise the split to xl: (1280, ~1040px content), so 1024 stacks cleanly; cap the left col xl:max-w-2xl so it doesn't spread 1000px+ on ultrawide; drop the no-op lg:justify-between (flex-1 already consumed the free space). - Content max-w-6xl (72rem) expanded to 1440px at the 20px root -> sparse table/cards on 1920px. Pin to fixed max-w-[1152px] (main + footer) so the cap is viewport-stable while inner rem text/spacing still scales. - Sidebar inflated 240->300px at desktop: cap md:max-w-[240px] / collapsed md:max-w-[64px]; content gains ~60px. - Mobile card min-h-[112px] -> min-h-[7rem] (scales); search wrapper inline minWidth:200px -> class min-w-[12.5rem]; home header + detail disclaimer gain max-w-3xl for prose line-length on ultrawide. Kept the 20px fluid ceiling (user asked for larger text); the audit's suggested 20->18px reduction + the 768-834px table Sector-clip are deferred/flagged. Verified (Playwright measure + screenshots): hero=column@1024 / row@1280-1920, content=1152px@1920 (was 1440), sidebar=240@desktop (was 300), zero overflow at any width, mobile unchanged. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
On a md+ viewport (landscape phone / tablet / desktop) with the sidebar COLLAPSED, the green Q logo box (28px) and the expand-chevron toggle (32px) overlapped in the 64px rail header — px-3 leaves only ~40px content, and 28+32 don't fit side-by-side (user-reported on a real device; reproduced collapsed @900px: Q 14-46px, chevron 35-49px -> overlap). Fix: when collapsed at md+, hide the Q home-link (md:hidden) and center the chevron (md:mx-auto) as the sole header control; the Q returns on expand. Expanded + mobile-drawer states unchanged (Q + wordmark + chevron/close-X all show with room). Verified (Playwright): collapsed @900 overlap=false (link hidden, chevron centered); expanded @900 Q 14-46 / chevron 189-225 (no overlap); before/after screenshots confirm. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
Per user "ตามที่แนะนำ" (go with the recommended option) on both flagged items: 1. Fluid font ceiling 20px -> 18px (globals.css clamp 1.25rem -> 1.125rem) for tighter desktop data-density (audit MAJOR-3). Now caps at ~835px so tablet+ is a flat 18px (H1 27px not 37.5px, table cells ~15.75px); mobile keeps the 16px floor. CLAUDE.md §Gotcha + AGENTS.md mirror updated to match. 2. Ranking table<->card breakpoint md -> lg (RankingTable.tsx): portrait tablets (768-1023px, only ~530px content beside the sidebar) now use the mobile card list instead of the 7-col table that clipped the Sector column (audit MINOR-4); the table returns at lg (1024, ~784px content). Verified (Playwright): root 16.1->17.7->18.0px capping at 834px; view = cards @414/768/834, TABLE @1024/1280/1920; zero overflow at all widths; screenshots confirm 768 cards clean + 1280 table dense-but-scannable. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
… rail Revises the earlier overlap fix (which hid the Q when collapsed) per user request to keep the green Q visible. When collapsed at md+, the header now switches to a vertical STACK (md:flex-col, auto-height): the Q logo on top (still a home link; the "QuantRank" wordmark stays hidden) + the expand-chevron centered below. No overlap (stacked, not side-by-side). Verified (Playwright, collapsed @900): Q visible y14-45 / chevron below y52-88, overlap2D=false, both centered in the 64px rail; expanded unchanged (Q left / chevron right, 63px header). tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
…ne row Per user clarification (supersedes the vertical-stack approach): keep the Q logo and the expand-chevron in the SAME ROW, not overlapping. A 64px rail can't fit both, so the collapsed rail widens to a fixed 96px (md:w-[96px]) and the header stays a row but centers the group (md:justify-center md:gap-1 md:px-2); the chevron drops ml-auto when collapsed so it sits beside the Q. Verified (Playwright, collapsed @900): rail 96px, Q x12-43 + chevron x48-84 (same row, 5px gap), no overlap, header back to 63px; expanded unchanged. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
Per user request: the expand-arrow button was a 32px square; make it a portrait rectangle. md:h-12 md:w-6 when collapsed (27x54px @18px root, taller than wide) beside the Q; expanded keeps the h-8 w-8 square. Verified (Playwright, collapsed @900): chevron w27 x h54 (vertical), Q 32x32, still one row / no overlap / 96px rail. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
The narrower vertical chevron (27px vs the old 32px square) left the 96px rail with ~7px excess slack each side. Narrow the collapsed rail to a fixed 84px (md:w-[84px]) = 32px Q + gap + 27px chevron + px-2 padding. Verified @900: rail 84px, Q x10-42 / chevron x46-73 (fills the content area with ~1-2px slack), no overlap, expanded unchanged. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
The md:h-12 (48px) collapsed expand-chevron stood taller than the Q logo and stuck out above/below it. Change to md:h-7 md:w-6 — height now equals the Q (h-7, both 32px @18px root, tops/bottoms flush) while staying narrower (w-6) so it still reads as a slim portrait rectangle. Verified @900: Q 32x32 / chevron 27x32, both y15-47 (aligned), no overlap; expanded unchanged. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
…arrow below) Per user request, restore the earlier stack arrangement (over the side-by-side row): when collapsed at md+, the header is a vertical stack (md:flex-col, auto-height) with the green Q logo on TOP and the square expand-chevron centered BELOW; rail narrowed back to a fixed 64px (md:w-16 md:max-w-[64px]). Verified @900: rail 64px, Q y14-45 (top) / chevron y52-88 (below), centered, no overlap; expanded + mobile drawer unchanged. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
… clone Fixes the recurring intermittent "Python (lint + test)" CI failure on this branch. test_list_ranking_commits_returns_real_commits runs `git log -- frontend/public/data/rankings.json` then asserts >= 1 commit. On CI's shallow clone (actions/checkout@v6 -> fetch-depth: 1), when the tip commit doesn't touch rankings.json (all of this PR's commits are frontend/docs), git log returns empty and the unconditional assert fails — intermittently, depending on whether the runner reused a warm workspace with deeper history. Add a pytest.skip() guard when the commit list is empty (shallow clone), matching the PR #284 (a820cae) precedent that fixed the same pattern in a sibling test but missed this one. A full clone still exercises the real assertion + the sort/well-formedness checks below. Root-caused by ci-triage-engineer; NOT caused by this PR's frontend code. Verified: pytest tests/test_validation/test_ranking_history.py -> 18 passed (full clone); ruff clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
Per user request, size + shape the collapsed expand-chevron like the nav-item boxes below it (rounded-sm full-width box) instead of a 32px square. Collapsed header gains md:px-2 (matches the nav's px-2); the chevron becomes md:h-auto md:w-full md:py-1.5 with a subtle md:bg-slate-100 md:dark:bg-slate-800 fill so it renders as a full-width rounded rectangle. Verified @900: chevron 45x28 / nav-item 45x30 — same width + x-position + fill; expanded + mobile drawer unchanged. tsc + next build (506) clean. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
dackclup
pushed a commit
that referenced
this pull request
May 30, 2026
…shair on sidebar toggle; guard 2 flaky tests Bug A — sidebar "opens then shrinks back by itself" on refresh OR portrait↔landscape rotation: - Pre-paint a `.sidebar-collapsed` class (inline script in layout.tsx) + a globals.css rule renders the collapsed rail at its narrow width before React hydrates (the static export bakes the EXPANDED markup) — no wide→narrow flash on refresh. AppShell keeps the class synced to live state. - Gate the aside width/transform transition behind an `animate` flag that's on only for ~250ms around an EXPLICIT user toggle; refresh + rotation + resize switch instantly (no shrink animation). Explicit toggles still animate smoothly. Bug B — chart crosshair jumps to the far left when the sidebar expands/ collapses: - Replace the orientation-only matchMedia re-park with a width-delta ResizeObserver on the chart wrapper that debounce-bumps the AreaChart remount key after the container re-measures, re-parking defaultIndex at the latest point. Subsumes rotation + now also catches sidebar toggle + resize. Flaky tests (finish the shallow-clone / network hardening): - test_ranking_history smoke: explicit pytest.skip on an empty frame (shallow CI clone, fetch-depth=1) — mirrors the #325 + PR #284 guard; proven on a real depth-1 clone (46/46 git-dependent tests pass). - test_osap import scout: check the OpenAP API surface on the class instead of instantiating (the openassetpricing 0.0.2 ctor does a live Google-Drive fetch → flaky on Drive quota); the live path stays in the @network test. Verified: ruff clean; pytest -m "not network" 1407 passed; tsc clean on edited files; next build 506 routes; Playwright empirical Bug A x3 + Bug B all PASS. https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
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.
What
User: the UI looked distorted at larger screens because text/elements didn't scale with the viewport, then asked to verify all platforms and audit that the layout is well-arranged with balanced whitespace (not too sparse, not too cramped).
Fix 1 — fluid root font-size —
a3e496c9globals.css:html { font-size: clamp(1rem, 0.89rem + 0.45vw, 1.25rem) }. The rem-based app scales every text/spacing/gap/chart dimension proportionally — ~16px phone → ~20px desktop — one rule. Mobile unchanged (clamp floor);remterms (not purevw) keep zoom working (WCAG 1.4.4).Fix 2 — micro-labels scale too —
ae132e8c~44 fixed-px
text-[10px]/text-[11px]→ rem equivalents (pixel-identical at base, now scale on desktop). Only Rechartstick fontSize(SVG) + StockLogo px-prop stay px (self-contained).Fix 3 — cross-platform layout-density (dual-audit) —
ac344f50Two parallel read-only audits (
expert-user-explorerempirical render across 10 widths 360→1920 × home + detail × dark/light ·frontend-design-reviewercode review) both PASSED the scaling and converged on whitespace imbalances (side-effects of rem growing 16→20px). Fixed:lg:flex-rowfired when the sidebar left only ~666px content → left block crushed to ~156px. Raised split toxl:(1280, ~1040px → balanced);lg/1024 now stacks cleanly; left col cappedxl:max-w-2xl(no ultrawide spread); dropped the no-oplg:justify-between.max-w-6xl(72rem) expanded to 1440px at the 20px root → sparse on 1920px. Pinned to fixedmax-w-[1152px](viewport-stable; inner rem still scales).md:max-w-[240px]/md:max-w-[64px](content +60px).min-h-[112px]→min-h-[7rem]· search inlineminWidth:200px→min-w-[12.5rem]· home header + detail disclaimermax-w-3xl(prose line-length).Verified
Playwright measure: hero=column@1024 / row@1280-1920 · content=1152px@1920 (was 1440) · sidebar=240@desktop (was 300) · zero horizontal overflow at all 10 widths · mobile (≤414) unchanged · data all correct.
ruff+tsc+next build(506 routes) clean across all 3 commits.Deferred (judgment calls flagged to user)
Scope
Frontend-only (
globals.css+ ~18 components); no schema / compute / scoring / valuation change. CLAUDE.md §Gotcha (fluid root) + AGENTS.md mirror +PHASE_STATUS_INFLIGHT.mdentries.https://claude.ai/code/session_0144kHrCYNaamMPH57b7xdM7
Generated by Claude Code