Skip to content

Stats with Circular Progress Rings

Dark stats section with four SVG circular progress rings that fill and count up to their percentage when scrolled into view.

Live preview

Open in new tab

Code

A dark stats section that shows four percentage metrics as circular progress rings, built with inline SVG, pure CSS and a short vanilla JavaScript file. Each ring fills up and its number counts from zero when the section scrolls into view. It fits impact reports, About pages, SaaS landing pages and annual reviews.

How it works

The root section.sr-stats contains an .sr-intro (eyebrow, heading, lead text) and a ul.sr-grid of li.sr-item cards. Each card holds an .sr-ring with an inline SVG: a grey circle.sr-track and a colored circle.sr-bar on top. Both circles use pathLength="100", so the arc length is simply the percentage: stroke-dasharray="92 100" on .sr-bar draws 92 percent of the ring. transform="rotate(-90 60 60)" starts the arc at twelve o'clock, and stroke-linecap: round plus a drop-shadow filter give the glowing rounded ends. The number sits in .sr-value, absolutely centered over the ring, with the % sign in .sr-unit.

The script empties every ring on DOMContentLoaded by setting stroke-dashoffset to the value and the .sr-num text to 0. An IntersectionObserver waits until a quarter of the section is visible, then animates each card with an ease-out curve, offset by a 120 ms stagger. Timers are used instead of requestAnimationFrame so the final state is also reached in background tabs and screenshot tools. Without JavaScript the rings and numbers are rendered complete.

Customization

  • stroke-dasharray on .sr-bar and data-value on .sr-num: set both to the same number to change a stat; keep the text inside .sr-num equal too for the no-JS fallback.
  • .sr-item--cyan, .sr-item--violet, .sr-item--amber and .sr-item--pink set --sr-color, the ring, glow and % color. Add a modifier for your own brand color.
  • --sr-size on .sr-stats: diameter of the rings (6.75rem on phones, 8rem from 640px, 9rem from 960px); the number scales with it.
  • --sr-stroke: thickness of the track and progress ring.
  • --sr-duration: length of the fill and count animation, read by the script, e.g. 2.4s for a slower build-up.
  • --sr-track and --sr-bg: color of the empty ring and the section background.
  • grid-template-columns in .sr-grid: two columns on small screens and four from 960px.

Accessibility

The SVG rings are decorative (aria-hidden="true", focusable="false"); the value is real text in .sr-value followed by an h3.sr-label and a description, so screen readers announce "92%" with its meaning. Text colors keep strong contrast on the dark background. If prefers-reduced-motion: reduce is set, the script does nothing and the final values are shown immediately.

Usage notes

The section works several times on one page; each .sr-stats gets its own observer and animates only once.

Requirements

No dependencies. Pure HTML, CSS and JavaScript.

Stats with Circular Progress Rings – Pure CSS stats sections component screenshot
Stats with Circular Progress Rings · Pure CSS Stats Sections

More Stats Sections

Animated Number Counters – Pure CSS stats sections preview

Animated Number Counters

Stats section with four number cards that count up when scrolled into view, using IntersectionObserver, locale formatting and reduced-motion support.

Stats Section with Count-Up Numbers – Tailwind CSS stats sections preview

Stats Section with Count-Up Numbers

Tailwind CSS key-metrics section on an emerald brand background with large numbers, labels, trend notes and an accessible count-up on scroll.