Skip to content
ShineUI
Install

ProgressRing

Progress in the round.

<sui-progress-ring>FeedbackStable

About

Determinate progress drawn as a ring, for an import or a period close where a circle fits the layout better than a bar. With show-value the center reads the rounded percentage, or the value slot's count. It is output, not a control; use sui-progress-spinner when the total is unknown and sui-knob when the user sets the value.

Usage

<sui-progress-ring value="72" label="Importing" show-value></sui-progress-ring> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); </script>
import { SuiProgressRing } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { return <SuiProgressRing value="72" label="Importing" showValue />; }
<script setup> import { SuiProgressRing } from "@shineui/vue"; import "@shineui/core/shineui.css"; </script> <template> <SuiProgressRing value="72" label="Importing" show-value /> </template>
import { Component } from "@angular/core"; import { SuiProgressRing } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiProgressRing], template: `<sui-progress-ring value="72" label="Importing" show-value></sui-progress-ring>`, }) export class Example { }

Basic

A navy arc on a soft track, with show-value the rounded percentage in the middle, and the caption underneath. The wrapper is role="progressbar" with min 0, max 100 and the value; the SVG is aria-hidden and focusable="false", so the value is announced once and the arc never becomes a tab stop.

<sui-progress-ring value="72" label="Importing" show-value></sui-progress-ring>

Quiet by default

Without show-value the ring draws the arc and the caption and says no number, which is the shape for a small ring beside its own count in a row. Add show-value where the percentage is the point.

<div style="display:flex;gap:20px;align-items:flex-end;flex-wrap:wrap;"> <sui-progress-ring value="58" label="Period close"></sui-progress-ring> <sui-progress-ring value="58" label="Period close" show-value></sui-progress-ring> </div>

Accent and sizes

accent swaps the navy arc for orange, for a budget or attention meter. diameter is the outer diameter in px and thickness the stroke; a ring with no caption takes its name from aria-label on the host.

<div style="display:flex;gap:20px;align-items:flex-end;flex-wrap:wrap;"> <sui-progress-ring value="58" diameter="28" thickness="3" aria-label="Sync progress"></sui-progress-ring> <sui-progress-ring value="58" label="Default" show-value></sui-progress-ring> <sui-progress-ring value="58" diameter="84" thickness="7" accent label="Budget used" show-value></sui-progress-ring> </div>

Value slot

Fill the value slot to put a count or a figure in the middle instead of the percentage. The slot is a readout only: value, aria-valuenow and the drawn arc are unchanged, and it is drawn with the readout, so it needs show-value too.

7/12 $5.2k
<div style="display:flex;gap:20px;align-items:center;flex-wrap:wrap;"> <sui-progress-ring value="58" diameter="72" label="Period close" show-value><span slot="value">7/12</span></sui-progress-ring> <sui-progress-ring value="87" diameter="72" accent label="Marketing" show-value><span slot="value">$5.2k</span></sui-progress-ring> </div>

Live value

value is a plain number prop written from app state. It clamps to 0 through 100 and the readout rounds; the arc animates to each new value.

0/12
<div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap;"> <sui-progress-ring id="pr-live" value="0" diameter="72" label="Closing July" show-value><span slot="value" id="pr-count">0/12</span></sui-progress-ring> <sui-button id="pr-step" variant="outline" label="Close an account"></sui-button> <sui-button id="pr-reset" variant="ghost" label="Reset"></sui-button> </div>
// setup(root): properties and events setup(root) { const ring = root.querySelector('#pr-live'); const count = root.querySelector('#pr-count'); let done = 0; const paint = () => { ring.value = (done / 12) * 100; count.textContent = `${done}/12`; }; root.querySelector('#pr-step').addEventListener('click', () => { done = Math.min(12, done + 1); paint(); }); root.querySelector('#pr-reset').addEventListener('click', () => { done = 0; paint(); }); }

API

<sui-progress-ring>

Properties

PropertyAttributeTypeDefaultDescription
accentaccentbooleanfalseAccent arc instead of brand.
diameterdiameternumber44Outer diameter of the ring in px. The center readout scales with it and floors at 10px.
labellabelstringstring | undefinedCaption under the ring. Also the accessible name of the progressbar role; a ring with no caption takes its name from aria-label on the host instead. The SVG itself is aria-hidden and focusable="false", so the value is announced once and the arc is never a tab stop.
showValueshow-valuebooleanfalseDraws the readout in the center: the rounded percentage in the page's locale (47%, 47 % in German), or the value slot when it is filled. Off by default, so a small ring beside its own count, as in a milestone row, says nothing twice.
thicknessthicknessnumber4Ring stroke in px.
valuevaluenumber0Completion, 0 to 100.

Slots

SlotDescription
valueReplaces the center percentage. Drawn with the readout, so it needs show-value too.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also