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