Skip to main content
Compendium / Specs & Documents / Lifecycle / Spec Index
#013

Spec Index

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 / INDEX 123 LINES
  1. I Intent 012
  2. II Acceptance Criteria 028
  3. III Constraints 014
  4. IV Failure Modes 022
  5. V Threat Model 031
  6. VI Out-of-Scope 009
  7. VII Approvals & Lineage 007
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>