Spec
Intent
Show the per-dimension breakdown of an Intent Completion Score. Five axes — Clarity, Verifiability, Completeness, Constraints, Lineage — each scored 0–100. The polygon area communicates overall strength; jagged shapes flag weak axes.
Acceptance Criteria
[ ] Five-axis radar grid (pentagon)
[ ] Score polygon filled with pillar accent
[ ] Axis labels visible
[ ] Numeric readout per axis
[ ] Does not require JS or hover for the data to read
Constraints
Pure SVG, no JS
Theme-aware via tokens
// ics-scoring-breakdown.astro copy
---
import type { CompendiumMeta } from '../../../data/types';
export const meta: CompendiumMeta = {
id: 14,
slug: 'ics-scoring-breakdown',
name: 'ICS Scoring Breakdown',
category: 'specs-documents',
subcategory: 'analysis-and-lint',
pillar: 'de',
description: 'A radar/spider chart of the five ICS scoring components — Clarity, Verifiability, Completeness, Constraints, Lineage.',
spec: `## Intent
Show the per-dimension breakdown of an Intent Completion Score. Five axes — Clarity, Verifiability, Completeness, Constraints, Lineage — each scored 0–100. The polygon area communicates overall strength; jagged shapes flag weak axes.
## Acceptance Criteria
- [ ] Five-axis radar grid (pentagon)
- [ ] Score polygon filled with pillar accent
- [ ] Axis labels visible
- [ ] Numeric readout per axis
- [ ] Does not require JS or hover for the data to read
## Constraints
- Pure SVG, no JS
- Theme-aware via tokens`,
tags: ['svg', 'chart', 'real-content', 'dark-mode-tested'],
status: 'stable',
health: { ics: 100, a11y: 'aa', themed: true, animated: false },
};
const axes = [
{ name: 'CLARITY', score: 88 },
{ name: 'VERIFIABILITY', score: 72 },
{ name: 'COMPLETENESS', score: 80 },
{ name: 'CONSTRAINTS', score: 65 },
{ name: 'LINEAGE', score: 95 },
];
const cx = 100, cy = 100, R = 70;
const pt = (i: number, r: number): [number, number] => {
const angle = -Math.PI / 2 + (i * 2 * Math.PI) / axes.length;
return [cx + r * Math.cos(angle), cy + r * Math.sin(angle)];
};
const gridLevels = [0.25, 0.5, 0.75, 1];
const scorePoints = axes.map((a, i) => pt(i, R * (a.score / 100)).join(',')).join(' ');
const labelPts = axes.map((_, i) => pt(i, R + 14));
---
<div class="w-full bg-paper text-black border-4 border-black shadow-nb-sm p-3 flex flex-col items-center">
<div class="w-full flex items-baseline justify-between mb-1">
<span class="font-mono text-[9px] uppercase tracking-widest font-black">ICS BREAKDOWN</span>
<span class="font-mono text-[9px] uppercase tracking-widest opacity-60">SPEC-014</span>
</div>
<svg viewBox="0 0 200 200" class="w-full max-w-[240px] block" aria-label="ICS radar chart">
{gridLevels.map((lvl) => (
<polygon
points={axes.map((_, i) => pt(i, R * lvl).join(',')).join(' ')}
fill="none"
stroke="var(--color-ink)"
stroke-width={lvl === 1 ? 2 : 1}
opacity={lvl === 1 ? 1 : 0.2}
/>
))}
{axes.map((_, i) => {
const [x, y] = pt(i, R);
return <line x1={cx} y1={cy} x2={x} y2={y} stroke="var(--color-ink)" stroke-width="1" opacity="0.2" />;
})}
<polygon
points={scorePoints}
fill="var(--color-de)"
fill-opacity="0.55"
stroke="var(--color-ink)"
stroke-width="2"
/>
{axes.map((a, i) => {
const [x, y] = labelPts[i];
return (
<text
x={x}
y={y}
text-anchor="middle"
font-family="JetBrains Mono, monospace"
font-size="7"
letter-spacing="1"
font-weight="900"
fill="var(--color-ink)"
>{a.name} {a.score}</text>
);
})}
</svg>
</div>