Cairn

Ring

Stable

Single-value radial progress, a grayscale ink arc on a neutral track. tone=accent opts into hue.

  • value
  • tone: ink
  • accent

Grayscale by default

  • Latency budget · sequential
  • p5082%
  • p7566%
  • p9048%
  • p9934%
Show code
import { Ring } from '@six-vallees/ui-react';

const val = 'font-semibold leading-none tabular-nums';

// Concentric gauge on a dark tile, one hue: the single-hue sequential ramp (light → dark blue),
// outer to inner. One family, not a rainbow; the labelled legend carries meaning, never colour alone.
const metrics = [
  { label: 'p50', value: 82, size: 200, color: 'var(--sv-sys-chart-sequential-4)' },
  { label: 'p75', value: 66, size: 156, color: 'var(--sv-sys-chart-sequential-3)' },
  { label: 'p90', value: 48, size: 112, color: 'var(--sv-sys-chart-sequential-2)' },
  { label: 'p99', value: 34, size: 68, color: 'var(--sv-sys-chart-sequential-1)' },
];

export default function RingDemo() {
  return (
    <div className="flex flex-col gap-10">
      {/* Grayscale by default (ink arc on a neutral track). Hue is opt-in via tone/color. */}
      <div>
        <p className="mb-4 sv-body-sm text-on-surface-variant">Grayscale by default</p>
        <div className="flex flex-wrap items-center gap-10 text-on-surface">
          <Ring value={70}>
            <div className={`${val} sv-metric text-on-surface`}>70%</div>
          </Ring>
          <Ring value={42} size={120} thickness={12}>
            <div className={`${val} sv-title-lg text-on-surface`}>42%</div>
          </Ring>
          <Ring value={88}>
            <div className={`${val} sv-metric text-on-surface`}>88%</div>
          </Ring>
        </div>
      </div>

      {/* Concentric multi-metric — the single-hue sequential ramp keeps it one family. */}
      <div
        data-theme="dark"
        className="flex flex-wrap items-center gap-x-12 gap-y-8 rounded-card bg-surface p-8 text-on-surface"
      >
        <div className="relative shrink-0" style={{ width: 200, height: 200 }}>
          {metrics.map((m) => (
            <div key={m.label} className="absolute inset-0 grid place-items-center">
              <Ring value={m.value} size={m.size} thickness={12} color={m.color} aria-label={`${m.label} ${m.value}%`} />
            </div>
          ))}
        </div>
        <ul className="flex flex-col gap-3">
          <li className="sv-body-sm text-on-surface-variant">Latency budget · sequential</li>
          {metrics.map((m) => (
            <li key={m.label} className="flex items-center gap-3 sv-body-md">
              <span className="size-3 shrink-0 rounded-full" style={{ background: m.color }} aria-hidden="true" />
              <span className="w-24 text-on-surface-variant">{m.label}</span>
              <span className={`${val} sv-body-lg`}>{m.value}%</span>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

Installation

npx shadcn@latest add https://cairn.sixvallees.com/r/ring.json