Score Indicator
Compact circular ring visualization for percentage or ratio metrics — health score, uptime, engagement, capacity. Auto-toned color mapping derived from the value/max ratio.
npx visor add score-indicatorBasic
A score indicator pairs a value with a max (defaulting to 100) and renders a circular ring filled proportionally to the ratio. The default tone="auto" picks a semantic color from the ratio.
Auto-toned ramp
With tone="auto" (the default), the ring picks a semantic color from the value-to-max ratio:
| Ratio range | Tone | Use case |
|---|---|---|
>= 0.85 | success | Healthy / on-target |
0.60 – 0.85 | info | Trending well |
0.40 – 0.60 | warning | Watch closely |
< 0.40 | destructive | Needs attention |
Tones
Pass an explicit tone to bypass the auto mapping — useful when the visual intent should not track the underlying ratio (a low-but-intentional value, a fixed status color, etc.).
Sizes
Three sizes for different placement densities. md is the default for dashboard facts rows; sm fits inline next to body text; lg is for feature placement.
Denominator placement
Hide, trail, or stack the / N denominator label.
Custom format
The default formatter renders a rounded integer. Pass format to render percentages, decimals, or any custom string. The format callback runs on the client — pass it from a client component or inline inside a 'use client' boundary.
<ScoreIndicator value={86} max={100} format={(v) => `${v}%`} size="lg" />
<ScoreIndicator
value={99.95}
max={100}
ariaLabel="Uptime 99.95 percent"
format={(v) => `${v.toFixed(2)}%`}
/>In a dashboard facts row
The score indicator is purpose-built for admin dashboard "facts" rows where a numeric KPI sits next to a quick visual cue.
Health score
98
Accessibility
The ring container is role="img" with a default aria-label of "{value} out of {max}". Override ariaLabel to add semantic context (e.g. "Organization health score: 98 out of 100").
When the resolved tone is destructive or warning, a small filled phosphor icon is positioned at the top-right of the ring as an additional non-color cue. The icon is aria-hidden — the surrounding context (label or ariaLabel) is responsible for conveying the meaning.
The denominator label is aria-hidden because it is already announced inside the default aria-label.
Transitions on the indicator arc are disabled under prefers-reduced-motion.
Installation
npx visor add score-indicatorThis copies two files into your project:
components/ui/score-indicator/score-indicator.tsx— the componentcomponents/ui/score-indicator/score-indicator.module.css— the styles
Usage
import { ScoreIndicator } from '@/components/ui/score-indicator/score-indicator';
<ScoreIndicator value={organization.healthScore} max={100} />
// Explicit tone, denominator below
<ScoreIndicator value={86} tone="success" denominator="below" size="lg" />
// Percent formatter, custom aria-label
<ScoreIndicator
value={99.95}
max={100}
ariaLabel="Uptime 99.95 percent"
format={(v) => `${v.toFixed(2)}%`}
/>API Reference
ScoreIndicatorProps
No props data available for “score-indicator”.
The component also accepts all standard <span> HTML attributes.
Customization
After copying the component, you own it completely. Common customizations:
- Tune the ring track + arc colors per tone by overriding the
--score-indicator-*custom properties on the wrapper. - Replace the phosphor
WarningCircle/Warningicons with a different glyph for the destructive / warning tone overlay. - Add an animated count-up by wrapping the value in your own animation primitive — this is intentionally out of scope here.
- Swap the SVG ring for a
conic-gradientbackground if your design system prefers a solid wedge over a stroke arc.
Page Header
Admin page header compound with eyebrow, title, description, breadcrumb, and actions slots. Built with CSS Modules and container queries — copy it into your project and own it completely.
Stat Card
Compact admin dashboard metric card with label, value, delta, trend, and footer slots. Built with CSS Modules, container queries, and accessible delta announcements — copy it into your project and own it completely.