Case study · dashboard and design system
Pulseboard
Operations analytics for multi-location clinics, built as a production-grade frontend showcase.
- React 19
- RTK Query
- Tailwind 4
- visx
- TanStack Table
- Storybook 10
- MSW
- Cypress

- Rows, virtualized
52,555
23 to 34 in the DOM - p95 frame
10.2 ms
Budget 16.7 ms - Tests
264
4 tiers - CI run
< 5 min
4 jobs
How it works
The dashboard sends fetch requests that an MSW service worker intercepts and answers from a seeded mock API. The UI package supplies components to the dashboard, and Storybook runs its stories as tests.
Requests
Dashboard app
React 19, RTK Query
MSW worker
Intercepts fetch
Mock API
Seeded, deterministic
Design system
Storybook
Stories as tests
UI package
Semantic tokens
Dashboard app
React 19, RTK Query
- RTK Query owns server state
- Slices own URL-synced filters and API health
- Seeded streams per location, metric and date
- Daily totals roll up from the same call records
Architecture decisions
- ADR-0001: Vite SPA over Next.js (opens in a new tab)
- ADR-0002: MSW is the only backend (opens in a new tab)
- ADR-0003: Semantic tokens, CSS-first Tailwind 4 (opens in a new tab)
- ADR-0004: Stories are the component test suite (opens in a new tab)
- ADR-0005: Internal packages ship source, not dist (opens in a new tab)
- ADR-0006: RTK Query for server state; the local-state rule (opens in a new tab)
- ADR-0007: visx over Recharts (opens in a new tab)
Design system
Palette wiped
Only semantic tokens compile; bg-blue-500 fails the build.
Contrast pairs tested
A unit test checks each token pair against WCAG.
Chart hues checked
A colour-vision deficiency check guards chart colours.
Stories are tests
Play functions and axe run in headless Chromium and fail the build.
Theme before paint
The data-theme attribute is set before first paint, so no flash.
Tabular numbers
Every figure uses mono tabular-nums, so columns never jitter.
About 20 components
Pruned on purpose: no Toast, no Breadcrumbs.
Charts on currentColor
SVG attributes reject var(), so a wrapper class sets the colour.
Performance
Virtualized table
Run it (opens in a new tab)52,555 rows, 110 days × 12 locations. Measured by Cypress on a production build.
- Mount
- 64.7 ms
- Rows in the DOM
- 23–34
Bundle budgets, enforced in CI
Set with size-limit. Used of budget:
- App entry229 / 360 KB
- Overview, visx19 / 30 KB
- Calls, TanStack2 / 10 KB
- CSS8 / 12 KB
Date picker and Radix stay in the shell on purpose.
- Stable query args via createSelector
- No React.memo, by profiling
- Zero-CLS skeletons
- Shimmer over stale data
Failure modes you can trigger
- Slow
- Degraded
- Outage
- Per-widget error boundaries
- Retries with backoff
- Degraded banner over cached data
- Empty states
Testing and CI
Unit
Vitest, pure logic
Behavioural
jsdom
Behavioural
Real browser via Storybook
Smoke
Cypress, 9 e2e specs
- quality
- storybook-tests
- build
- e2e
- Lines ≥ 90%
- Branches ≥ 85%
Honest limits
- Data is fictional and seeded
- No auth and no server
- MSW ships in the production bundle, about 40 KB (ADR-0002 (opens in a new tab))
- Coverage exclusions are documented (ADR-0004 (opens in a new tab))