Skip to main content
Compendium / Maturity & Process / Instruments / Demonstration Completion Meter
#484

Demonstration Completion Meter

Half-circle gauge showing the fraction of acceptance criteria demonstrated this cycle.

STABLE DE
#svg #gauge #animated #reduced-motion #real-content #dark-mode-tested
Live
17/24 71% demonstrated
demos IN PROGRESS
Spec

Intent

Acceptance criteria only matter when shown working. This dial reports how many of the cycle's criteria have been demonstrated end-to-end, separating "done" from "claimed done".

Acceptance Criteria

  • [ ] 180-degree arc with demonstrated/total ratio
  • [ ] Bands by completion: danger under 50, attention 50-79, success 80+
  • [ ] Ratio readout in tabular numerals
  • [ ] Negative: does NOT require JS

Constraints

  • Pure SVG, no runtime
  • Theme-aware via tokens
// demonstration-completion-meter.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 484,
  slug: 'demonstration-completion-meter',
  name: 'Demonstration Completion Meter',
  category: 'maturity-process',
  subcategory: 'instruments',
  pillar: 'de',
  description: 'Half-circle gauge showing the fraction of acceptance criteria demonstrated this cycle.',
  spec: `## Intent
Acceptance criteria only matter when shown working. This dial reports how many of the cycle's criteria have been demonstrated end-to-end, separating "done" from "claimed done".

## Acceptance Criteria
- [ ] 180-degree arc with demonstrated/total ratio
- [ ] Bands by completion: danger under 50, attention 50-79, success 80+
- [ ] Ratio readout in tabular numerals
- [ ] Negative: does NOT require JS

## Constraints
- Pure SVG, no runtime
- Theme-aware via tokens`,
  tags: ['svg', 'gauge', 'animated', 'reduced-motion', 'real-content', 'dark-mode-tested'],
  status: 'stable',
  health: { ics: 88, a11y: 'aa', themed: true, animated: true },
};

const demonstrated = 17;
const total = 24;
const pct = Math.round((demonstrated / total) * 100);
const fill = pct >= 80 ? 'var(--color-success)' : pct >= 50 ? 'var(--color-attention)' : 'var(--color-danger)';
const band = pct >= 80 ? 'SHIPPABLE' : pct >= 50 ? 'IN PROGRESS' : 'EARLY';
const r = 90;
const arcTotal = Math.PI * r;
const dashOffset = arcTotal * (1 - pct / 100);
---

<div data-this-component class="w-full bg-paper text-ink border-4 border-ink p-4 flex flex-col items-center overflow-hidden">
  <svg viewBox="0 0 200 120" class="w-full max-w-[280px] block" role="img" aria-label={`Demonstration ${demonstrated} of ${total} complete, ${band}`}>
    <path d="M 10 100 A 90 90 0 0 1 190 100" stroke="var(--color-ink)" stroke-width="14" fill="none" opacity="0.12" />
    <path
      d="M 10 100 A 90 90 0 0 1 190 100"
      stroke={fill}
      stroke-width="14"
      fill="none"
      stroke-dasharray={arcTotal}
      stroke-dashoffset={dashOffset}
      style="transition: stroke-dashoffset 800ms cubic-bezier(0.2,0,0.13,1.4)"
    />
    <text x="100" y="84" text-anchor="middle" font-family="Inter, sans-serif" font-weight="900" font-size="32" fill="var(--color-ink)" class="tabular-nums">{demonstrated}/{total}</text>
    <text x="100" y="104" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="9" letter-spacing="2" fill="var(--color-ink)" opacity="0.6">{pct}% demonstrated</text>
  </svg>
  <div class="mt-1 flex items-baseline gap-2">
    <span class="font-mono text-[10px] uppercase tracking-widest opacity-60">demos</span>
    <span class="font-black text-[11px] uppercase tracking-widest">{band}</span>
  </div>
</div>