# Metrics Components Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add StatusText primitive, Card title prop, and KpiStrip composite to eliminate ~320 lines of duplicated KPI layout code across Dashboard, Routes, and AgentHealth pages. **Architecture:** StatusText is a tiny inline span primitive with semantic color variants. Card gets an optional title prop for a header row. KpiStrip is a new composite that renders a horizontal row of metric cards with labels, values, trends, subtitles, and sparklines. **Tech Stack:** React, TypeScript, CSS Modules, Vitest, React Testing Library **Spec:** `docs/superpowers/specs/2026-03-24-mock-deviations-design.md` (Sections 1, 5, 6) --- ## File Map | Action | File | Task | |--------|------|------| | CREATE | `src/design-system/primitives/StatusText/StatusText.tsx` | 1 | | CREATE | `src/design-system/primitives/StatusText/StatusText.module.css` | 1 | | CREATE | `src/design-system/primitives/StatusText/StatusText.test.tsx` | 1 | | MODIFY | `src/design-system/primitives/index.ts` | 1 | | MODIFY | `src/design-system/primitives/Card/Card.tsx` | 2 | | MODIFY | `src/design-system/primitives/Card/Card.module.css` | 2 | | CREATE | `src/design-system/primitives/Card/Card.test.tsx` | 2 | | CREATE | `src/design-system/composites/KpiStrip/KpiStrip.tsx` | 3 | | CREATE | `src/design-system/composites/KpiStrip/KpiStrip.module.css` | 3 | | CREATE | `src/design-system/composites/KpiStrip/KpiStrip.test.tsx` | 3 | | MODIFY | `src/design-system/composites/index.ts` | 3 | --- ## Task 1: StatusText Primitive **Files:** - CREATE `src/design-system/primitives/StatusText/StatusText.tsx` - CREATE `src/design-system/primitives/StatusText/StatusText.module.css` - CREATE `src/design-system/primitives/StatusText/StatusText.test.tsx` - MODIFY `src/design-system/primitives/index.ts` ### Step 1.1 — Write test (RED) - [ ] Create `src/design-system/primitives/StatusText/StatusText.test.tsx`: ```tsx import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { StatusText } from './StatusText' describe('StatusText', () => { it('renders children text', () => { render(OK) expect(screen.getByText('OK')).toBeInTheDocument() }) it('renders as a span element', () => { render(OK) expect(screen.getByText('OK').tagName).toBe('SPAN') }) it('applies variant class', () => { render(BREACH) expect(screen.getByText('BREACH')).toHaveClass('error') }) it('applies bold class when bold=true', () => { render(HIGH) expect(screen.getByText('HIGH')).toHaveClass('bold') }) it('does not apply bold class by default', () => { render(idle) expect(screen.getByText('idle')).not.toHaveClass('bold') }) it('accepts custom className', () => { render(active) expect(screen.getByText('active')).toHaveClass('custom') }) it('renders all variant classes correctly', () => { const { rerender } = render(text) expect(screen.getByText('text')).toHaveClass('success') rerender(text) expect(screen.getByText('text')).toHaveClass('warning') rerender(text) expect(screen.getByText('text')).toHaveClass('error') rerender(text) expect(screen.getByText('text')).toHaveClass('running') rerender(text) expect(screen.getByText('text')).toHaveClass('muted') }) }) ``` - [ ] Run test — expect FAIL (module not found): ```bash npx vitest run src/design-system/primitives/StatusText/StatusText.test.tsx ``` ### Step 1.2 — Implement (GREEN) - [ ] Create `src/design-system/primitives/StatusText/StatusText.module.css`: ```css .statusText { /* Inherits font-size from parent */ } .success { color: var(--success); } .warning { color: var(--warning); } .error { color: var(--error); } .running { color: var(--running); } .muted { color: var(--text-muted); } .bold { font-weight: 600; } ``` - [ ] Create `src/design-system/primitives/StatusText/StatusText.tsx`: ```tsx import styles from './StatusText.module.css' import type { ReactNode } from 'react' interface StatusTextProps { variant: 'success' | 'warning' | 'error' | 'running' | 'muted' bold?: boolean children: ReactNode className?: string } export function StatusText({ variant, bold = false, children, className }: StatusTextProps) { const classes = [ styles.statusText, styles[variant], bold ? styles.bold : '', className ?? '', ].filter(Boolean).join(' ') return {children} } ``` - [ ] Run test — expect PASS: ```bash npx vitest run src/design-system/primitives/StatusText/StatusText.test.tsx ``` ### Step 1.3 — Barrel export - [ ] Add to `src/design-system/primitives/index.ts` (alphabetical, after `StatusDot`): ```ts export { StatusText } from './StatusText/StatusText' ``` ### Step 1.4 — Commit ```bash git add src/design-system/primitives/StatusText/ src/design-system/primitives/index.ts git commit -m "feat: add StatusText primitive with semantic color variants" ``` --- ## Task 2: Card Title Extension **Files:** - MODIFY `src/design-system/primitives/Card/Card.tsx` - MODIFY `src/design-system/primitives/Card/Card.module.css` - CREATE `src/design-system/primitives/Card/Card.test.tsx` ### Step 2.1 — Write test (RED) - [ ] Create `src/design-system/primitives/Card/Card.test.tsx`: ```tsx import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { Card } from './Card' describe('Card', () => { it('renders children', () => { render(Card content) expect(screen.getByText('Card content')).toBeInTheDocument() }) it('renders title when provided', () => { render(content) expect(screen.getByText('Section Title')).toBeInTheDocument() }) it('does not render title header when title is omitted', () => { const { container } = render(content) expect(container.querySelector('.titleHeader')).not.toBeInTheDocument() }) it('wraps children in body div when title is provided', () => { render(body text) const body = screen.getByText('body text').closest('div') expect(body).toHaveClass('body') }) it('renders with accent and title together', () => { const { container } = render( details ) expect(container.firstChild).toHaveClass('accent-success') expect(screen.getByText('Status')).toBeInTheDocument() expect(screen.getByText('details')).toBeInTheDocument() }) it('accepts className prop', () => { const { container } = render(content) expect(container.firstChild).toHaveClass('custom') }) it('renders children directly when no title (no wrapper div)', () => { const { container } = render(hi) expect(screen.getByTestId('direct')).toBeInTheDocument() // Should not have a body wrapper when there is no title expect(container.querySelector('.body')).not.toBeInTheDocument() }) }) ``` - [ ] Run test — expect FAIL (title prop not supported yet, body class missing): ```bash npx vitest run src/design-system/primitives/Card/Card.test.tsx ``` ### Step 2.2 — Implement (GREEN) - [ ] Add to `src/design-system/primitives/Card/Card.module.css` (append after existing rules): ```css .titleHeader { padding: 12px 16px; border-bottom: 1px solid var(--border-subtle); } .titleText { font-size: 11px; text-transform: uppercase; font-family: var(--font-mono); font-weight: 600; color: var(--text-secondary); letter-spacing: 0.5px; margin: 0; } .body { padding: 16px; } ``` - [ ] Replace `src/design-system/primitives/Card/Card.tsx` with: ```tsx import styles from './Card.module.css' import type { ReactNode } from 'react' interface CardProps { children: ReactNode accent?: 'amber' | 'success' | 'warning' | 'error' | 'running' | 'none' title?: string className?: string } export function Card({ children, accent = 'none', title, className }: CardProps) { const classes = [ styles.card, accent !== 'none' ? styles[`accent-${accent}`] : '', className ?? '', ].filter(Boolean).join(' ') return (
{title && (

{title}

)} {title ?
{children}
: children}
) } ``` - [ ] Run test — expect PASS: ```bash npx vitest run src/design-system/primitives/Card/Card.test.tsx ``` ### Step 2.3 — Commit ```bash git add src/design-system/primitives/Card/ git commit -m "feat: add optional title prop to Card primitive" ``` --- ## Task 3: KpiStrip Composite **Files:** - CREATE `src/design-system/composites/KpiStrip/KpiStrip.tsx` - CREATE `src/design-system/composites/KpiStrip/KpiStrip.module.css` - CREATE `src/design-system/composites/KpiStrip/KpiStrip.test.tsx` - MODIFY `src/design-system/composites/index.ts` ### Step 3.1 — Write test (RED) - [ ] Create `src/design-system/composites/KpiStrip/KpiStrip.test.tsx`: ```tsx import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import { KpiStrip } from './KpiStrip' const sampleItems = [ { label: 'Total Throughput', value: '12,847', trend: { label: '\u25B2 +8%', variant: 'success' as const }, subtitle: '35.7 msg/s', sparkline: [44, 46, 45, 47, 48, 46, 47], borderColor: 'var(--amber)', }, { label: 'Error Rate', value: '0.42%', trend: { label: '\u25BC -0.1%', variant: 'success' as const }, subtitle: '54 errors / 12,847 total', }, { label: 'Active Routes', value: 14, }, ] describe('KpiStrip', () => { it('renders all items', () => { render() expect(screen.getByText('Total Throughput')).toBeInTheDocument() expect(screen.getByText('Error Rate')).toBeInTheDocument() expect(screen.getByText('Active Routes')).toBeInTheDocument() }) it('renders labels and values', () => { render() expect(screen.getByText('12,847')).toBeInTheDocument() expect(screen.getByText('0.42%')).toBeInTheDocument() expect(screen.getByText('14')).toBeInTheDocument() }) it('renders trend with correct text', () => { render() expect(screen.getByText('\u25B2 +8%')).toBeInTheDocument() expect(screen.getByText('\u25BC -0.1%')).toBeInTheDocument() }) it('applies variant class to trend', () => { render() const trend = screen.getByText('\u25B2 +8%') expect(trend).toHaveClass('trendSuccess') }) it('hides trend when omitted', () => { render() // Should only have label and value, no trend element const card = screen.getByText('Routes').closest('[class*="kpiCard"]') expect(card?.querySelector('[class*="trend"]')).toBeNull() }) it('renders subtitle', () => { render() expect(screen.getByText('35.7 msg/s')).toBeInTheDocument() expect(screen.getByText('54 errors / 12,847 total')).toBeInTheDocument() }) it('renders sparkline when data provided', () => { const { container } = render() // Sparkline renders an SVG with aria-hidden const svgs = container.querySelectorAll('svg[aria-hidden="true"]') expect(svgs.length).toBe(1) // Only first item has sparkline }) it('accepts className prop', () => { const { container } = render() expect(container.firstChild).toHaveClass('custom') }) it('handles empty items array', () => { const { container } = render() expect(container.firstChild).toBeInTheDocument() // No cards rendered expect(container.querySelectorAll('[class*="kpiCard"]').length).toBe(0) }) it('uses default border color when borderColor is omitted', () => { const { container } = render( ) const card = container.querySelector('[class*="kpiCard"]') expect(card).toBeInTheDocument() // The default borderColor is applied via inline style expect(card).toHaveStyle({ '--kpi-border-color': 'var(--amber)' }) }) it('applies custom borderColor', () => { const { container } = render( ) const card = container.querySelector('[class*="kpiCard"]') expect(card).toHaveStyle({ '--kpi-border-color': 'var(--error)' }) }) it('renders trend with muted variant by default', () => { render( ) const trend = screen.getByText('~ stable') expect(trend).toHaveClass('trendMuted') }) }) ``` - [ ] Run test — expect FAIL (module not found): ```bash npx vitest run src/design-system/composites/KpiStrip/KpiStrip.test.tsx ``` ### Step 3.2 — Implement (GREEN) - [ ] Create `src/design-system/composites/KpiStrip/KpiStrip.module.css`: ```css /* KpiStrip — horizontal row of metric cards */ .kpiStrip { display: grid; gap: 12px; margin-bottom: 20px; } /* ── Individual card ─────────────────────────────────────────────── */ .kpiCard { background: var(--bg-surface); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); padding: 16px 18px 12px; box-shadow: var(--shadow-card); position: relative; overflow: hidden; transition: box-shadow 0.15s; } .kpiCard:hover { box-shadow: var(--shadow-md); } /* Top gradient border — color driven by CSS custom property */ .kpiCard::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, var(--kpi-border-color), transparent); } /* ── Label ───────────────────────────────────────────────────────── */ .label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-muted); margin-bottom: 6px; } /* ── Value row ───────────────────────────────────────────────────── */ .valueRow { display: flex; align-items: baseline; gap: 6px; margin-bottom: 4px; } .value { font-family: var(--font-mono); font-size: 26px; font-weight: 600; line-height: 1.2; color: var(--text-primary); } /* ── Trend ────────────────────────────────────────────────────────── */ .trend { font-family: var(--font-mono); font-size: 11px; display: inline-flex; align-items: center; gap: 2px; margin-left: auto; } .trendSuccess { color: var(--success); } .trendWarning { color: var(--warning); } .trendError { color: var(--error); } .trendMuted { color: var(--text-muted); } /* ── Subtitle ─────────────────────────────────────────────────────── */ .subtitle { font-size: 11px; color: var(--text-muted); margin-top: 2px; } /* ── Sparkline ────────────────────────────────────────────────────── */ .sparkline { margin-top: 8px; height: 32px; } ``` - [ ] Create `src/design-system/composites/KpiStrip/KpiStrip.tsx`: ```tsx import styles from './KpiStrip.module.css' import { Sparkline } from '../../primitives/Sparkline/Sparkline' import type { CSSProperties, ReactNode } from 'react' export interface KpiItem { label: string value: string | number trend?: { label: string; variant?: 'success' | 'warning' | 'error' | 'muted' } subtitle?: string sparkline?: number[] borderColor?: string } export interface KpiStripProps { items: KpiItem[] className?: string } const trendClassMap: Record = { success: styles.trendSuccess, warning: styles.trendWarning, error: styles.trendError, muted: styles.trendMuted, } export function KpiStrip({ items, className }: KpiStripProps) { const stripClasses = [styles.kpiStrip, className ?? ''].filter(Boolean).join(' ') const gridStyle: CSSProperties = { gridTemplateColumns: items.length > 0 ? `repeat(${items.length}, 1fr)` : undefined, } return (
{items.map((item) => { const borderColor = item.borderColor ?? 'var(--amber)' const cardStyle: CSSProperties & Record = { '--kpi-border-color': borderColor, } const trendVariant = item.trend?.variant ?? 'muted' const trendClass = trendClassMap[trendVariant] ?? styles.trendMuted return (
{item.label}
{item.value} {item.trend && ( {item.trend.label} )}
{item.subtitle && (
{item.subtitle}
)} {item.sparkline && item.sparkline.length >= 2 && (
)}
) })}
) } ``` - [ ] Run test — expect PASS: ```bash npx vitest run src/design-system/composites/KpiStrip/KpiStrip.test.tsx ``` ### Step 3.3 — Barrel export - [ ] Add to `src/design-system/composites/index.ts` (alphabetical, after `GroupCard`): ```ts export { KpiStrip } from './KpiStrip/KpiStrip' export type { KpiItem, KpiStripProps } from './KpiStrip/KpiStrip' ``` ### Step 3.4 — Commit ```bash git add src/design-system/composites/KpiStrip/ src/design-system/composites/index.ts git commit -m "feat: add KpiStrip composite for reusable metric card rows" ``` --- ## Task 4: Barrel Exports Verification & Full Test Run **Files:** - VERIFY `src/design-system/primitives/index.ts` (modified in Task 1) - VERIFY `src/design-system/composites/index.ts` (modified in Task 3) ### Step 4.1 — Verify barrel exports - [ ] Confirm `src/design-system/primitives/index.ts` contains: ```ts export { StatusText } from './StatusText/StatusText' ``` - [ ] Confirm `src/design-system/composites/index.ts` contains: ```ts export { KpiStrip } from './KpiStrip/KpiStrip' export type { KpiItem, KpiStripProps } from './KpiStrip/KpiStrip' ``` ### Step 4.2 — Run full test suite - [ ] Run all tests to confirm nothing is broken: ```bash npx vitest run ``` - [ ] Verify zero failures. If any test fails, fix and re-run before proceeding. ### Step 4.3 — Final commit (if barrel-only changes remain) If the barrel export changes were not already committed in their respective tasks: ```bash git add src/design-system/primitives/index.ts src/design-system/composites/index.ts git commit -m "chore: add StatusText and KpiStrip to barrel exports" ``` --- ## Summary of Expected Barrel Export Additions **`src/design-system/primitives/index.ts`** — insert after `StatusDot` line: ```ts export { StatusText } from './StatusText/StatusText' ``` **`src/design-system/composites/index.ts`** — insert after `GroupCard` line: ```ts export { KpiStrip } from './KpiStrip/KpiStrip' export type { KpiItem, KpiStripProps } from './KpiStrip/KpiStrip' ``` --- ## Test Commands Quick Reference | Scope | Command | |-------|---------| | StatusText only | `npx vitest run src/design-system/primitives/StatusText/StatusText.test.tsx` | | Card only | `npx vitest run src/design-system/primitives/Card/Card.test.tsx` | | KpiStrip only | `npx vitest run src/design-system/composites/KpiStrip/KpiStrip.test.tsx` | | All tests | `npx vitest run` |