Stat Cards
Three number cards that grow one after the other when the block reaches the screen, while each value counts up to its target.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Data has 28 effects, including 1 free. Explore the category →
3 usage examples



How it works
Each .stcd-card holds a .stcd-value and a .stcd-label. The target lives in data-target (decimals with a dot: 2.4), the unit in data-prefix or data-suffix ("K", "%", " €"). The text written in the HTML stands as the final value until the script runs.
On start, the script hides the cards and resets the values to 0. When the block is half visible (IntersectionObserver, only once), the cards grow from 80% to 100% with a slight bounce (0.6 s, 0.1 s apart) and each value counts from 0 to its target over 2 s, slowing down at the end. The format follows the page language: "2.4K" and "89%" in English with the English HTML provided, "2,4 k" and "89 %" in French.
Changing data-target (or the unit) later counts again, from the displayed value to the new one: handy for a dashboard that updates or a popover that changes subject. Each .fx-0229-root lives on its own, nothing is declared on window.
Accessibility
- Screen readers: the moving value is hidden (
aria-hidden) and the script places a hidden text right after it with the final value and its unit. Users hear "2.4K Sales, 89% Rate, 156 Clients". After a change ofdata-target, that text is updated at once. - Reduced motion: under
prefers-reduced-motion: reduce, the cards are visible without growing and the final values are set straight away; a newdata-targetalso shows without counting. - Without script: the cards stay visible with the HTML text; only the script hides them before they appear.
- Labels: the unit sits with the value and the word in the label ("89%" then "Rate"), which reads in the right order.
Browser compatibility
Dependency-free JavaScript (about 3 KB), CSS animation and CSS variables. Measured on 08/11/2026 in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): cards and count on appearance, a block further down waiting for the scroll, a new data-target on the 2nd copy without touching the 1st, 390 px, reduced motion, French and English formats, no error. Versions taken from @mdn/browser-compat-data: IntersectionObserver sets the minimum.
Without IntersectionObserver, the cards and final values show at once. Without JavaScript, the cards display the text written in the HTML.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0229-root">
<div class="stcd-cards">
<div class="stcd-card">
<div class="stcd-value" data-target="2.4" data-suffix="K">2.4K</div>
<div class="stcd-label">Sales</div>
</div>
<div class="stcd-card">…</div>
<div class="stcd-card">…</div>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Value (HTML — data-target on .stcd-value) |
2.4 · 89 · 156 | Count target, decimals written with a dot. Changing it later in JavaScript counts again from the displayed value. |
Unit (HTML — data-prefix and data-suffix) |
"K" and "%" as suffixes on the first two | Text glued before or after the number: data-prefix="$", data-suffix=" €". Put the space inside the attribute if your language wants one. |
Duration (HTML — data-duration on .stcd-value) |
2000 (milliseconds) | Length of the count, set value by value. |
| Number of cards (HTML) | 3 | Add or remove .stcd-card elements; they wrap below the --stcd-max width. The 0.1 s offset is set for the 2nd and 3rd cards. |
Colours (CSS — --stcd-from, --stcd-to, --stcd-card, --stcd-border, --stcd-muted) |
#6366f1 → #d946ef · indigo at 10% · indigo at 30% · white at 50% | Value gradient, card background and border, label colour. |
Block width (CSS — --stcd-max) |
360px | Maximum width of the row of cards; none to fill the whole column. |
Background and height (CSS — --stcd-bg, --stcd-height) |
#0a0a0f · 300px | Inside a page: --stcd-bg:transparent; --stcd-height:0px. |
FAQ
card.querySelector('.stcd-value').setAttribute('data-target', '3.1'). The script notices it and counts from the old one. The text read by screen readers changes at the same time.data-target changes.lang); the unit comes from your data-suffix. The French HTML provided uses data-suffix=" k" and " %" with a space, as French usage wants.