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>