12

Progress ring

A zero-JS conic instrument: a registered @property transitions the sweep, @starting-style provides the one-shot mount draw, and a radial mask cuts the donut so it composes over any background. Soft ease-out only — a reading that overshoots is a false reading.

@property --ring-p@starting-styleconic-gradientmask

The centered figure SNAPS to the value; only the ring travels. And the ring may approach its reading, never pass it — springs are for chrome, and this is not chrome.

Mount sweep + live value

both are the same CSS transition — no JS animates anything

72%
capacity
38%
91%

Composes over anything

the mask cuts a true hole — no faked inner circle

The whole engine

motion.css §8 — there is no JS to show

@property --ring-p { syntax: "<number>"; inherits: false; initial-value: 0; }

.progress-ring::before {
  --ring-p: var(--ring-value, 0);                 /* host sets 0–100 inline  */
  background: conic-gradient(
    var(--ring-color, var(--accent)) calc(var(--ring-p) * 1%),
    var(--ring-track, var(--line-strong)) 0);
  mask: radial-gradient(closest-side,
    transparent calc(100% - var(--ring-w) - 1px), #000 calc(100% - var(--ring-w)));
  transition: --ring-p 900ms var(--ease-out-soft); /* NEVER a spring          */
  @starting-style { --ring-p: 0; }                 /* the mount sweep         */
}