Case study
One design system, every brand
How it was built, what the numbers are, and what they do not cover.
Opening
The Thursday problem
A concrete moment: a new client signs on Thursday and their branded app has to be in pilot on Monday. One paragraph, first person, no system vocabulary.
Try it
Type a colour. Get a brand.
Six inputs become a light and dark theme that passes WCAG 2.2 AA. Every correction the solver made is listed, so you can see where your colour was and where it had to go.
Brand inputs
Live preview
After fees
Net revenue
- This period
- Previous period
28 points, Sep 3 to Sep 30. This period: low $4,390, high $7,820, last $7,820. Previous period: low $4,020, high $5,740, last $5,740.
| Date | This period | Previous period |
|---|---|---|
| Sep 3 | $4,390 | $4,110 |
| Sep 4 | $4,720 | $4,020 |
| Sep 5 | $4,960 | $4,290 |
| Sep 6 | $5,210 | $4,380 |
| Sep 7 | $4,870 | $4,210 |
| Sep 8 | $5,030 | $4,460 |
| Sep 9 | $5,390 | $4,520 |
| Sep 10 | $5,620 | $4,390 |
| Sep 11 | $5,480 | $4,610 |
| Sep 12 | $5,810 | $4,730 |
| Sep 13 | $6,040 | $4,680 |
| Sep 14 | $5,720 | $4,820 |
| Sep 15 | $5,960 | $4,910 |
| Sep 16 | $6,210 | $4,760 |
| Sep 17 | $6,480 | $4,990 |
| Sep 18 | $6,130 | $5,120 |
| Sep 19 | $6,390 | $5,040 |
| Sep 20 | $6,670 | $5,210 |
| Sep 21 | $6,420 | $5,170 |
| Sep 22 | $6,810 | $5,330 |
| Sep 23 | $7,040 | $5,290 |
| Sep 24 | $6,720 | $5,460 |
| Sep 25 | $7,110 | $5,510 |
| Sep 26 | $7,380 | $5,380 |
| Sep 27 | $7,020 | $5,620 |
| Sep 28 | $7,290 | $5,690 |
| Sep 29 | $7,560 | $5,580 |
| Sep 30 | $7,820 | $5,740 |
September by channel
- Card payments
- $28,460
- Bank transfers
- $9,870
- Refunds
- $1,240
Get paid the day you invoice
Instant payouts move card sales into Operating within minutes, for 1% a payout.
- Minutes, not two daysCard sales land as soon as they settle.
- Same checks as todayEvery payout still goes through review.
- 1% a payout, capped at $15Only on the payouts you speed up.
Could have landed early in September
$28,460Accounts
$289,690 across 5 accounts
- OperatingChecking ·· 4821$84,250+5.2%
- Tax reserveSavings ·· 3306$32,600-6.1%
- Reserve fundTreasury · 4.1% yield$124,800+3.4%
- Card salesSettling · lands Oct 1$6,840+12.6%
- PayrollChecking ·· 7730$41,200+2.1%
Spend limits
Team cards reset on October 1.
Approvals
Payouts over $2,500 need a yes from one of them.
Scheduled
3 payments go out in October.
October 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Activity
Across all accounts, newest first.
- Payout to Lumen Print Co.Today, 09:42−$4,800.00Needs approval
- Invoice #1042 paidToday, 08:15+$12,650.00Completed
- Card charge at Northline AirYesterday−$612.40Declined
- Payroll, 14 peopleFri, Oct 2−$38,420.00Scheduled
Alerts
Push and email, as it happens.
Same code
Three brands, one component library
A brand has to be data, not code. Nothing below differs in code, and no component ever learns which brand it is rendering — there is no tenant id anywhere in packages/react.
VelaNeobanken-IN
HarborInsuranceen-GB
Qamarبقالة ومكافآتar-AE-u-nu-arab · RTL
Evidence
Numbers, and how to reproduce them
A design system that claims accessibility and cannot show it is a brochure. Each figure is read at build time from the file its command writes — none is typed by hand.
- 100%Contrast checks that pass118,000 checks across 1,000 random brands, 0 failures
pnpm test:themes - 6Inputs that drive a brand6 brands ship this way, each one file; no file under packages/react changes to add one
cat tenants/<id>/brand.json - 64% → 88%Agent runs fully on-systemclaude-sonnet-5, 100 runs: no context, then MCP + AGENTS.md
node evals/report.mjs --iteration 2 - 99.8 / 100Drift score, reference blocks1,150 places checked in 8,003 lines, 1 finding
pnpm drift apps/docs/blocks - 58Componentseach with a meta.json that drives its docs page, the MCP server and the SDUI schema
pnpm check:meta
Judgement
Five decisions that mattered
Every architectural call is an ADR, and every ADR records who made it — Anuj, the agent recommending and Anuj accepting, or the agent alone pending review. There are 48, and not one is anonymous. 6 are still waiting on his review, and they say so.
- ADR-006
Let a brand pick its colours, or guarantee the result is legible. You cannot promise both.
Call. Generate the ramps in OKLCH and run a solver that moves a role until the pair passes — then record what it moved.
Consequence. A brand can hand over any hex. Nothing ships that fails, and the page shows the correction rather than hiding it.
- ADR-011
Copy-in components get adopted fastest. Versioned packages are the only way to deprecate anything.
Call. Built both from one source, shipped both — then withdrew the registry four weeks later and kept npm.
Consequence. One install route to document and test. The reversal is in the ADR as a revision, not a rewrite.
- ADR-012
Overlays portal to the end of the document, so they leave the element whose brand and direction they belong to.
Call. Every overlay copies the nearest scope’s data attributes, dir and lang as it opens; the locale drives direction, not a dir prop.
Consequence. A dialog opened from an Arabic tenant is Arabic. The helper is repeated in each overlay file so a copied file still works.
- ADR-021
Button’s danger was a variant, which put status in the same prop as emphasis. Fixing it breaks everyone using it.
Call. Deprecate in a minor, remove at 1.0 only, ship a codemod with the RFC, and warn in development.
Consequence. The governance claim has one real deprecation behind it instead of a policy document.
- ADR-019
Every design system is asked for native components. Building them badly is worse than not building them.
Call. Said no. Shipped a server-driven UI schema and native token files instead, and wrote down that there are no native components.
Consequence. The mobile claim is small and true. Scope held.
In practice
Rolling this out to 40 designers and 300 engineers
- Q1
Audit, and earn the right
Inventory what exists across the estate and count it, not eyeball it. Pick the ten components that appear everywhere. Publish the audit before proposing anything.
Measured by. Baseline: components in use, duplicates per pattern, contrast failures per surface.
- Q2
Foundations, with one team
Tokens and the theme engine, adopted by a single product team who ship with it. No library-wide rollout until one real surface runs on it.
Measured by. That team’s drift score, and the time it takes them to re-skin.
- Q3
The top ten, and the governance that keeps them
Ship the ten, with RFCs, deprecations and codemods from day one. A design system without a deprecation path becomes a fork within a year.
Measured by. Adoption per component, and the number of overrides written against them.
- Q4
Adoption as a programme, not a memo
Office hours, a contribution path, and a lint rule that suggests the right component rather than only naming the wrong one. Measure the system, not the designers.
Measured by. Drift score across the estate, and how many teams ship without asking us anything.
Limits
What this does not prove
- No users. Nobody has adopted this. Every number measures the system against itself, not a team against a deadline.
- One maintainer. The governance is real and runnable, but it has never survived two people disagreeing about a breaking change.
- RTL is table stakes now. shadcn/ui calls its own RTL first-class and Untitled UI React ships logical properties throughout. Doing it from day one was right; it is not a difference.
- Tenant copy is mine, not a native speaker’s. The Hindi and Arabic are marked as drafts in the interface wherever they appear, and stay marked until someone fluent reads them.
- The agent eval is one model, one task family, 100 runs. It shows a direction, not a law. Its first run reported 0% against 70%, which would have been a far better headline; it was a fault in the harness. Both runs are in the repository and the invalid one is still there, marked. A number you cannot reproduce is worth less than no number.