One day, three goals
Sunday, 6 September
Walking
32 of 30 minutes
Goal exceeded
Movement breaks
5 of 8 breaks
Standing
7 of 10 hours
Targets chosen by you. Walking includes the extra two minutes.
Small steps still count.
Shows one to six personal goals as concentric rings with a staggered entrance, gentle rotating encouragement, named progress and explicit missing-data states.
Walking
18 of 30 minutes
Movement breaks
5 of 8 breaks
Standing
7 of 10 hours
Small steps still count.
Sunday, 6 September
Walking
32 of 30 minutes
Goal exceeded
Movement breaks
5 of 8 breaks
Standing
7 of 10 hours
Targets chosen by you. Walking includes the extra two minutes.
Small steps still count.
Three ways in. They share one source, so the pixels match whichever you pick.
npm install @noorddev/vlak-react
// once, next to your app's root
import "@noorddev/vlak-react/css";
import { ActivityRings } from "@noorddev/vlak-react";Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/activity-rings.
npx @noorddev/vlak-cli add activity-rings
The StyleX leaf lands in components/vlak/, for your own compiler to own. See StyleX.
npx shadcn add https://vlak.dev/r/activity-rings.json
The same registry item, installed by shadcn's CLI.
<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />
No React. Link vlak.css and use the markup and classes below.
import { ActivityRings } from "@noorddev/vlak-react";
<ActivityRings label="Daily activity" goals={[
{ id: "walk", label: "Walking", current: 18, target: 30, unit: "minutes" },
{ id: "move", label: "Movement breaks", current: 5, target: 8, unit: "breaks" },
{ id: "stand", label: "Standing", current: 7, target: 10, unit: "hours" },
]} description="Personal targets, supplied by your application" />Concentric personal-goal progress, with a complete named record for each ring.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | ||
goalsrequired | readonly ActivityRingGoal[] | One to six goals are drawn from the outside inward. Every goal retains a text record. | |
description | ReactNode | ||
animate | boolean | true | Sweep each ring into its supplied value on first view. Reduced motion stays instant. |
encouragement | false | readonly string[] | defaultEncouragement | Gentle lines rotate every ten seconds without immediate repetition. false hides the line. |
Also accepts HTMLAttributes<HTMLElement>.
| Keys | Does |
|---|---|
| Tab | Moves to the encouragement pause control when rotation is available. |
| Enter, Space | Pauses or resumes encouraging text rotation. |
<figure class="rs-activity-rings"><figcaption class="rs-activity-rings-caption">Daily activity</figcaption><div class="rs-activity-rings-body"><svg class="rs-activity-rings-graphic" viewBox="0 0 200 200" aria-hidden="true"><g transform="rotate(-90 100 100)"><circle class="rs-activity-rings-track" cx="100" cy="100" r="88" stroke-width="14"/><circle class="rs-activity-rings-arc" cx="100" cy="100" r="88" stroke-width="14" pathLength="100" stroke-dasharray="60 100"/></g></svg><ol class="rs-activity-rings-list" aria-label="Daily activity, outer ring first"><li class="rs-activity-rings-item"><span class="rs-activity-rings-index" aria-hidden="true">01</span><div><p class="rs-activity-rings-label">Walking</p><p class="rs-activity-rings-value">18 <span class="rs-activity-rings-target">of 30 minutes</span></p><progress class="rs-activity-rings-progress" value="18" max="30" aria-label="Walking, ring 1" aria-valuetext="18 of 30 minutes">18 of 30 minutes</progress></div></li></ol></div></figure>