Keploy
A design language system and frontend architecture for an open-source API testing platform.
Keploy records real API traffic and replays it as deterministic tests and mocks. The product surface had grown organically across a web console, a VS Code extension, and docs — three codebases, three visual dialects, no shared source of truth. I owned the design language system end-to-end: token pipeline, component library, and the redesign of the test-run console that sits on top of it.
Developer tools earn trust through precision. When a diff viewer renders a flaky timestamp as a test failure, or two surfaces disagree about what "warning orange" means, users stop believing the tool — and an API testing platform lives or dies on believability.
The audit found 14 button variants, 9 shades of the brand orange, and three different table implementations — one of which locked up above ~2,000 rows. Contributors (this is an open-source project, so most frontend PRs come from strangers) had no way to know which pattern was canonical. The brief: one system rigorous enough that a first-time contributor ships on-brand UI by default, without a designer in the loop.
Tokens as the contract
Everything starts from a three-tier token architecture — primitives → semantic aliases → component tokens — authored once in JSON and compiled by Style Dictionary to CSS custom properties, a typed TypeScript map, and a Tailwind preset. No surface consumes a raw hex value; the semantic layer is the only public API, so retheming (including the dark-first console theme) is a data change, not a refactor.
The typed map matters more than it sounds: token names are string-literal types, so a contributor referencing a token that does not exist gets a compile error, not a silently wrong color in production.
// tokens compile to CSS vars + a literal-typed map
export const token = {
'surface.raised': 'var(--kp-surface-raised)',
'status.pass': 'var(--kp-status-pass)',
'status.flaky': 'var(--kp-status-flaky)',
'diff.added.bg': 'var(--kp-diff-added-bg)',
} as const satisfies TokenMap;
// ✗ token['status.sucess'] — compile error, not a wrong color Headless core, styled shell
The library is 38 components in two layers: Radix primitives handle focus management, ARIA wiring, and collision-aware positioning; a thin styled shell binds them to the token system. Compound-component APIs keep composition in JSX rather than in prop soup — a test-run card is <TestRun> wrapping <TestRun.Status>, <TestRun.Diff>, <TestRun.Meta>, not a component with 30 props.
Every component ships with Storybook coverage, an interaction test, and a docs page generated from the same source. CI runs axe on every story; a PR that regresses contrast or keyboard reachability fails before review.
Making replayed traffic legible
The core screen is a diff between a recorded API response and the replayed one. Raw JSON diffs are hostile: a 4,000-line payload with one meaningful change and forty noisy ones (timestamps, UUIDs, server nonces). The redesigned diff viewer classifies noise declaratively — fields matched by the project's noise config render de-emphasized and excluded from the pass/fail verdict, with a one-click affordance to promote a flaky field into the config.
Test runs routinely contain thousands of assertions, so the run table is virtualized (TanStack Virtual) with stable row heights and keyboard-first navigation: j/k row traversal, enter to expand a diff, and every state deep-linkable. A 10,000-row run scrolls at 60fps where the old table died at 2,000.
Migration without a flag day
Rewrites stall; strangler migrations ship. Legacy screens got the token CSS variables injected at the root, so old components inherited the new palette immediately, and a codemod swapped the highest-traffic primitives (Button, Input, Table) in bulk. An ESLint rule flags raw hex values and legacy imports on every new PR, so the system tightens instead of eroding — the ratchet, not the review comment, guards consistency.
Dummy numbers, real shape: the system cut frontend build time for new screens by roughly 60%, and contributor UI PRs stopped needing design review in the common case — the tokens, lint rules, and CI gates carry that review instead. The console redesign shipped with the v2.3 release and became the default surface for new users.
- 38
- components in the system 100% Storybook + axe coverage
- −62%
- UI build time for new screens measured across 6 releases
- 10k
- rows at 60fps virtualized run table (was ~2k)
- 0
- raw hex values in app code enforced by lint ratchet