Spec
Intent
Show how a spec's ICS has evolved across revisions. The trend matters more than any single value — a flat 65 is healthier than a volatile 78.
Acceptance Criteria
- [ ] Sparkline plots ICS across N revisions
- [ ] Min and max ticks labelled on the line
- [ ] Current readout shown large to the side
- [ ] Negative: does NOT show absolute zero baseline (sparkline auto-scales)
Constraints
- Pure SVG, no JS
- Tabular-nums for the readout
// ics-sparkline.astro
---
import type { CompendiumMeta } from '../../../data/types';
export const meta: CompendiumMeta = {
id: 57,
slug: 'ics-sparkline',
name: 'ICS Sparkline',
category: 'metrics-telemetry',
subcategory: 'charts',
pillar: 'de',
description: 'Single-line trend of the Intent Completion Score over time, with min/max ticks and a current readout.',
spec: `## Intent
Show how a spec's ICS has evolved across revisions. The trend matters more than any single value — a flat 65 is healthier than a volatile 78.
## Acceptance Criteria
- [ ] Sparkline plots ICS across N revisions
- [ ] Min and max ticks labelled on the line
- [ ] Current readout shown large to the side
- [ ] Negative: does NOT show absolute zero baseline (sparkline auto-scales)
## Constraints
- Pure SVG, no JS
- Tabular-nums for the readout`,
tags: ['svg', 'chart', 'real-content'],
status: 'stable',
health: { ics: 86, a11y: 'aa', themed: true, animated: false },
};
const series = [42, 51, 58, 54, 63, 71, 68, 74, 79, 82, 78, 85];
const min = Math.min(...series);
const max = Math.max(...series);
const range = max - min || 1;
const current = series.at(-1)!;
const n = series.length - 1;
const xy = series.map((v, i) => ({
x: (i / n) * 100,
y: 100 - ((v - min) / range) * 100,
}));
const pts = xy.map((p) => `${p.x},${p.y}`).join(' ');
const minIdx = series.indexOf(min);
const maxIdx = series.indexOf(max);
---
<div class="w-full bg-paper text-black border-4 border-black">
<header class="flex items-baseline justify-between px-3 py-2 border-b-2 border-black">
<span class="font-mono text-[10px] uppercase tracking-widest opacity-60">// ICS · 12 revisions</span>
<span class="font-mono text-[10px] uppercase tracking-widest">TREND</span>
</header>
<div class="grid grid-cols-[1fr_auto]">
<div class="p-3 border-e-2 border-black">
<svg viewBox="0 0 100 100" preserveAspectRatio="none" class="w-full h-20 block" aria-label={`ICS trend, currently ${current}`}>
<polyline points={pts} fill="none" stroke="var(--color-de)" stroke-width="2.5" vector-effect="non-scaling-stroke" />
<circle cx={xy[minIdx].x} cy={xy[minIdx].y} r="2.5" fill="var(--color-attention)" />
<circle cx={xy[maxIdx].x} cy={xy[maxIdx].y} r="2.5" fill="var(--color-success)" />
<line x1="100" y1="0" x2="100" y2="100" stroke="var(--color-ink)" stroke-width="0.5" stroke-dasharray="2 2" vector-effect="non-scaling-stroke" />
</svg>
<div class="mt-2 flex justify-between font-mono text-[9px] uppercase tracking-widest">
<span class="opacity-60">min <span class="font-black text-black tabular-nums">{min}</span></span>
<span class="opacity-60">max <span class="font-black text-black tabular-nums">{max}</span></span>
</div>
</div>
<div class="p-3 flex flex-col items-center justify-center min-w-[80px]">
<span class="font-black text-3xl tabular-nums leading-none">{current}</span>
<span class="font-mono text-[9px] uppercase tracking-widest opacity-60 mt-1">now</span>
</div>
</div>
</div>