
SaaS Landing Page Key Stats Counter
Published
25 hand-coded CSS number counter animations for SaaS landing-page stat grids, dashboard KPI cards, e-commerce sales tickers, fintech revenue dashboards, healthcare vitals displays, real-estate price meters, gamified level-up widgets, and mission-critical telemetry panels. Each demo is a complete composition — the count-up paired with its chrome: progress bars, SVG ring charts, ticker tapes, odometer flip digits, sparklines, status dots. Ten demos ship Intl.NumberFormat for currency, compact-notation K/M/B, and locale-aware digits; every JS-driven demo gates on IntersectionObserver so counters only fire when scrolled into view. Portable to React (useEffect), Vue (onMounted), Svelte (onMount), and Tailwind v4 @theme keyframes without changing the HTML or CSS. Scoped under .cnc-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related22 CSS Progress Bars18 CSS Scroll Progress Bar22 CSS Countdown Timers30 CSS Keyframe Animations

Published

animation-timeline: view() path where the number is literally scrubbed by the scroll position, and an IntersectionObserver fallback that fires the same animation once per card in every other browser. Scroll the panel to watch them run.Published

Published

Published

Published

Published

Published

Published

Intl.NumberFormat, so the same code renders $1,284,930 in en-US, 1.284.930 $ in de-DE, ₹12,84,930 in the Indian lakh grouping, and a compact $1.28M — correct separators, correct currency placement, correct decimals — while the digits still animate smoothly. Switch locale, currency and notation live.Published

stroke-dashoffset and the printed percentage from the same animation, so the arc and the number can never disagree. A conic-gradient variant and a segmented-track variant are included for comparison.Published

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

animation-timeline: scroll() ties both the bar and a counting percentage directly to the scroll position of the article, so the number is scrubbed by the reader's own gesture. Minutes remaining tick down the same way.PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated
PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated

PublishedUpdated
@property combined with counter() can build a pure-CSS counter — Demo 08 (Pure CSS Odometer) demonstrates this — but the rendered number tends to be a single typographic style with no easy way to format thousands separators, currency symbols, or locale-aware digits. Every JS demo here is ~15 lines, zero dependencies, and drops into React (useEffect), Vue (onMounted), Svelte (onMount), Astro (island), or vanilla with no changes.rootMargin: '0px 0px -10% 0px', point it at the counter's container, and when the entry intersects, start the requestAnimationFrame loop and disconnect the observer so it does not re-fire when the user scrolls back. This is the right pattern for any counter below the fold — without it, the animation runs while the section is still offscreen and the user arrives at a static final number that has already finished animating. If prefers-reduced-motion is set, skip the observer entirely and set the final value on mount so screen-reader users and motion-sensitive users see the correct number immediately. The observer disconnect is critical: leaked observers accumulate across page navigations and become a memory-leak class Google demotes on repeat interactions.Intl.NumberFormat inside the RAF loop instead of raw String(value). Demo 09 (Currency Formatter Counter with Thousands Separators & K/M/B Suffixes) demonstrates the two most common shapes: new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(value) for $1,247,891 output, and a compact-notation form new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(value) for 1.2M / 890K / 4.5B output. Both are locale-aware, so shipping a Japanese, German, or Indian variant means changing the first argument only — no manual comma / thousand-separator handling. The Intl API is fully supported in every current browser (baseline 2019 for currency, 2021 for compact notation). Pair with a tabular-numeric font feature (font-variant-numeric: tabular-nums) on the container so the width doesn't jitter as digits change.prefers-reduced-motion — when the user has requested less motion at the OS level, the count-up resolves to its final value instantly and the ambient breathing / rotation / pulse animations suppress, so the page is still complete and readable without any transient state. Numbers are rendered as real text in the DOM (not as background images or SVG paths), so screen readers announce the final value correctly. For counters that represent live data — Demo 03 (e-commerce stock), Demo 07 (live visitor widget), Demo 18 (stock ticker) — add aria-live='polite' on the parent so screen readers announce updates without interrupting the user's current focus. Every counter also pairs its number with a visible text label positioned adjacently so the screen-reader announcement reads as "Monthly Recurring Revenue, forty-two thousand dollars" rather than a naked "42000". Focus rings honor :focus-visible, all interactive controls have real <button> semantics, and keyboard-only users can trigger every counter that has a manual replay control.@theme keyframe registration pattern for classes-only styling. For React, wrap the RAF loop in a useEffect(() => { … }, []) that runs once on mount and writes to a ref attached to the number span — the framework's render cycle never re-runs on every animation frame (which would tank INP). For Vue, use onMounted + ref; for Svelte, onMount + bind:this; for Angular, @ViewChild + ngAfterViewInit. In all four the HTML and CSS port unchanged — only the lifecycle hook wrapping the count-up loop changes. Strip the IIFE wrapper from the demo JS, expose the init function, and call it from your framework's mount hook. Compact-notation, currency formatting, IntersectionObserver gating, and reduced-motion checks all remain the same code.requestAnimationFrame yields between frames, so a count-up loop cannot block the main thread for layout, paint, or input responsiveness. Each demo animates a single number (or a small handful in dashboard grids) — well under any performance budget. Two things to watch: writing to textContent triggers a re-layout if the number's character width changes across frames, so wrap the counter in a fixed-width container OR use font-variant-numeric: tabular-nums so every digit occupies the same width; and never count up a number below the fold without an IntersectionObserver gate, because you're spending main-thread frames on something the user cannot see (and Lighthouse penalizes off-screen animation on the Total Blocking Time metric). None of the 25 demos here measurably affect LCP, CLS, or INP in testing on a mid-tier 2024 Android device.20 hand-coded CSS animated buttons — neon glow, ripple, 3D press, liquid fill, jelly bounce, shine sweep, animated border, moving gradient CTA, text flip, submit success state, add-to-cart progress, download icon, hamburger-to-close, toggle switch, loading spinner inside button, next/prev arrow nav, and ghost button background reveal. Half pure CSS, half lightweight JS for production interactions.
11 pure CSS animation-timeline demos built on the W3C scroll() and view() spec — reading progress bar, view-timeline fade & reveal, shrinking sticky header, stacking cards, horizontal scroll section, parallax hero, container shadow indicators, reverse-scroll columns, text scrim reveal, cover card to sticky header, and image zoom / clip-path wipe. Zero JS, off the main thread, Core Web Vitals safe. Copy-paste ready.
25 hand-coded CSS background animations for SaaS hero sections, developer tool documentation, agency portfolios, gaming and Web3 landing pages, seasonal e-commerce campaigns, and dark-mode dashboards. Covers full-screen gradient shifts, animated mesh gradients, aurora borealis glow, cursor spotlight follow, interactive water ripples, dot grid and diagonal stripe patterns, SVG grain and noise texture overlays, topographic contour lines, layered SVG waves, plasma and lava lamp fluids, bokeh light blur, particle constellation networks, matrix digital rain, synthwave 3D perspective grids, starfield warp speed, CRT scanline overlays, fog and mist drift, morphing organic blobs, floating glassmorphism orbs, halftone dot patterns, starry night skies, floating geometric shapes, rising bubbles, and falling snow. 20 are pure CSS with zero JavaScript; the 5 that need it use vanilla canvas or pointer tracking with no dependencies. Every background animates compositor-only properties so it never blocks the main thread, is scoped under a .bga-NN prefix for no-collision pasting, guards prefers-reduced-motion, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.