v138 · css
Progress Steps
Two step-progress patterns — a horizontal step tracker and a vertical job pipeline — where both the visible step number and the screen-reader announcement are driven by counter(). Visual: "3". Spoken: "Step 3 — current step". Zero JS, zero hard-coded numbers.
- Applied
- Screened
- Interview
- Offer
- Hired
-
PassedApplication receivedResume and cover letter reviewed by recruiting team.
-
PassedPhone screen30-minute call with a recruiter to confirm background and expectations.
-
ScheduledTechnical interview3-hour session with engineers. Live coding and architecture discussion.
-
UpcomingHiring committee reviewPanel decision based on all interview feedback.
-
UpcomingOffer extendedCompensation package sent; candidate has 5 business days to respond.
Pre-138 vs Chrome 138+
counter() was silently ignored inside the alt-text half of the content property. Authors had to duplicate the count manually in ARIA labels (aria-label="Step 3 of 5") or use JavaScript to keep visual counter and ARIA in sync. If steps were added or removed, the JS had to be updated too.
The alt-text half of content: visual / alt now evaluates counter() and counters() at render time — the same counter value used for the visual label is reused verbatim in the accessible name. Add or remove steps; both the number and the announcement update automatically.
/* counter-reset on the list */
ol.steps {
counter-reset: step;
}
/* Each li increments and uses counter() in BOTH halves */
li.step::before {
counter-increment: step;
/* "3" visually, "Step 3 — current step" for screen readers */
content: counter(step) / "Step " counter(step) " — current step";
/* Chrome 138+: counter() is evaluated in the alt-text slot */
}
/* Different states get different alt-text phrasing */
li.step.done::before {
content: counter(step) / "Step " counter(step) " — done";
}
li.step.pending::before {
content: counter(step) / "Step " counter(step) " — not yet started";
}