Skip to main content
Compendium / Metrics & Telemetry / Gauges / Progress Ring With Centre Label
#705

Progress Ring With Centre Label

Progress ring with a two-line centred label — value above, qualifier below.

STABLE DE
#svg #gauge #real-content #responsive
Live
17/24 runbooks
Spec

Intent

Self-contained progress widget — the ring shows fraction, the centred label tells you what fraction and of what.

Acceptance Criteria

  • [ ] Thin radial ring with proportional fill
  • [ ] Centred two-line label: value + qualifier
  • [ ] Token-driven colour
  • [ ] Negative: does NOT crop label on overflow

Constraints

  • Pure SVG + HTML overlay
  • Theme-aware
// progress-ring-with-center-label.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 705,
  slug: 'progress-ring-with-center-label',
  name: 'Progress Ring With Centre Label',
  category: 'metrics-telemetry',
  subcategory: 'gauges',
  pillar: 'de',
  description: 'Progress ring with a two-line centred label — value above, qualifier below.',
  spec: `## Intent
Self-contained progress widget — the ring shows fraction, the centred label tells you what fraction and of what.

## Acceptance Criteria
- [ ] Thin radial ring with proportional fill
- [ ] Centred two-line label: value + qualifier
- [ ] Token-driven colour
- [ ] Negative: does NOT crop label on overflow

## Constraints
- Pure SVG + HTML overlay
- Theme-aware`,
  tags: ['svg', 'gauge', 'real-content', 'responsive'],
  status: 'stable',
  health: { ics: 87, a11y: 'aa', themed: true, animated: false },
};

const done = 17;
const total = 24;
const value = Math.round((done / total) * 100);
const r = 44;
const c = 2 * Math.PI * r;
const dashOffset = c * (1 - value / 100);
---

<div data-this-component class="w-full bg-paper text-black border-4 border-black p-4 flex items-center justify-center overflow-hidden">
  <div class="relative">
    <svg viewBox="0 0 100 100" class="w-28 h-28 block -rotate-90" aria-label={`${done} of ${total} done`}>
      <circle cx="50" cy="50" r={r} fill="none" stroke="var(--color-ink)" stroke-width="8" opacity="0.12" />
      <circle cx="50" cy="50" r={r} fill="none" stroke="var(--color-de)" stroke-width="8" stroke-dasharray={c} stroke-dashoffset={dashOffset} />
    </svg>
    <div class="absolute inset-0 flex flex-col items-center justify-center">
      <span class="font-black text-xl tabular-nums leading-none">{done}<span class="opacity-60">/{total}</span></span>
      <span class="font-mono text-[9px] uppercase tracking-widest opacity-60 mt-1">runbooks</span>
    </div>
  </div>
</div>