Cadence is the metronome of a mature process. This indicator plots the last N expected beats; struck ticks are filled, missed ticks are hollow with a danger mark. Drift is visible without a number.
Acceptance Criteria
[ ] 14-cell grid of recent beats
[ ] Hit cells filled with pillar token; missed cells flagged
[ ] Hit-rate readout in tabular-nums
[ ] Negative: does NOT animate by default
Constraints
Pure CSS
Theme-aware
// process-cadence-pulse.astro
---
import type { CompendiumMeta } from '../../../data/types';
export const meta: CompendiumMeta = {
id: 444,
slug: 'process-cadence-pulse',
name: 'Process Cadence Pulse',
category: 'maturity-process',
subcategory: 'indicators',
pillar: 'de',
description: 'Tick-grid of recent process beats — daily standups, weekly reviews — showing cadence health at a glance.',
spec: `## Intent
Cadence is the metronome of a mature process. This indicator plots the last N expected beats; struck ticks are filled, missed ticks are hollow with a danger mark. Drift is visible without a number.
## Acceptance Criteria
- [ ] 14-cell grid of recent beats
- [ ] Hit cells filled with pillar token; missed cells flagged
- [ ] Hit-rate readout in tabular-nums
- [ ] Negative: does NOT animate by default
## Constraints
- Pure CSS
- Theme-aware`,
tags: ['chart', 'real-content', 'dark-mode-tested', 'responsive'],
status: 'stable',
health: { ics: 87, a11y: 'aa', themed: true, animated: false },
};
const beats = [1,1,1,0,1,1,1,1,0,1,1,1,1,1];
const hits = beats.filter((b) => b).length;
const rate = Math.round((hits / beats.length) * 100);
---
<div data-this-component class="w-full bg-paper text-black border-4 border-black overflow-hidden">
<header class="flex items-baseline justify-between px-3 py-2 border-b-2 border-black">
<span class="font-mono text-[10px] uppercase tracking-widest opacity-60">// cadence · 14 beats</span>
<span class="font-mono text-[10px] uppercase tracking-widest tabular-nums">{rate}%</span>
</header>
<div class="p-3">
<ol class="grid grid-cols-7 gap-1.5" aria-label="Recent cadence beats">
{beats.map((b, i) => (
<li
class:list={[
'aspect-square border-2 border-black flex items-center justify-center font-black text-[10px]',
b ? 'bg-de text-black' : 'bg-paper text-danger',
]}
aria-label={b ? `Beat ${i + 1} hit` : `Beat ${i + 1} missed`}
>{b ? '' : '×'}</li>
))}
</ol>
<div class="mt-3 pt-3 border-t-2 border-black grid grid-cols-3 gap-2 font-mono text-[9px] uppercase tracking-widest">
<div class="min-w-0"><span class="opacity-60 block">hits</span><span class="font-black text-[11px] tabular-nums">{hits}</span></div>
<div class="min-w-0"><span class="opacity-60 block">missed</span><span class="font-black text-[11px] tabular-nums">{beats.length - hits}</span></div>
<div class="min-w-0 text-end"><span class="opacity-60 block">streak</span><span class="font-black text-[11px] tabular-nums">5</span></div>
</div>
</div>
</div>
</content>
</invoke>