7 Pure CSS1 with JSPublished 2026-10-04Free · no sign-up
Eight ways to show progress, from a styled native progress element to a circular ring, segmented steps, an indeterminate loading line, a fundraising goal, a stacked storage meter and a bar driven by JavaScript.
A simple determinate progress bar built on the native progress element, so screen readers announce the percentage. The bar is restyled for WebKit and Firefox with their own pseudo-elements, and the value is also printed as text above it.
A striped bar whose diagonal stripes move to show that work is happening. The stripes are a repeating linear gradient and the motion animates background-position, which is cheap to render; the movement stops when reduced motion is requested.
Progress shown as a row of segments, one per step, which suits checkouts and onboarding. Completed segments are filled, the current one is half filled, and the text Step 3 of 5 gives the same information without relying on the bars.
A circular progress ring drawn with two SVG circles. The coloured arc length comes from stroke-dasharray and stroke-dashoffset, set from a custom property so one number controls both the ring and the label in the middle.
A thin bar for when you do not know how long something will take, like the loading line at the top of many apps. A coloured segment slides across repeatedly, the progress element has no value so it is announced as busy, and reduced motion shows a static bar.
View code
<div class="ind">
<p id="ind-l">Checking your store connection</p>
<progress aria-labelledby="ind-l"></progress>
</div>
A goal tracker for fundraising or sales targets, with a gradient fill, the amount raised, the target and a marker for the halfway point. The numbers are written out in text, so the bar itself is decorative and hidden from screen readers.
View code
<div class="goal">
<p><b>$7,450</b> raised of $10,000 goal</p>
<div class="track" aria-hidden="true"><span style="width:74.5%"></span><i></i></div>
<small>214 supporters, 9 days left</small>
</div>
One bar split into coloured parts, like a storage meter showing photos, video and documents. Each part's width is its share, and a legend below repeats every value in text so the breakdown is readable without telling the colours apart.
A bar that fills in steps while a simulated upload runs, then turns green with a done message. The progress element's value is updated from script, and a polite live region announces only the start and the finish rather than every percent.
<progress max="100" value="64"> is announced by screen readers with its percentage and needs no ARIA. Without a value it becomes an indeterminate busy indicator. Style it with ::-webkit-progress-bar, ::-webkit-progress-value and ::-moz-progress-bar.
When to use role=progressbar
Custom shapes such as rings or striped bars cannot be a progress element, so give the wrapper role="progressbar" with aria-valuemin, aria-valuemax, aria-valuenow and a label, and keep those values in sync with what is drawn.
Show the number in text
A bar alone is hard to read precisely. Print the value next to it, such as 64% or Step 3 of 5, and for stacked bars add a legend with each value so the meaning does not depend on telling colours apart.
Announce sparingly
When progress updates live, announce only the start and the finish in a polite live region. Reading every percentage aloud would drown out everything else for screen reader users.
Frequently asked questions
Which demos need JavaScript?
Only the live upload demo. The others are HTML and CSS.
How do I draw a circular progress ring?
Use two SVG circles and set stroke-dasharray and stroke-dashoffset from the value, as in the ring demo.
Do animated stripes affect performance?
Animating background-position is cheap, and the animation stops when reduced motion is requested.