VisorVisor
ComponentsAdmin

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-indicator

Basic

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.

98

Auto-toned ramp

With tone="auto" (the default), the ring picks a semantic color from the value-to-max ratio:

Ratio rangeToneUse case
>= 0.85successHealthy / on-target
0.60 – 0.85infoTrending well
0.40 – 0.60warningWatch closely
< 0.40destructiveNeeds attention
1025507595

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.).

8686868686

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.

989898

Denominator placement

Hide, trail, or stack the / N denominator label.

989898

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-indicator

This copies two files into your project:

  • components/ui/score-indicator/score-indicator.tsx — the component
  • components/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 / Warning icons 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-gradient background if your design system prefers a solid wedge over a stroke arc.