Circular Progress
Circular Progress draws a track and an indicator on an SVG circle, then animates the indicator's stroke-dashoffset from empty to the current value. Because the ring is one path rather than a clipped rectangle, it follows any diameter and thickness without extra layout.
Changing value animates from wherever the ring currently sits, so a stream of updates reads as a smooth climb rather than a series of jumps. The ring holds at its final value when the visitor has asked for reduced motion.
Usage
<script setup lang="ts">
const rings = [
{ value: 72, size: 132, caption: 'Storage' },
{ value: 45, size: 108, caption: 'Memory' },
{ value: 91, size: 84, caption: 'CPU' },
]
</script>
<template>
<div class="flex w-full flex-wrap items-center justify-center gap-8 py-6">
<MCircularProgress
v-for="ring in rings"
:key="ring.caption"
:value="ring.value"
:size="ring.size"
:caption="ring.caption"
/>
</div>
</template>
Examples
Customize
Customize
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | 0 | Progress from 0 to 100. Values outside that range are clamped. |
size | number | 140 | Diameter of the ring, in pixels. The stroke is drawn inside this box. |
strokeWidth | number | 10 | Thickness of the ring, in pixels. |
duration | number | 1.1 | Seconds the ring takes to reach a new value. |
showLabel | boolean | true | Draw the percentage in the middle of the ring. Turn it off when the ring is decoration. |
caption | string | undefined | Text under the percentage, for a short label like Storage. |
label | string | undefined | Accessible description of the ring. Defaults to the current percentage. |
class | string | '' | CSS classes for the wrapper. |
Slots
| Slot | Props | Description |
|---|---|---|
default | - | Replaces the percentage and caption in the middle of the ring. |
UI
This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.circularProgress in app.config.ts.
| Prop | Type | Default | Description |
|---|---|---|---|
ui | object | - | Per-slot classes merged over the theme defaults. |
| Slot | Props | Description |
|---|---|---|
root | - | relative inline-flex items-center justify-center |
svg | - | -rotate-90 |
track | - | stroke-current text-muted/20 |
indicator | - | stroke-current text-primary |
label | - | absolute inset-0 flex flex-col items-center justify-center text-highlighted |
value | - | text-lg font-semibold tabular-nums |
caption | - | text-xs text-muted |
Accessibility
The ring is a role="img" with a label that spells out the value, so a screen reader hears "72% complete" instead of nothing. The digits in the middle are inside that labelled image and stay legible without a second announcement. Because the value is also readable as text, the ring never has to carry the number on its own.