Menu


Progress ring


A ring that fills clockwise from the top to show how far something has got, with the value in the middle.

Customize the preview


Choices
Flags
View

Options


Progress ring options
ChoiceValuesDefaultWhat it does
sizesmbk-progress-ring--size-<value>mdsmmd
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--completeOn or offOff
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.

Partssmbk-progress-ring__tracksmbk-progress-ring__value

Examples


Usage

The ring with its value in the middle, named by the words beside it.

Profile complete

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.

Connect a domain
Invite the team
Set up billing

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.
Profile complete
Do

Say what the ring measures beside it.

Don't

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, or aria-labelledby pointing 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-valuetext when 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
progress-ring.css
/* 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
Progress ring tokens
Token and CSSValuePreviewFoundation
size.progress-ring.md--smbk-size-progress-ring-md72pxSize
radius.full--smbk-radius-full9999pxRadius
color.data.series--smbk-color-data-serieslight: default: #3b5ce3 · cockpit: #0a85a1dark: default: #7fa0ff · cockpit: #34a2bfchanges with themeColor
color.surface.track-subtle--smbk-color-surface-track-subtlelight: #c4cdd8dark: #2d3237changes with themeColor
size.6--smbk-size-66pxSize
color.text.default--smbk-color-text-defaultlight: #232527dark: #e7ebf0changes with themeColor
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-transformfontFamily: 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-iconlight: #037a1fdark: #54b85bchanges with themeColor
size.progress-ring.sm--smbk-size-progress-ring-sm36pxSize
size.4--smbk-size-44pxSize
motion.state.duration--smbk-motion-state-duration200msMotion
motion.state.easing--smbk-motion-state-easingcubic-bezier(0.65, 0, 0.35, 1)Motion
Manifest
progress-ring.manifest.json
{
  "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"
}