A ring that fills clockwise from the top to show how far something has got, with the value in the middle.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
sizesmbk-progress-ring--size-<value> | mdsm | md | Medium shows the value in the middle; small is a thinner ring with no room for it, for a row or a list. |
completesmbk-progress-ring--complete | On or off | Off | The value has reached the end: the whole ring turns to the success color. The wrapper sets it; plain markup sets it the same way. |
Examples
Usage
The ring with its value in the middle, named by the words beside it.
Values
It fills clockwise from the top; at the end the whole ring turns green.
Sizes
Medium shows the value; small is thinner, with no room for it.
In a list
Small rings beside the steps of a checklist.
Guidelines
Purpose
Shows how far something has got as a ring that fills, with the percentage in the middle: a profile completed, a course taken, the steps of a setup done. It takes less room than a progress bar and sits well beside a name or at the start of a list row.
When to use
- For progress with a known end, beside the name of what is measured.
- In a list or a checklist, a small ring per row.
When not to use
- For work whose end is not known. Use a spinner or a progress bar with no value.
- For how full something is, such as storage. Use a meter.
- For a value over time or a share of several parts. Use a chart.
Usage rules
Do
- Say what the ring measures beside it.
- Use the small ring in rows, the medium one where it stands on its own.
Don't
- Use a ring for work whose end is not known.
- Put a ring with no words near it where the reader cannot tell what it measures.
Say what the ring measures beside it.
A ring on its own leaves the reader guessing what is 60% done.
Anatomy
- The block, a square as wide as the ring.
- The track part, the ring itself: the empty ring in the subtle track color, and the done part laid over it from the top, clockwise, in the chart color, with round ends.
- The value part, the percentage in the middle in a label style, at the medium size only.
Behavior
The value is kept inside the range. The done part grows clockwise from the top; at the end the whole ring turns to the success color. When the value changes, the done part grows or shrinks to it, and does not move under reduced motion. Nothing moves as the page loads.
The small ring is thinner and has no value in the middle: name it with words beside it.
Accessibility
What it does for you
- The ring is a progress bar to a screen reader, with its value, and is said as progress.
- The percentage in the middle is hidden from screen readers, which hear the value from the ring itself.
- It takes no focus and no keys.
- In high contrast mode the done part is drawn in the highlight color over a ring in the text color.
What you must do
- Name it: an
aria-label, oraria-labelledbypointing at the words beside it. - When a percentage is not what it measures, give the value in words with
aria-valuetext, such as "2 of 4 steps". - Name the small ring with words beside it, since it has no value in the middle.
How to test
- With a screen reader, it is read as a progress bar by its name, with its value, or with the words of
aria-valuetextwhen given. The percentage in the middle is not read a second time. - Change the value: the done part grows or shrinks to it, and with reduced motion it does not move.
- With text at 200% and the page 320 pixels wide, the ring and the words beside it stay whole.
- In a forced-color mode, the done part is drawn in the highlight color over a ring in the text color.
Source
The files the progress ring is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-progress-ring — Progress ring. Figma "Progress ring" 7580:184 (Value × Size).
* A ring in the subtle track color with the done part laid over it from the top, clockwise, in the
* chart series color, its two ends round; at the end the whole ring turns to the success color. The
* value in the middle in Label MD at the medium size; the small ring has no room for it.
* The ring is one box: a conic gradient paints the done part over the track and a radial mask cuts
* the hole, so its size and thickness come from tokens alone. The round ends are the box's two
* pseudo-elements, dots as wide as the ring: one at the top, one turned round the center to where the
* done part ends. The wrapper sets --smbk-progress-ring-value inline, from 0 to 100; that is data,
* not a design value. */
@property --smbk-progress-ring-value {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.smbk-progress-ring {
box-sizing: border-box;
display: inline-grid;
flex-shrink: 0;
place-items: center;
width: var(--smbk-size-progress-ring-md);
height: var(--smbk-size-progress-ring-md);
vertical-align: middle;
}
.smbk-progress-ring__track {
position: relative;
grid-area: 1 / 1;
width: 100%;
height: 100%;
border-radius: var(--smbk-radius-full);
background: conic-gradient(
var(--smbk-color-data-series) calc(var(--smbk-progress-ring-value) * 1%),
var(--smbk-color-surface-track-subtle) 0
);
mask-image: radial-gradient(
farthest-side,
transparent calc(100% - var(--smbk-size-6)),
currentcolor calc(100% - var(--smbk-size-6))
);
}
.smbk-progress-ring__track::before,
.smbk-progress-ring__track::after {
content: "";
position: absolute;
top: 0;
left: calc(50% - var(--smbk-size-6) / 2);
width: var(--smbk-size-6);
height: var(--smbk-size-6);
border-radius: var(--smbk-radius-full);
background: var(--smbk-color-data-series);
/* Nothing is drawn at zero, as Figma draws it. */
opacity: clamp(0, var(--smbk-progress-ring-value), 1);
}
.smbk-progress-ring__track::after {
transform-origin: 50% calc(var(--smbk-size-progress-ring-md) / 2);
transform: rotate(calc(var(--smbk-progress-ring-value) * 3.6deg));
}
.smbk-progress-ring__value {
grid-area: 1 / 1;
color: var(--smbk-color-text-default);
font-family: var(--smbk-typography-label-md-font-family);
font-size: var(--smbk-typography-label-md-font-size);
font-weight: var(--smbk-typography-label-md-font-weight);
line-height: var(--smbk-typography-label-md-line-height);
letter-spacing: var(--smbk-typography-label-md-letter-spacing);
text-transform: var(--smbk-typography-label-md-text-transform);
font-variant-numeric: tabular-nums;
}
/* ---- complete: the whole ring in the success color ---- */
.smbk-progress-ring--complete .smbk-progress-ring__track {
background: var(--smbk-color-feedback-success-icon);
}
.smbk-progress-ring--complete .smbk-progress-ring__track::before,
.smbk-progress-ring--complete .smbk-progress-ring__track::after {
background: var(--smbk-color-feedback-success-icon);
}
/* ---- small: a smaller, thinner ring with no value in it ---- */
.smbk-progress-ring--size-sm {
width: var(--smbk-size-progress-ring-sm);
height: var(--smbk-size-progress-ring-sm);
}
.smbk-progress-ring--size-sm .smbk-progress-ring__track {
mask-image: radial-gradient(
farthest-side,
transparent calc(100% - var(--smbk-size-4)),
currentcolor calc(100% - var(--smbk-size-4))
);
}
.smbk-progress-ring--size-sm .smbk-progress-ring__track::before,
.smbk-progress-ring--size-sm .smbk-progress-ring__track::after {
left: calc(50% - var(--smbk-size-4) / 2);
width: var(--smbk-size-4);
height: var(--smbk-size-4);
}
.smbk-progress-ring--size-sm .smbk-progress-ring__track::after {
transform-origin: 50% calc(var(--smbk-size-progress-ring-sm) / 2);
}
.smbk-progress-ring--size-sm .smbk-progress-ring__value {
display: none;
}
/* ---- motion: when the value changes, the done part grows or shrinks to it with the state purpose,
so the change can be followed. No movement under reduced motion. ---- */
.smbk-progress-ring {
transition: --smbk-progress-ring-value var(--smbk-motion-state-duration) var(--smbk-motion-state-easing);
}
@media (prefers-reduced-motion: reduce) {
.smbk-progress-ring {
transition: none;
}
}
/* ---- high contrast (forced colors) ---- */
/* In Windows high-contrast mode the browser drops fills; this puts the meaning back with the user's own
colors, as the progress bar does: the done part in Highlight over a track in the text color. Nothing
here applies outside that mode. */
@media (forced-colors: active) {
.smbk-progress-ring .smbk-progress-ring__track {
forced-color-adjust: none;
background: conic-gradient(Highlight calc(var(--smbk-progress-ring-value) * 1%), CanvasText 0);
}
.smbk-progress-ring .smbk-progress-ring__track::before,
.smbk-progress-ring .smbk-progress-ring__track::after {
forced-color-adjust: none;
background: Highlight;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| size.progress-ring.md--smbk-size-progress-ring-md | 72px | Size | |
| radius.full--smbk-radius-full | 9999px | Radius | |
| color.data.series--smbk-color-data-series | light: default: #3b5ce3 · cockpit: #0a85a1dark: default: #7fa0ff · cockpit: #34a2bfchanges with theme | Color | |
| color.surface.track-subtle--smbk-color-surface-track-subtle | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| size.6--smbk-size-6 | 6px | Size | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| typography.label-md--smbk-typography-label-md-font-family--smbk-typography-label-md-font-size--smbk-typography-label-md-font-weight--smbk-typography-label-md-line-height--smbk-typography-label-md-letter-spacing--smbk-typography-label-md-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| color.feedback.success.icon--smbk-color-feedback-success-icon | light: #037a1fdark: #54b85bchanges with theme | Color | |
| size.progress-ring.sm--smbk-size-progress-ring-sm | 36px | Size | |
| size.4--smbk-size-4 | 4px | Size | |
| motion.state.duration--smbk-motion-state-duration | 200ms | Motion | |
| motion.state.easing--smbk-motion-state-easing | cubic-bezier(0.65, 0, 0.35, 1) | Motion |
Manifest
{
"name": "progress-ring",
"block": "smbk-progress-ring",
"summary": "A ring that fills clockwise from the top to show how far something has got, with the value in the middle.",
"adapt": "css",
"axes": {
"size": {
"values": [
"md",
"sm"
],
"default": "md",
"description": "Medium shows the value in the middle; small is a thinner ring with no room for it, for a row or a list."
}
},
"flags": {
"complete": {
"description": "The value has reached the end: the whole ring turns to the success color. The wrapper sets it; plain markup sets it the same way."
}
},
"parts": [
"track",
"value"
],
"figmaNodeId": "7580:184"
}