Activity rings

Shows one to six personal goals as concentric rings with a staggered entrance, gentle rotating encouragement, named progress and explicit missing-data states.

Daily activityOuter to inner
  1. Walking

    18 of 30 minutes

    18 of 30 minutes
  2. Movement breaks

    5 of 8 breaks

    5 of 8 breaks
  3. Standing

    7 of 10 hours

    7 of 10 hours

Small steps still count.

One day, three goals

Sunday, 6 September

Your activityOuter to inner
  1. Walking

    32 of 30 minutes

    32 of 30 minutes

    Goal exceeded

  2. Movement breaks

    5 of 8 breaks

    5 of 8 breaks
  3. Standing

    7 of 10 hours

    7 of 10 hours

Targets chosen by you. Walking includes the extra two minutes.

Small steps still count.

Use it for

  • One activity ring or a compact set of personal movement, routine, or wellness goals.
  • Supply goals in outer-to-inner order, with unique ids, labels, amounts, targets, and units.
  • Pair with ActivityGoal for a linear progress view of the same data.
  • Rings sweep into their supplied values on first view and ease into later updates. Set animate to false for an instant graphic.
  • Encouragement picks a different line every ten seconds while visible. Supply your own encouragement strings, one string for a static line, or false to hide it. The first line stays deterministic during server rendering.

Not for

  • Deriving a readiness score, calorie prescription, or recommended target.
  • Treating an unrecorded amount as zero. More than six goals retain their text records without a ring graphic.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

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.

2. Vendor the source

npx @noorddev/vlak-cli add activity-rings

The StyleX leaf lands in components/vlak/, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/activity-rings.json

The same registry item, installed by shadcn's CLI.

CSS only

<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" />

ActivityRings

Concentric personal-goal progress, with a complete named record for each ring.

PropTypeDefaultDescription
labelrequiredstring
goalsrequiredreadonly ActivityRingGoal[]One to six goals are drawn from the outside inward. Every goal retains a text record.
descriptionReactNode
animatebooleantrueSweep each ring into its supplied value on first view. Reduced motion stays instant.
encouragementfalse | readonly string[]defaultEncouragementGentle lines rotate every ten seconds without immediate repetition. false hides the line.

Also accepts HTMLAttributes<HTMLElement>.

KeysDoes
TabMoves to the encouragement pause control when rotation is available.
Enter, SpacePauses 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>
.rs-activity-rings.rs-activity-rings-caption.rs-activity-rings-body.rs-activity-rings-graphic.rs-activity-rings-track.rs-activity-rings-arc.rs-activity-rings-list.rs-activity-rings-item.rs-activity-rings-index.rs-activity-rings-label.rs-activity-rings-value.rs-activity-rings-target.rs-activity-rings-note.rs-activity-rings-description.rs-activity-rings-progress.rs-activity-rings-motion.rs-activity-rings-waiting.rs-activity-rings-encouragement.rs-activity-rings-encouragement-text.rs-activity-rings-pause.rs-activity-rings-pause-icon