feat(home): wire Real-world progress stats section with synced counters [INTORG-670]#301
Merged
Conversation
…rs [INTORG-670] Add the fifth home-page section rendering the four stats from Figma (21M+ in grants, 300+ projects, 130+ countries, 1000+ students). Mobile- first stacked layout, 2x2 grid from tablet up, vertically centered at min-h-screen on tablet & desktop per Radu's layout rule. AnimatedNumber: - SSR renders the final value so no-JS users, screen readers, and search bots see the real number; JS swaps to 0 and animates only when motion is allowed - 1200ms linear, fixed default duration across all instances — matches the Framer prototype (verified by frame-stepping counters.mov at 60fps: the "300" counter advances ~25/100ms = 250/sec, extrapolating to 1.2s total; all four counters share the same progress curve) - tabular-nums kills width jitter as digits change - Group-trigger: counters that share a [data-animated-number-trigger] ancestor all fire together when any one enters the viewport. Lets the stacked mobile cards animate in lockstep instead of one-card-at-a-time, matching the prototype's "wave" feel (verified in counters-mobile.mov) - a11y: animating digits are aria-hidden so SR doesn't announce every frame; the parent <li> carries an aria-label with the human-readable full value (e.g. "21 million dollars in grants") StatsSection: - Heading and body as slots (mirroring TextMediaSection); stats as a typed StatItem[] prop - bg-blush-100 background, Poppins typography pinned to design tokens (text-h1/h2/h3/h5 + their -md/-lg responsive variants)
✅ Deploy Preview for interledger-org-v5 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
…ing [INTORG-672]
INTORG-672 ("[Astro] Create individual stat component") asks for a single-
stat component as its own file, separate from the section container. Move
the per-card markup, the StatItem type, and the AnimatedNumber wiring into
StatCard.astro and have StatsSection map over `stats` rendering
`<StatCard {...stat} />`. StatsSection re-exports StatItem so consumers
(HomePage) keep their existing import path unchanged.
Also drop `whitespace-nowrap` from the description <p> and let it wrap
inside the card's fixed width — required by INTORG-671's i18n note that
the component must "render correctly when locale strings are significantly
longer or shorter than the default". The English labels fit on one line
at 227/251/290px, so the visual matches Figma; longer translations now
wrap to multiple lines centered (mobile/tablet) or left-aligned (desktop)
instead of overflowing. Suffix keeps nowrap since "M+" / "+" must never
break.
The project is `output: 'static'` (no SSR runtime), so the comments describing how the final value gets into the markup were inaccurate. The behavior is unchanged — Astro renders the frontmatter at build time and the final value is baked into the .html file shipped to the CDN — but calling that "SSR" is misleading. Switch to "build time" / "static HTML" wording so a future reader doesn't conclude the page needs a server.
…-670] The <ul> had tablet:w-full max-w-[600px] from the tablet 2x2 layout but no desktop reset. On desktop the inner is a flex row, so the ul's inherited w-full forced it to claim the entire row, squeezing the text column down to its min-content (~252px). With desktop:flex-1 still on the text column, the heading "Real-world progress" wrapped to three lines and the 120px gap between text and stats read as enormous next to the collapsed text — Sarah's "spacing is not right between the text and stats" comment on PR #301. Add desktop:w-auto so the ul shrinks to the grid's intrinsic 2×290 + 12px gap = 592px on desktop. Text's flex-1 then fills the remaining 1280 − 120 − 592 = 568px at a 1440 viewport, where the heading fits on one line at 56/64 SemiBold and the body wraps to two lines, matching Figma node 466:58427.
# Conflicts: # src/components/pages/HomePage.astro # src/data/ui.ts
6 tasks
…s on desktop Both home-page TextMediaSection CTAs shipped with `w-full tablet:w-auto tablet:mx-auto`. Tailwind's mobile-first cascade means `tablet:mx-auto` keeps applying at the desktop breakpoint, so the auto margins center the button on desktop too — but the reviewer on PR #290 asked for it to be centered on tablet only and left-aligned on desktop. Same regression on Independent by Design from the PR #296 merge. Add `desktop:mx-0` to both buttons. Final cascade: - mobile: `w-full` → full-width button - tablet: `w-auto` + `mx-auto` → auto-width, horizontally centered - desktop: `w-auto` + `mx-0` → auto-width, left-aligned (picked up by TextMediaSection's text column `desktop:items-start`)
Collaborator
JonathanMatthey
approved these changes
May 21, 2026
Collaborator
JonathanMatthey
left a comment
There was a problem hiding this comment.
LGTM -
couple of feedback notes
Co-authored-by: Jonathan Matthey <mattheyj@gmail.com>
Figma shows the stat numbers without a comma (1000, not 1,000).
AnimatedNumber was using `toLocaleString('en-US')` at build time and
`Intl.NumberFormat('en-US')` during the tick animation, both of which
insert a comma at the thousands break. Switched both to `String(...)`.
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.


Summary
Adds the fifth home-page section: Real-world progress — four stats (21M+ grants, 300+ projects, 130+ countries, 1000+ students skilled) on a
blush-100background, with counters that animate from 0 to their target the first time the section enters the viewport.Layout (per Figma desktop 466:58427, tablet 475:60244, mobile 372:49380):
min-h-screenvertical centermin-h-screenvertical centerCounter (
AnimatedNumber.astro) — vanilla Astro + JS, no React/framer dependency:output: 'static'), so no-JS users, screen readers, and search bots see the real number with no JS required; the client script then swaps it to0and animates up[data-animated-number-trigger]ancestor — counters in the same group all fire together when any one of them crosses the viewport threshold, so the four stacked mobile cards animate in lockstep instead of one-at-a-time (matchescounters-mobile.mov)tabular-numslocks digit column width so the row doesn't jitter horizontally as digits changetextContentonly updated when the displayed integer actually changes<span>isaria-hiddenso screen readers don't get spammed every frame; parent<li>carries the human-readablearia-label(e.g. "21 million dollars in grants")Related Issue
This PR closes all three of INTORG-670's sub-issues, INTORG-671, INTORG-672, INTORG-673
Manual Test
min-h-screenwith a 2×2 stat grid.Checks
pnpm run formatpnpm run lintPR Checklist
docs/plans/counters.mov+counters-mobile.mov)