Table-of-contents for a spec document — section names with line counts and dot leaders, like a legal index.
STABLE DO
#list
#navigation
#real-content
#dark-mode-tested
Live
SPEC-014 / INDEX123 LINES
IIntent012
IIAcceptance Criteria028
IIIConstraints014
IVFailure Modes022
VThreat Model031
VIOut-of-Scope009
VIIApprovals & Lineage007
Spec
Intent
Render the table-of-contents of a long-form spec. Section names, mono line counts, classic dot-leader rule between them. The component looks like the front matter of a printed statute — emphasising that DoCoDeGo specs are documents, not tickets.
Acceptance Criteria
[ ] At least 5 sections listed
[ ] Dot leaders between section name and line count
[ ] Mono line-count column right-aligned
[ ] Section ordinals in roman or mono numerals
[ ] Does not require JS to render leaders
Constraints
Pure CSS
Theme-aware
// spec-index.astro
---
import type { CompendiumMeta } from '../../../data/types';
export const meta: CompendiumMeta = {
id: 13,
slug: 'spec-index',
name: 'Spec Index',
category: 'specs-documents',
subcategory: 'lifecycle',
pillar: 'do',
description: 'Table-of-contents for a spec document — section names with line counts and dot leaders, like a legal index.',
spec: `## Intent
Render the table-of-contents of a long-form spec. Section names, mono line counts, classic dot-leader rule between them. The component looks like the front matter of a printed statute — emphasising that DoCoDeGo specs are documents, not tickets.
## Acceptance Criteria
- [ ] At least 5 sections listed
- [ ] Dot leaders between section name and line count
- [ ] Mono line-count column right-aligned
- [ ] Section ordinals in roman or mono numerals
- [ ] Does not require JS to render leaders
## Constraints
- Pure CSS
- Theme-aware`,
tags: ['list', 'navigation', 'real-content', 'dark-mode-tested'],
status: 'stable',
health: { ics: 100, a11y: 'aa', themed: true, animated: false },
};
const sections = [
{ n: 'I', name: 'Intent', lines: 12 },
{ n: 'II', name: 'Acceptance Criteria', lines: 28 },
{ n: 'III', name: 'Constraints', lines: 14 },
{ n: 'IV', name: 'Failure Modes', lines: 22 },
{ n: 'V', name: 'Threat Model', lines: 31 },
{ n: 'VI', name: 'Out-of-Scope', lines: 9 },
{ n: 'VII', name: 'Approvals & Lineage', lines: 7 },
];
---
<section class="w-full bg-paper text-black border-4 border-black shadow-nb-sm">
<header class="px-3 py-2 border-b-2 border-black flex items-baseline justify-between">
<span class="font-mono text-[10px] uppercase tracking-widest font-black">SPEC-014 / INDEX</span>
<span class="font-mono text-[9px] uppercase tracking-widest opacity-60">123 LINES</span>
</header>
<ol class="px-3 py-2 space-y-0.5">
{sections.map((s) => (
<li class="flex items-baseline gap-2 text-[11px]">
<span class="font-mono uppercase tracking-widest opacity-60 w-8 shrink-0">{s.n}</span>
<span class="whitespace-nowrap">{s.name}</span>
<span class="flex-1 border-b border-dotted border-black/40 mx-1 translate-y-[-3px]"></span>
<span class="font-mono tabular-nums text-[10px] opacity-70 shrink-0">{String(s.lines).padStart(3, '0')}</span>
</li>
))}
</ol>
</section>