Skip to main content
Compendium / Specs & Documents / Analysis & Lint / Spec Diff Viewer
#006

Spec Diff Viewer

Side-by-side diff of two spec versions — red strikethrough on the left, green addition on the right, like a redlined contract.

STABLE DO
#list #real-content #dark-mode-tested
Live
- v1.2.0RETIRED
+ v1.3.0APPROVED
## Intent
## Intent
Authenticate via OAuth.
Authenticate via OAuth.
Session lifetime: 7 days.
Session lifetime: 24 hours.
Rate limit: 100/min.
Rate limit: 10/min per identity.
- [ ] Verified email -> session
- [ ] Verified email -> session
Spec

Intent

Show the textual evolution of a spec from v1.2.0 to v1.3.0 as a side-by-side diff. Removed lines are struck through on the left in attention; added lines appear on the right in success. The visual must feel like a redlined contract.

Acceptance Criteria

  • [ ] Two columns labelled v1.2.0 and v1.3.0
  • [ ] Removed lines: strikethrough on attention background
  • [ ] Added lines: success background
  • [ ] Unchanged lines visible in both columns
  • [ ] Does not require JavaScript

Constraints

  • Pure markup
  • Theme-aware
// spec-diff-viewer.astro
---
import type { CompendiumMeta } from '../../../data/types';

export const meta: CompendiumMeta = {
  id: 6,
  slug: 'spec-diff-viewer',
  name: 'Spec Diff Viewer',
  category: 'specs-documents',
  subcategory: 'analysis-and-lint',
  pillar: 'do',
  description: 'Side-by-side diff of two spec versions — red strikethrough on the left, green addition on the right, like a redlined contract.',
  spec: `## Intent
Show the textual evolution of a spec from v1.2.0 to v1.3.0 as a side-by-side diff. Removed lines are struck through on the left in attention; added lines appear on the right in success. The visual must feel like a redlined contract.

## Acceptance Criteria
- [ ] Two columns labelled v1.2.0 and v1.3.0
- [ ] Removed lines: strikethrough on attention background
- [ ] Added lines: success background
- [ ] Unchanged lines visible in both columns
- [ ] Does not require JavaScript

## Constraints
- Pure markup
- Theme-aware`,
  tags: ['list', 'real-content', 'dark-mode-tested'],
  status: 'stable',
  health: { ics: 100, a11y: 'aa', themed: true, animated: false },
};

type Row = { kind: 'same' | 'change'; left: string; right: string };
const rows: Row[] = [
  { kind: 'same',   left: '## Intent',                       right: '## Intent' },
  { kind: 'same',   left: 'Authenticate via OAuth.',         right: 'Authenticate via OAuth.' },
  { kind: 'change', left: 'Session lifetime: 7 days.',       right: 'Session lifetime: 24 hours.' },
  { kind: 'change', left: 'Rate limit: 100/min.',            right: 'Rate limit: 10/min per identity.' },
  { kind: 'same',   left: '- [ ] Verified email -> session', right: '- [ ] Verified email -> session' },
];
---

<section class="w-full bg-paper text-black border-4 border-black shadow-nb-sm overflow-hidden">
  <header class="grid grid-cols-2 border-b-2 border-black font-mono text-[10px] uppercase tracking-widest">
    <div class="px-3 py-1.5 bg-attention text-white border-e-2 border-black flex justify-between"><span>- v1.2.0</span><span class="opacity-80">RETIRED</span></div>
    <div class="px-3 py-1.5 bg-success text-black flex justify-between"><span>+ v1.3.0</span><span class="opacity-80">APPROVED</span></div>
  </header>
  <div class="divide-y-2 divide-black">
    {rows.map((r) => (
      <div class="grid grid-cols-2">
        <div class:list={[
          'px-3 py-1.5 font-mono text-[10px] leading-snug border-e-2 border-black',
          r.kind === 'change' ? 'bg-attention/20 line-through' : 'opacity-70',
        ]}>{r.left}</div>
        <div class:list={[
          'px-3 py-1.5 font-mono text-[10px] leading-snug',
          r.kind === 'change' ? 'bg-success/30 font-black' : 'opacity-70',
        ]}>{r.right}</div>
      </div>
    ))}
  </div>
</section>