CircularProgressBar to draw a determinate value when the dial itself is the main content. Its angular range, stroke weight, and size are configurable, which fits a charge level, countdown, camera or media meter, or any design that needs a gapped arc rather than a closed ring. By default, it sweeps clockwise from the lower left to the lower right, leaving a gap at the bottom.import { CircularProgressBar } from '@wearables-ui-toolkit/mrbd';
type Props = {
chargeLevel: number;
};
export function ChargeDial({ chargeLevel }: Props) {
return (
<div className="charge-dial">
<CircularProgressBar
progress={chargeLevel}
animated
aria-label={`Battery ${Math.round(chargeLevel * 100)} percent`}
/>
<span className="uit-text-heading2">{Math.round(chargeLevel * 100)}%</span>
</div>
);
}
.charge-dial {
display: grid;
place-items: center;
width: 100%;
aspect-ratio: 1;
}
.charge-dial > * {
grid-area: 1 / 1;
}
size omitted, the component fills its parent, so a square, layout-driven container is all it needs to scale. The component draws only the arc; a centered readout is a sibling in your own stacking container.import { CircularProgressBar } from '@wearables-ui-toolkit/mrbd';
export function TopHalfMeter({ level }: { level: number }) {
return (
<CircularProgressBar
progress={level}
startAngleDegrees={180}
endAngleDegrees={360}
strokeWidthPx={4}
size={80}
aria-label={`Signal ${Math.round(level * 100)} percent`}
/>
);
}
CircularProgressBarProps, which extends HTMLAttributes<HTMLDivElement>.| Prop | Type | Default | Description |
|---|---|---|---|
progress | number | 0 | Fraction complete from 0 to 1. Values outside the range are clamped. |
animated | boolean | false | Tween arc changes with the progress spring instead of snapping. Applies to every change while set. |
strokeWidthPx | number | 6 | Stroke weight of both the track and the arc, in the drawing coordinate system. |
startAngleDegrees | number | 140 | Angle where the arc begins. 0 is the right of the circle, 90 the bottom, 180 the left, 270 the top. |
endAngleDegrees | number | 400 | Angle where the arc ends. The difference from the start angle is the sweep. |
size | number | — | Width and height of the component, in pixels; it is always square. Omit it to fill the parent. |
aria-label | string | — | Replaces the default percentage accessible name. |
className | string | '' | Added to the root element. |
style | CSSProperties | {} | Merged onto the root element, after the width and height the component sets, so it can override them. |
progressbar element. Every other DOM prop — aria-*, data-*, and the rest — lands there too. The component writes role, aria-valuenow, aria-valuemin, and aria-valuemax last, so values you pass for those four are overwritten; aria-label is the one it defers to.progress is a normalized fraction, not a percentage: divide your own values before passing them. Anything below 0 or above 1 is clamped.0 at the right of the circle. The arc is drawn from startAngleDegrees to endAngleDegrees, so the sweep is the difference between them; the defaults sweep 260 degrees and leave a gap centered on the bottom of the circle. Keep the sweep below a full turn — for a closed circle, use ProgressRing instead — and keep the end angle greater than the start angle, carrying it past 360 when the arc has to cross the right of the circle, as the defaults do. The path is always drawn clockwise from start to end, while the fill length is computed from the difference between the two angles, so a reversed pair draws one arc and measures another and the fill stops tracking the value.size, the root is sized to 100% in both axes, so give it a square container and it scales with the available space. With size, the component is locked to that square. Because your style is merged after those dimensions, an explicit width or height there wins.size when you supply it, and a fixed default extent when you do not. strokeWidthPx is expressed in that coordinate system, so a stated stroke weight is exact when size is set and scales proportionally with the parent when it is not. The drawing is centered and keeps its aspect ratio in either case.progress={0}; the arc is simply drawn with zero length.--uit-color-icon-primary, which keeps the dial consistent with iconography beside it.animated applies a sampled spring transition — the same SpringConfigs.PROGRESS_VALUE curve used across determinate progress. It is a persistent declarative prop backed by a CSS transition, so it applies to every progress change while set. To animate only some updates, toggle the prop around the update rather than passing a per-update flag.usePrefersReducedMotion and drop animated if your app honors a reduced-motion preference.role="progressbar" with aria-valuenow set to the rounded percentage, aria-valuemin of 0, and aria-valuemax of 100. Note that aria-valuenow is the percentage, not the 0-to-1 fraction you passed.aria-label replaces that name entirely — unlike the other determinate indicators, it is not appended — so include the value in your label if you still want it read out.announceUpdatesForAccessibility, or drive your own status text.aria-labelledby and aria-describedby are forwarded to the same root element, so they can point at on-screen text you already render.aria-label because that label replaces the default percentage.size when the stroke weight needs to land on an exact pixel value.CircularProgressBar or ProgressRing for one value so readers see a single progress representation.announceUpdatesForAccessibility when updates need to be announced; CircularProgressBar has no live region.