Skip to main content
Compendium / Specs & Documents / Atoms / ICS Gauge
#002

ICS Gauge

A semicircular gauge for the Intent Completion Score — the framework's primary spec-quality metric.

STABLE DE
#svg #gauge #animated #real-content
Live
82 / 100
ICS READY
Spec

Intent

Show the Intent Completion Score (0–100) of a spec as a half-circle gauge. Communicate at a glance whether a spec is ready for composition (≥80), needs work (60–79), or is unfit for AI consumption (<60).

Acceptance Criteria

  • [ ] Renders a 180° arc with a coloured fill that maps to the score
  • [ ] Bands: red <60, yellow 60–79, green ≥80
  • [ ] Numeric readout in centre, large and dominant
  • [ ] Threshold label below (READY / DEVELOPING / UNFIT)
  • [ ] Does not require JS — animation via CSS transition only

Constraints

  • Pure SVG, no JS
  • Theme-aware stroke colour via tokens
// ics-gauge.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 2,
  slug: 'ics-gauge',
  name: 'ICS Gauge',
  category: 'specs-documents',
  pillar: 'de',
  description: 'A semicircular gauge for the Intent Completion Score — the framework\'s primary spec-quality metric.',
  spec: `## Intent
Show the Intent Completion Score (0–100) of a spec as a half-circle gauge. Communicate at a glance whether a spec is ready for composition (≥80), needs work (60–79), or is unfit for AI consumption (<60).

## Acceptance Criteria
- [ ] Renders a 180° arc with a coloured fill that maps to the score
- [ ] Bands: red <60, yellow 60–79, green ≥80
- [ ] Numeric readout in centre, large and dominant
- [ ] Threshold label below (READY / DEVELOPING / UNFIT)
- [ ] Does not require JS — animation via CSS transition only

## Constraints
- Pure SVG, no JS
- Theme-aware stroke colour via tokens`,
  tags: ['svg', 'gauge', 'animated', 'real-content'],
  status: 'stable',
  health: { ics: 92, a11y: 'aa', themed: true, animated: true },
};

const score = 82;
const band  = score >= 80 ? 'READY' : score >= 60 ? 'DEVELOPING' : 'UNFIT';
const fill  = score >= 80 ? 'var(--color-success)' : score >= 60 ? 'var(--color-do)' : 'var(--color-attention)';
const r = 90;
const total = Math.PI * r;
const dashOffset = total * (1 - score / 100);
---

<div class="w-full bg-paper text-black border-4 border-black p-4 flex flex-col items-center">
  <svg viewBox="0 0 200 120" class="w-full max-w-[280px] block" aria-label={`ICS ${score}`}>
    <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-linecap="butt"
      stroke-dasharray={total}
      stroke-dashoffset={dashOffset}
      style="transition: stroke-dashoffset 800ms cubic-bezier(0.2,0,0.13,1.4)"
    />
    <g stroke="var(--color-ink)" stroke-width="2">
      <line x1="10"  y1="100" x2="10"  y2="112"/>
      <line x1="100" y1="10"  x2="100" y2="22"/>
      <line x1="190" y1="100" x2="190" y2="112"/>
    </g>
    <text x="100" y="86" text-anchor="middle" font-family="Inter, sans-serif" font-weight="900" font-size="42" fill="var(--color-ink)">{score}</text>
    <text x="100" y="106" text-anchor="middle" font-family="JetBrains Mono, monospace" font-size="9" letter-spacing="2" fill="var(--color-ink)" opacity="0.6">/ 100</text>
  </svg>
  <div class="mt-1 flex items-baseline gap-2">
    <span class="font-mono text-[10px] uppercase tracking-widest opacity-60">ICS</span>
    <span class="font-black text-[11px] uppercase tracking-widest">{band}</span>
  </div>
</div>