Progress ring

Use the ring where a single percentage needs little space: monthly goal in an athlete tile, readiness in a list aside. The percentage is printed in the centre.

The ring is one image for assistive tech with a label; default "{value} Prozent erledigt". Pass label to say what the percentage is about ("Monatsziel: 31 Prozent erreicht").

Values are rounded to whole percent for display. For progress with absolute numbers ("1 von 3") prefer the progress bar or segments.

Examples

Default

Data
render({
  "value": 31
})
Markup
<div class="ring" style="--value: 31" role="img" aria-label="31 Prozent erledigt"><span>31%</span></div>

Large with context label

Data
render({
  "value": 75,
  "size": "lg",
  "label": "Monatsziel: 75 Prozent erreicht"
})
Markup
<div class="ring" data-size="lg" style="--value: 75" role="img" aria-label="Monatsziel: 75 Prozent erreicht"><span>75%</span></div>

Data contract

What render(data) accepts — validated against this JSON Schema in development and tests. Unknown fields are rejected. The same schema is in components.json.

FieldTypeRequiredDefaultDescription
valuenumberrequired—Percentage 0–100 (rounded for display).
≥ 0 · ≤ 100
labelstringoptional—Accessible label; default "{value} Prozent erledigt".
size"sm" | "md" | "lg"optional"md"sm 40 px, md 56 px, lg 80 px (design guide §9.7), written as data-size.
JSON Schema
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "value"
  ],
  "properties": {
    "value": {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "description": "Percentage 0–100 (rounded for display)."
    },
    "label": {
      "type": "string",
      "description": "Accessible label; default \"{value} Prozent erledigt\"."
    },
    "size": {
      "type": "string",
      "enum": [
        "sm",
        "md",
        "lg"
      ],
      "default": "md",
      "description": "`sm` 40 px, `md` 56 px, `lg` 80 px (design guide §9.7), written as `data-size`."
    }
  }
}

Markup & states

Root: div.ring — usable without render() by writing the markup directly.

Attributes

AttributeValuesDefaultDescription
data-sizesm | lg(none = md, 56 px)Diameter: 40 / 80 px.
style="--value: N"0–100—Fill amount (set by render; the only inline style).
role="img" aria-labeltext—Accessible label.

Parts

PartDescription
::beforeThe 4 px ring: --color-surface-2 track, --color-accent arc (conic gradient, masked to a stroke so it works on any surface).
spanCentred percentage text (semibold, tabular figures).

CSS custom properties

PropertyDefaultDescription
--value0Percentage filled (conic gradient).
--ring-size3.5remDiameter; set by data-size, can be overridden for special layouts.

Accessibility

  • Announced once as an image with its label; the centre text is visible for everyone.
  • The number is always printed — the arc alone is never the only information.

Guidance

DoSay what the percentage is about in the label and nearby text.
DoUse for the athlete's own goals.
Don'tDon't place several rings side by side for comparison — use a table.
Don'tDon't use status colours on the ring without a word explaining them.

Related: Progress bar · Progress segments · Stat tile