Skip to content

feat(frontend): hero attribute tiles — 4-box category grid (lucide-react)#344

Merged
dackclup merged 5 commits into
mainfrom
claude/sharp-turing-q3A5J
Jun 1, 2026
Merged

feat(frontend): hero attribute tiles — 4-box category grid (lucide-react)#344
dackclup merged 5 commits into
mainfrom
claude/sharp-turing-q3A5J

Conversation

@dackclup
Copy link
Copy Markdown
Owner

What

The user asked for "กรอบสี่เหลี่ยมสี่อันในภาพ" — a reference stock app's 4 category tiles (icon-over-label boxes). A /grill-me session locked the spec across 6 decisions.

Renders a 4-box grid in its own section under the hero (above the price chart):

Tile Source State
Size market_cap → Mega/Large/Mid/Small data
Sector detail.sector data
Dividend — (no dividend data in schema) reserved placeholder "Coming soon"
More reserved placeholder "Coming soon"

Key decisions (from grill-me)

  1. Match the structure, reskin the theme — icon-top / label-bottom / grid grid-cols-2 sm:grid-cols-4 like the reference app, but light slate / dark slate surfaces, NOT black boxes (those break in light mode). LedgerCraft rounded ≤4px, border-as-depth, paired dark:.
  2. 2 data + 2 reserved — QuantRank has no dividend data, so the reference app's "จ่ายปันผล" tile can't be filled.
  3. Empty tiles = intentional "reserved" (dashed border + "Coming soon" sub-line), NOT a bare "—" — so they read as reserved, not broken.
  4. Icons via lucide-react (the project's first icon library).
  5. Supersedes + closes PR feat(frontend): hero attribute chips (cap tier / margin / valuation tone) #343 (the earlier inline-chip attempt — the user wanted the box grid, not a compact chip row).
  6. Info tiles, not filters (single-stock detail page).

New dependency — lucide-react@^1.17.0

Both dependency-auditor + security-reviewer SAFE: ISC license (MIT-tier), React-18.3 peer, 0 transitive deps, 0 install-scripts, SLSA-attested, 0 CVE. Tree-shakes to ~1.5-2 KB gzipped via NAMED imports only (never the import * as Icons barrel = 224 KB). Stock-detail route went 113 → 115 KB First Load JS as predicted. Not added to the dependabot ignore-list (normal flow); not added to THIRD_PARTY_NOTICES.md (that file is vendored-source/skills only — runtime npm deps like next/react/recharts aren't listed there).

Verification (real data)

  • tsc --noEmit clean · next build 506/506 pages
  • NVDA HTML: Size→Mega cap, Sector→Information Technology, 2 "Coming soon" placeholders, grid-cols-2 + sm:grid-cols-4 present, lucide icons render as inline SVG (tree-shaken)

Scope

Frontend. No schema / Python / scoring / valuation / output-JSON change — 1 new dep + 1 new server component + hero wiring. CLAUDE.md §Gotcha ×2 (lucide import discipline + the tiles) + AGENTS.md inventory + PHASE_STATUS_INFLIGHT.md.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq


Generated by Claude Code

…act)

User asked for "กรอบสี่เหลี่ยมสี่อันในภาพ" — a reference stock app's 4
category tiles (icon-over-label boxes). A grill-me session locked the spec:
match the structure but reskin to the QuantRank theme (light slate / dark
slate, not the reference app's black boxes which break in light mode); 2
tiles carry data (Size = market-cap tier, Sector), 2 are intentional
"Coming soon" placeholders (dashed border + sub-line — QuantRank has no
dividend data, so the reference app's dividend tile can't be filled; the
empty tile reads as reserved, not broken). Info tiles, not filters.

Supersedes + closes PR #343 (the earlier inline-chip attempt — the user
wanted the box grid, not a compact chip row).

New dep lucide-react@^1.17.0 (the project's first icon library). Both
dependency-auditor + security-reviewer SAFE: ISC license, React-18.3 peer,
0 transitive, 0 install-scripts, SLSA-attested, 0 CVE. Tree-shakes to
~1.5-2 KB gzipped via NAMED imports (never the import-* barrel); the
stock-detail route went 113 -> 115 KB First Load JS as predicted. Not added
to the dependabot ignore-list (normal flow); not added to
THIRD_PARTY_NOTICES.md (that file is vendored-source/skills only — runtime
npm deps like next/react/recharts aren't listed there).

New HeroAttributeTiles.tsx pure server component: grid grid-cols-2
sm:grid-cols-4, 4 fixed tiles via a Tile sub-component (null value -> dashed
reserved treatment). Own section under the hero, above the price chart.

tsc clean, next build 506/506, NVDA HTML verified (Size->Mega cap,
Sector->Information Technology, 2 Coming-soon placeholders, responsive grid,
lucide inline SVG). No schema/Python/scoring/valuation change.

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

vercel Bot commented May 31, 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 1:30am

…a11y)

frontend-design-reviewer found no blockers; folds in the SHOULD-FIX + NIT
items:
- S1 contrast: reserved-tile caption text-slate-400 -> text-slate-500
  (~3.4:1, WCAG large/bold floor instead of the too-faint ~2.8:1).
- S2 casing: reserved-tile caption now uses the same UPPERCASE
  tracking-wider vocabulary as a filled tile (de-emphasis via color only),
  so promoting a placeholder later won't flip the type treatment.
- N1 icon: Sector icon Factory -> Layers (Factory connotes manufacturing
  specifically; Layers reads as a neutral "category" for all 11 GICS
  sectors, and stops blurring with Building2 on the Size tile).
- N2 a11y: wrap the grid in <section aria-label="Company attributes"> with
  an sr-only <h2> so the tile block joins the document outline like every
  other detail-page section.

tsc clean, next build 506/506, NVDA HTML verified (section + sr-only h2,
Layers icon, uppercase reserved caption, Mega cap / Information Technology
data tiles intact). No behavior change beyond the review polish.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq
Per user direction: the 4th placeholder tile is reserved for a future
security-type signal (Common stock / ADR / etc. — the analog of the
reference app's "Common · หุ้นสามัญ" tile), not a generic "More". Caption
More -> Type, icon Gauge -> ScrollText (reads as a security certificate /
type). Still a "Coming soon" reserved tile — QuantRank has no security-type
field in the schema yet; when one lands, swapping the tile's value from null
to the real field auto-promotes it out of the reserved state.

tsc clean, next build 506/506, NVDA HTML: Type + Dividend reserved tiles
present, More gone. CLAUDE.md gotcha updated to match.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq
…#7

The two reserved HeroAttributeTiles slots (Dividend + Type, PR #344) now have
a planned data path on the roadmap so they're a tracked deliverable, not an
open-ended "Coming soon":

- 7a Dividend — dividend_yield_pct / pays_dividend from yfinance (already in
  the stack via compute/ingest/prices.py, no new dep) + schema triple +
  Metadata.dividend_coverage_pct observability-first.
- 7b Security-type — Common / ADR / REIT label from yfinance quoteType / SEC
  filer flags + schema triple, same observability discipline.

Both are display-only (fill the reserved tiles; no ranking/scoring/veto
impact). Each sequenced behind a *_coverage_pct diagnostic cron before the
tile reads live data (the Phase 4h -> 4h.2 observability-before-wiring
precedent). Tiles auto-promote out of the reserved state when the field
lands. Documented in PHASE_STATUS.md §Next deliverables #7 + CLAUDE.md +
AGENTS.md inventory note + PHASE_STATUS_INFLIGHT.md.

Doc-only — no compute/schema/scoring/frontend code change.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq
…item

docs-reviewer verified the roadmap #7 field names against the yfinance
actually installed and found three wrong:
- .info["quoteType"] is retired into fast_info on current yfinance ->
  use fast_info.quote_type (kept as a "NOT this" warning in the text).
- .info["legalType"] is funds-only (None for all S&P 500 equities) -> dropped.
- IsForeignPrivateIssuer is not a real SEC field -> use
  dei:DocumentType == "20-F" or the EDGAR submissions entityType field
  (already fetched by compute/ingest/sec_health.py) for ADR/FPI detection.
- the dividend .info ingest anchor was mis-attributed to prices.py
  (yf.download OHLCV-only) -> corrected to cross_source.py, which already
  uses yf.Ticker().info + caches under YFINANCE_INFO_CACHE_DIR.

Roadmap framing (display-only, observability-before-wiring, no scoring
impact, schema-triple lockstep) was confirmed accurate; only the API
field names + ingest-file attribution were fixed across PHASE_STATUS.md +
CLAUDE.md + PHASE_STATUS_INFLIGHT.md. Doc-only.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq
@dackclup dackclup marked this pull request as ready for review June 1, 2026 01:59
@dackclup dackclup merged commit b773bbf into main Jun 1, 2026
4 checks passed
@dackclup dackclup deleted the claude/sharp-turing-q3A5J branch June 1, 2026 02:00
dackclup added a commit that referenced this pull request Jun 1, 2026
…ccable document) (#354)

Full regenerate of the impeccable design-context (created in #350) against the
current code. The creative decisions locked in #350 (North Star, palette,
typography, Named Rules, the 4 anti-references) are preserved verbatim; only the
spec's accuracy had drifted, fixed here:

- Elevation: DESIGN.md/sidecar claimed cards rest on shadow-medium and the hero
  on shadow-large. Reality (post-A3 reskin): only shadow-overlay is used anywhere
  (FilterDrawer) -- every card/table/hero is border-only `rounded`. Rewrote the
  Elevation section + sidecar shadow purposes so Subtle/Medium/Large read as
  defined-but-unused; an agent will no longer reach for a resting shadow.
- Components: added ListingChips (#351 country/exchange chips) +
  HeroAttributeTiles (#344 4-box tile grid) to DESIGN.md and the sidecar, plus an
  Icons subsection (lucide named-import / country-flag-icons per-country
  tree-shake discipline) and the signature Score Gauge as the 8th sidecar entry.
- Motion: corrected two sidecar easings -- gauge-sweep cubic-bezier(.22,1,.36,1)
  -> ease-in-out, hover-lift ease-out -> ease-in-out (the app-wide single curve,
  #330).
- Sidecar Card css dropped its stale box-shadow (border-only); Recommendation
  Chip now uses the soft-OKLCH values that actually render after the globals.css
  override (expert-user-explorer-measured) instead of raw emerald hex. Added a
  Colors + Do's note on the soft-override allowlist (the bg-rose-600 gap from
  #352) + the gauge-stroke inline-rgb carve-out to the Tailwind-Class Rule.

Doc-only (root DESIGN.md + .impeccable/design.json + PHASE_STATUS_INFLIGHT.md) --
no compute / schema / scoring / valuation / frontend-code change. JSON validated
(schemaVersion 2, 8 components); the 6 Stitch headers present in order; YAML
frontmatter parses.

https://claude.ai/code/session_012xxKfyR939bZDmbxxqMFZi

Co-authored-by: Claude <noreply@anthropic.com>
dackclup added a commit that referenced this pull request Jun 1, 2026
… MoS labels · warning hierarchy) (#355)

* fix(frontend): a11y + clarity punch-down from $impeccable audit+critique

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

* fix(frontend): review-gate follow-up — back-link + period-selector 44px 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

---------

Co-authored-by: Claude <noreply@anthropic.com>
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