The one determinate loader in this gallery. Use it only when remaining work can actually be calculated — a file upload, a download, an install — because showing a number you can't back up (a fake progress value) burns trust. The reverse also holds: using an indeterminate loader like a spinner or dots when progress is knowable hides information the user could otherwise see, which feels needlessly opaque.
An SVG <circle> sets its full circumference (2πr) via stroke-dasharray, and stroke-dashoffset is computed as "circumference × (1 − percent)" so only that fraction of the ring appears drawn. Showing the same number as text in the center matters for anyone with low vision or color-vision differences who can't rely on the ring's fill alone.
This shape suits progress for one region — a card, an inline button — rather than a full-screen load; for several files processed in sequence, showing "done so far out of total" beats one ring per file. Update role="progressbar" with aria-valuenow, aria-valuemin="0" and aria-valuemax="100" live, and reflect the same percentage as text inside an aria-live="polite" region. Under prefers-reduced-motion, shorten the fill transition or update the value instantly instead of animating it.