Skip to content

A design system that brought speed and consistency to 4 product teams

A token-based design system in Figma and React that eliminated component duplication across 4 product squads and cut the time from design handoff to merged frontend.

Tessera 1.8.0

Tokens, React components and versioned docs for the Onboarding, Payments, Lending and Wealth squads

Changelogv1.8.0 Figma StorybookML

Apps on the package

12 of 12

Installs @tessera/react · not every pattern within it

Color tokens

84

One Figma source, generated to npm

UI on the system

84%

8,412 instances · last 30 days

Still duplicated

6

Components · down from 37 at the audit

Components

Every entry tested before it joined the library
ComponentImportStoriesWCAG AAVisual regressionSquads on it
ButtonButton5 statesKeyboard + SRNo changesOPLW4/4
Text inputTextinput5 statesKeyboard + SRNo changesOPLW4/4
SelectSelect5 statesKeyboard + SRNo changesOPLW3/4
Date pickerDatepicker5 statesKeyboard + SRNo changesOPLW3/4
TableTable5 statesKeyboard + SRNo changesOPLW2/4
ModalModal5 statesKeyboard + SRNo changesOPLW3/4
Data gridDatagrid5 statesKeyboard + SRNo changesOPLW3/4
ToastToast5 statesKeyboard + SRNo changesOPLW3/4

Release 1.8.0

09/16/2026 · private npm + Storybook, same version
Tablenegative accepts "sign" or "parentheses"; both keep color as a second cue
DatePickermin and max replace minDate and maxDate · codemod available
Tokenstext.loss moves to loss.600 so negatives hold body-text contrast
Installprivate registry
$ npm install @tessera/react @tessera/tokens
$ npx @tessera/codemods --list

Squads

Each pins its own version
DROnboardingDev Raman · 3 surfaces1.8.062% on system
ACPaymentsAisha Coleman · 3 surfaces1.7.292% on system
TWLendingTom Whitaker · 3 surfaces1.8.091% on system
LOWealthLucía Ortega · 3 surfaces1.8.088% on system
WCAG AA · 100%Third-party audit of the component library. It covers the library, not every consuming surface.

The brief, in specifics

Industry
Professional Services
Duration
14 weeks
Cooperation model
Fixed price, phased
Services
Design tokensReact component libraryDocumentation
Integrations
StripeSendGridSegmentSentry
Technologies
ReactTypeScriptTailwind CSSFigma TokensStorybooknpm
Team
1 Project lead2 Product designers1 UX researcher1 QA engineer

Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.

The screens

Every surface, in order

Color

84 tokens · 60 ramp steps and 24 semantic aliases, published from one Figma source to @tessera/tokens

Open in FigmaCopy tokens.jsonv1.8.0 Figma StorybookML

Ramps · 60 tokens

Ratio against surface.default #FFFFFF · 25 of 60 may hold body text
50100200300400500600700800900
violet

#F4EFFE

1.13

#E9DDFD

1.29

#D4BCFB

1.70

#B993F7

2.45

#A06CF3

3.54

#8D4DF0

4.72

#7A36DE

6.16

#6428B8

8.28

#4E2090

10.86

#37176A

14.06

slate

#F7F7FA

1.07

#EEEEF3

1.16

#DCDCE5

1.36

#C2C2CF

1.76

#9D9DAE

2.67

#77778A

4.38

#5A5A6C

6.74

#434353

9.69

#2C2C38

13.78

#18181F

17.66

gain

#ECFAF3

1.07

#D2F3E2

1.19

#A6E6C6

1.42

#6ED3A3

1.82

#38BB7F

2.45

#179F65

3.40

#0F8453

4.72

#0C6943

6.74

#0A5035

9.49

#073826

13.10

loss

#FEF0F0

1.11

#FDDCDC

1.28

#FAB8B9

1.66

#F58A8D

2.36

#EC5E63

3.31

#D93E44

4.45

#BC2C33

5.92

#972328

8.12

#711C20

11.12

#4D1417

14.71

amber

#FFF7E8

1.06

#FEEBC7

1.17

#FCD68D

1.39

#F8BB4F

1.72

#EEA024

2.17

#D48710

2.89

#B06D0B

4.17

#88540B

6.33

#633E0B

9.44

#432A08

13.35

info

#EEF5FF

1.10

#D9E8FF

1.24

#B3D1FF

1.56

#80B2FA

2.17

#4F90F0

3.19

#2F73DB

4.57

#235CBA

6.34

#1D4894

8.73

#18376F

11.57

#11264C

14.94

4.5:1 and up · AA, body text3:1 to 4.5:1 · AA large, 18px or 14px boldUnder 3:1 · fills and borders only

Text on surfaces

The pairs product code actually uses
Aa17.66:1
text.primaryAA
Aa6.74:1
text.secondaryAA
Aa4.38:1
text.mutedAA large
Aa6.16:1
text.linkAA
Aa4.72:1
text.gainAA
Aa5.92:1
text.lossAA
Aa6.33:1
text.cautionAA
Aa16.51:1
text.inverseAA

Semantic aliases · 24

Alias → ramp step
text.primaryslate.900
text.secondaryslate.600
text.mutedslate.500
text.inverseslate.50
text.linkviolet.600
text.gaingain.600
text.lossloss.600
text.cautionamber.700
surface.defaultslate.50
surface.raisedslate.100
surface.sunkenslate.200
surface.inverseslate.900
border.subtleslate.200
border.strongslate.300
border.focusviolet.600
action.primaryviolet.600
action.primary.hoverviolet.700
action.dangerloss.600
status.gain.bggain.50
status.loss.bgloss.50
status.caution.bgamber.50
status.info.bginfo.50
chart.primaryviolet.500
chart.compareslate.300

The color foundation: 84 tokens published from one Figma source to npm, every swatch carrying its hex value, and the contrast ratios that decide which may hold body text.

Adoption by squad

Eight tracked components across four products · share of rendered component instances, 08/18 – 09/16/2026

Export CSVv1.8.0 Figma StorybookML
DROnboardingLead · Dev Raman1.8.0
62%4 of 8 on system
1,444 system885 hand-rolled

Sign-up flow · Identity check · Welcome dashboard

ACPaymentsLead · Aisha Coleman1.7.2
92%7 of 8 on system
2,604 system214 hand-rolled

Transfers · Bill pay · Payee admin

TWLendingLead · Tom Whitaker1.8.0
91%7 of 8 on system
2,318 system236 hand-rolled

Loan application · Underwriting console · Repayments

LOWealthLead · Lucía Ortega1.8.0
88%7 of 8 on system
2,046 system267 hand-rolled

Portfolio · Statements · Advisor console

Eight components across four products

System = imported from @tessera/react
ComponentOnboardingPaymentsLendingWealthOn system
ButtonSystemSystemSystemSystem4 of 4
Text inputSystemSystemSystemSystem4 of 4
SelectSystemSystemSystemHand-rolled3 of 4
Date pickerHand-rolledSystemSystemSystem3 of 4
TableHand-rolledSystemHand-rolledSystem2 of 4
ModalHand-rolledSystemSystemSystem3 of 4
Data gridHand-rolledSystemSystemSystem3 of 4
ToastSystemHand-rolledSystemSystem3 of 4

Onboarding · what is left

4 hand-rolled

62% of Onboarding’s UI on the system

Still hand-rolling its own date picker, table, modal and data grid.

Date pickerHand-rolled

Migration PR open · 41 call sites across 9 files

TableHand-rolled

Pairing with Dev Raman, week of 09/21

ModalHand-rolled

Next, after the date picker merges

Data gridHand-rolled

Waiting on the Table migration

Maya Lindqvist embedded with Dev Raman through migration

Adoption by squad: eight components across four products, and Onboarding at 62 percent, still hand-rolling its own date picker, table, modal and data grid.

Usage · last 30 days

Component instances rendered in production across all twelve apps · 08/18 – 09/16/2026

Export CSVv1.8.0 Figma StorybookML

Component instances

8,412

From @tessera/react, in production

UI on the system

84%

1,602 hand-rolled instances remain

Still duplicated

6

Components · there were 37

Apps on the package

12 of 12

Package installed · not every pattern

Instances by component

8,412 total
Button2,186
Text input1,402
Select918
Table764
Modal412
Toast386
Date picker297
Data grid241
All other components1,806

Share of UI on the system

System vs hand-rolled instances
Onboarding62% · 2,329 instances
Payments92% · 2,818 instances
Lending91% · 2,554 instances
Wealth88% · 2,313 instances
All twelve apps84%
System Hand-rolled

Duplicated components · 37 at the audit, 6 today

31 retired into the library

31 retired · 6 still duplicated

ComponentHand-rolled inReplacementNext step
Date pickerOnboardingDatepickerMigration PR open
TableOnboarding, LendingTableScheduled with squad lead
ModalOnboardingModalScheduled with squad lead
Data gridOnboardingDatagridScheduled with squad lead
ToastPaymentsToastScheduled with squad lead
SelectWealthSelectScheduled with squad lead

Thirty days of use: 8,412 component instances in production, 84 percent of the UI on the system, and the six components still duplicated where there were thirty-seven.

Type & spacing

Every step compiled from its Figma variable to the CSS custom property product code reads

Open in Figmav1.8.0 Figma StorybookML

Type scale · 10 steps

Figma variable → CSS custom property
SampleFigma variableCSS custom propertySize / lineUse
Availabletype/display--ts-type-display34/42 · 700Balances on summary screens
Availabletype/heading-1--ts-type-heading-126/34 · 700Page titles
Transfer fundstype/heading-2--ts-type-heading-220/28 · 600Section titles
Transfer fundstype/heading-3--ts-type-heading-317/24 · 600Card titles
Transfer fundstype/body-lg--ts-type-body-lg16/24 · 400Onboarding copy, dialogs
Transfer fundstype/body--ts-type-body14/22 · 400Default running text
$48,210.07type/numeric--ts-type-numeric14/22 · 500Money in tables · tabular
Transfer fundstype/label--ts-type-label13/18 · 500Field labels, buttons
Transfer fundstype/caption--ts-type-caption12/16 · 400Table metadata, helper textAA only · not for body text
Transfer fundstype/micro--ts-type-micro11/14 · 500Badges, axis ticksAA only · not for body text

caption and micro are checked at text.secondary on surface.default: AA for labels and metadata, never for running paragraphs.

Spacing · 4px base

11 steps
space/0--ts-space-00px
space/1--ts-space-14px
space/2--ts-space-28px
space/3--ts-space-312px
space/4--ts-space-416px
space/5--ts-space-520px
space/6--ts-space-624px
space/8--ts-space-832px
space/10--ts-space-1040px
space/12--ts-space-1248px
space/16--ts-space-1664px
tokens.cssgenerated
1 :root {
2 --ts-type-body: 14px/22px var(--ts-font-sans);
3 --ts-type-caption: 12px/16px var(--ts-font-sans);
4 --ts-space-4: 16px;
5 }

Type and spacing scales: every step mapped from its Figma variable to the CSS custom property it compiles to, with two small steps marked AA only, not for body text.

Playground · DataGrid

Storybook for @tessera/react 1.8.0 · every state is a story, and every story is checked

Copy JSXv1.8.0 Figma StorybookML

Components

▸ Button

▾ DataGrid

Docs

Loading

Empty

Error

Dense

Long content

▸ DatePicker

▸ Modal

▸ Select

▸ Table

▸ TextInput

▸ Toast

LoadingEmptyErrorDenseLong content 100% · 1280px
Linked accounts7 rows · dense
AccountOwnerStatusBalance ↓Updated
Operating ··4410Harlow Supply Co.Active184,220.3609/16 2:36 PM
Payroll ··4427Harlow Supply Co.Active62,480.0009/16 2:36 PM
Card ··1009Harlow Supply Co.Review(18,742.55)09/16 2:35 PM
Reserve ··8830Brightwater DentalActive210,000.0009/16 2:35 PM
Card ··2261Brightwater DentalFrozen(6,318.20)09/16 7:40 AM
Escrow ··5102Ninefold StudioActive41,907.1209/16 2:31 PM
Card ··7730Ninefold StudioActive(2,210.40)09/16 2:30 PM

Controls

Props from the TypeScript source
PropTypeDefaultControl
rowsRow[]—7 fixture rows
columnsColumn<Row>[]—5 columns
density"comfortable" | "dense""comfortable"comfortabledense
stickyHeaderbooleantrue
loadingbooleanfalse
negative"sign" | "parentheses""parentheses"parentheses
onSortChange(key, direction) => void—action

Accessibility

Runs on every story
0violations
across 5 states

By state

Loading0 violations
Empty0 violations
Error0 violations
Dense0 violations
Long content0 violations

Checks

color-contrast

aria-required-children

th-has-data-cells

scrollable-region-focusable

aria-sort on sorted header

focus visible on every cell

keyboard: arrows move, Tab exits

The Storybook playground: the data grid with its props, its five states, and the accessibility checks that run on every story, at zero violations.

Migrating Onboarding's date picker

Guide · OnboardingDatePicker (hand-rolled) → DatePicker from @tessera/react 1.8.0

View PR #2184v1.8.0 Figma StorybookML

Before · OnboardingDatePicker

apps/onboarding/src/shared

Date of birth

mm-dd-yy ▼
SMTWTFS678910111213141516171819

No visible focus · arrows do nothing

After · DatePicker

@tessera/react

Date of birth

MM/DD/YYYY ▾
SuMoTuWeThFrSa678910111213141516171819

Focus ring token · arrows, PageUp/Down, Home/End

Prop renames

Handled by the codemod
OnboardingDatePickerDatePickerNote
selectedDatevalueControlled, ISO date string
onDateChangeonValueChangeSame signature
minDateminInclusive
maxDatemaxInclusive
placeholderTextplaceholderMM/DD/YYYY default
isDisableddisabledNative attribute
signup/steps/DateOfBirthStep.tsx1 of 9 files · 9 call sites
3−import { OnboardingDatePicker } from "../../shared/OnboardingDatePicker"
3+import { DatePicker } from "@tessera/react"
41 {/* applicant date of birth */}
42− <OnboardingDatePicker selectedDate={dob} onDateChange={setDob} maxDate={today} />
42+ <DatePicker value={dob} onValueChange={setDob} max={today} />
Terminalapps/onboarding
$ npx @tessera/codemods date-picker src/
scanning 214 files…
✔ 9 files changed
✔ 41 call sites rewritten
✔ 6 props renamed · 0 skipped

Files changed

41 call sites · 9 files
signup/steps/DateOfBirthStep.tsx9
signup/steps/AddressHistoryStep.tsx7
identity/DocumentExpiryField.tsx6
identity/ReviewQueueFilters.tsx5
welcome/FirstDepositScheduler.tsx4
welcome/GoalsForm.tsx4
signup/steps/EmploymentStep.tsx3
shared/forms/DateRangeField.tsx2
shared/forms/index.ts1

Before merge

Dev Raman · Onboarding

Type check passes

Visual regression · no unexpected change

Keyboard pass on the sign-up flow

Squad lead review

The migration guide: Onboarding's hand-rolled date picker beside the system component, with the prop renames, the codemod, and 41 call sites changed across 9 files.

Introduction

The brief

Four product teams were building with slightly different buttons, inputs, and patterns. We built one design system everyone actually liked using.

Four product teams, twelve web surfaces, and a component audit that found nine visually distinct primary buttons. Nobody had decided on nine; each had been reasonable at the moment it was written. The engagement was commissioned when a compliance review flagged accessibility defects that turned out to be present in eleven of the twelve surfaces, each needing its own separate fix.

Design Engineering

What people were running into

Designers spent hours rebuilding the same components, and developers shipped inconsistent UI patterns across products.

The duplication was expensive in an unusual way. Building the ninth button was cheap; the cost was that a fix to any one of them fixed one surface. The accessibility findings made that concrete: the same missing focus state had to be corrected eleven times, by four teams, on four schedules, with no way to verify it had been done everywhere.

We built a shared design system with Figma tokens, a React component library, and interactive Storybook documentation.

Design decisions

  1. 01

    Audited components across 12 existing product surfaces

    The audit found nine visually distinct primary buttons, none of which anyone had decided on: each had been reasonable when it was written.

  2. 02

    Created a token system for color, typography, and spacing

    Tokens live in Figma variables and export to one JSON source the Tailwind theme is generated from, with CI failing the build on drift.

  3. 03

    Engineered an accessible React component library with Storybook

    Every interactive primitive was keyboard and screen-reader tested against WCAG AA before it entered the library, ahead of any adoption.

  4. 04

    Embedded with squad leads to migrate existing products smoothly

    Squad leads had the team beside them for the whole migration, since each of the four had already worked around a handed-over library.

Process

Phase by phase

  1. Phase 1: Inconsistent Component Audit

    Product UI & Token Inventory

    Audited 12 scattered fintech web applications to catalog duplicated button, form, and table component variants.

    • Component Inconsistency Audit
    • Token Inventory
    • UI Debt Matrix
  2. Phase 2: Core Token & Figma Library

    Token Architecture & Accessibility

    Designed unified Figma design tokens for color, typography, spacing, and accessible contrast ratios.

    • Figma Token Architecture
    • Accessibility Spec
    • Token Engine
  3. Phase 3: React Library & Storybook

    npm Package & Component Portal

    Engineered accessible React component library published to private npm with interactive Storybook docs.

    • React UI Component Library
    • Storybook Portal
    • npm Package
  4. Phase 4: Product Squad Migration

    Multi-App Adoption & Sprint Speed

    Migrated 4 core fintech applications, cutting the time from design handoff to merged frontend by 40% with zero UI regressions.

    • App Migration Clearances
    • Developer Adoption Workshop
    • SLA Monitor
Component audit

Twelve web surfaces across four squads, inventoried before a single token was designed

Export UI debt matrixv1.8.0 Figma StorybookML

Primary buttons found · 9

As each surface shipped them · none of them decided on
Continue
Variant A#2F73DB
r4 · 13px/600
2 surfaces
Continue
Variant B#235CBA
rfull · 13px/700
1 surface
Continue
Variant C#1F6FEB
r6 · 12px/500
2 surfaces
Continue
Variant D#0F8453
r8 · 14px/600
1 surface
Continue
Variant E#18181F
r2 · 11px/700
1 surface
Continue
Variant F#4F90F0
r10 · 13px/600
2 surfaces
Continue
Variant Goutline
r6 · 13px/600
1 surface
Continue
Variant H#6428B8
r5 · 14px/600
1 surface
Continue
Variant I#2C6BD1
r3 · 12px/500
1 surface

Twelve surfaces

Primary button and keyboard focus, as audited
SquadSurfacePrimary buttonVisible focus stateFix owner
OnboardingSign-up flowVariant AMissingOnboarding squad
OnboardingIdentity checkVariant BMissingOnboarding squad
OnboardingWelcome dashboardVariant CMissingOnboarding squad
PaymentsTransfersVariant AMissingPayments squad
PaymentsBill payVariant DMissingPayments squad
PaymentsPayee adminVariant EMissingPayments squad
LendingLoan applicationVariant FMissingLending squad
LendingUnderwriting consoleVariant GMissingLending squad
LendingRepaymentsVariant FMissingLending squad
WealthPortfolioVariant CMissingWealth squad
WealthStatementsVariant HMissingWealth squad
WealthAdvisor consoleVariant IPresent—

What the audit found

12Web surfaces, owned by 4 squads
9Visually distinct primary buttons
11 of 12Surfaces missing a visible focus state
37Duplicated components across buttons, forms and tables

The same missing focus state meant eleven separate fixes, by four teams, on four schedules, with no way to verify them.

On screen

The audit that started it: nine visually distinct primary buttons across twelve surfaces, and eleven of the twelve missing a visible focus state.

01

Figma Token Sync & Design System

Bi-directional design tokens keeping Figma mockups and React code strictly aligned.

Tokens live in Figma variables and are exported to a JSON source of truth that the Tailwind theme is generated from, so a designer who changes a color changes the code, with no ticket in between. The sync runs in CI and fails the build on drift, which is the only thing that actually keeps a design system aligned once the launch enthusiasm wears off.

What shipped
  • Figma variables exported to one JSON source of truth
  • Tailwind theme generated from it, never hand-maintained
  • CI fails the build on token drift
Token pipeline

Figma variables export to one JSON source; the Tailwind theme is generated from it on every build

Re-run checkv1.8.0 Figma StorybookML
1Figma variables

Tessera Foundations › color

NameLight
loss/500#D93E44
loss/600#BC2C33
loss/700#972328
gain/600#0F8453

Published 09/16 4:12 PM · 84 color variables

2JSON source of truth
packages/tokens/tokens.jsonexported, committed
212 "loss": {
213 "500": { "$type": "color", "$value": "#D93E44" },
214 "600": { "$type": "color", "$value": "#BC2C33" },
215 "700": { "$type": "color", "$value": "#972328" }
216 },
3Generated theme
generated/tailwind.theme.ts do not edit
1 // Generated from tokens.json by tokens:build.
88 loss: {
89 500: "var(--ts-color-loss-500)",
90 600: "var(--ts-color-loss-600)",
91 700: "var(--ts-color-loss-700)",

tokens:check failed · build blocked

payments/statement-negatives · a91f3c2 · k.osei · 09/16/2026 4:38 PM

1 token drifted

Steps

Export Figma variables84 tokens6s
Validate tokens.json schema0 errors1s
Generate Tailwind theme + CSS2 files3s
Compare with committed themeDrift found1s
Visual regressionSkipped—
tokens:check · outputexit code 1
✖ Drift in packages/theme/generated/tailwind.theme.ts
colors.loss.600, line 90
expected, generated from tokens.json (#BC2C33)
90+ 600: "var(--ts-color-loss-600)",
found, committed in a91f3c2
90− 600: "#C0343A",
This file is generated. Change the Figma variable,
then run npm run tokens:build.

What the pipeline holds

1One source of truth

Figma variables export to tokens.json. Nothing else defines a value.

2Never hand-maintained

The Tailwind theme and CSS properties are generated on every build.

3Fails on drift

A value that differs from tokens.json blocks the merge, whoever changed it.

On screen

A Figma variable exported to tokens.json and generated into the Tailwind theme, and the CI drift check failing the build where the generated value was edited by hand.

Table · financial data

Tabular numerals, negatives marked beyond color, a sticky header through a thousand rows

<Table negative="parentheses">v1.8.0 Figma StorybookML

Example · Operating account, 1,000 rows

Scrolled to row 212 · header pinned
#DateDescriptionCategoryAmountBalance
21203/02/2026Client payment · Ninefold StudioIncome6,400.0054,610.07
21303/02/2026Cloudway HostingSoftware(412.18)54,197.89
21403/03/2026Northside Supply Co.Materials(1,240.00)52,957.89
21503/03/2026Transfer to Payroll ··4427Transfer(18,500.00)34,457.89
21603/04/2026Card refund · Parkline CouriersShipping86.5034,544.39
21703/04/2026Harbor Row LeasingRent(4,500.00)30,044.39
21803/05/2026Client payment · Brightwater DentalIncome12,750.0042,794.39
21903/05/2026Brightfield UtilitiesUtilities(318.44)42,475.95
22003/06/2026Wire feeFees(25.00)42,450.95
22103/06/2026InterestIncome41.1242,492.07
22203/09/2026Lakeshore Print WorksMarketing(212.40)42,279.67
22303/09/2026Client payment · Alder & FinchIncome3,900.0046,179.67
22403/10/2026Quarterly tax estimateTaxes(9,200.00)36,979.67
“Amount, column 5. Minus 1,240.00 dollars. Northside Supply Co.. Row 214 of 1,000.” ↓ from row 213

1240

18500

86.5

4500

Don't

Not in the library

Proportional, left-aligned, sign by color only

(1,240.00)

(18,500.00)

86.50

(4,500.00)

Do

Table default

Tabular, right-aligned, parentheses and color

Tested before it entered the library

Added in 1.2.0
WCAG AA · all checks passed

Keyboard

Arrow keys move between cells

Tab leaves the table in one stop

Enter sorts the focused column

Focus ring visible on every cell

Screen readers

NVDA with Chrome

VoiceOver with Safari

Header announced on row 214 of 1,000

WCAG AA

1.3.1 Header cells stay in the accessibility tree

1.4.1 Negatives not shown by color alone

1.4.3 text.loss at 5.92:1

2.4.7 Focus visible

text.loss5.92:1 · AA
On screen

The financial table at row 214 of 1,000: tabular, right-aligned numerals, negatives in parentheses as well as red, the header pinned, a visible focus ring, and the keyboard, screen-reader and WCAG AA checks it passed before entering the library.

02

Accessible Fintech Component Library

WCAG AA compliant tables, inputs, buttons, and modal dialogs engineered for financial data.

Financial tables are the hard part: numerals are tabular and right-aligned so columns compare by eye, negative values are marked by more than color, and the header stays sticky through a thousand rows without leaving the accessibility tree. Every interactive primitive was tested with keyboard and a screen reader against WCAG AA before it entered the library, not after adoption.

What shipped
  • Tabular numerals, right-aligned, negatives marked beyond color
  • Sticky headers that stay in the accessibility tree
  • Keyboard and screen-reader tested before entering the library
Modal · stories

Every component ships its states as stories; visual regression runs over them in CI

Re-run visual testsv1.8.0 Figma StorybookML

Stories · 5 states

Build for PR #2201 · compared with 1.8.0 baselines
Add payee×
CancelAdd payee
LoadingUnchanged
Add payee×
No saved payees yet
CancelAdd payee
EmptyUnchanged
Add payee×
Couldn’t load payees. Try again.
CancelAdd payee
ErrorUnchanged
Add payee×
Harlow Supply··4410Cloudway··0912Parkline··3381Harbor Row··7720Brightfield··5502
CancelAdd payee
DenseUnchanged
Add payee×
Northern Territories Community Credit Union, Operating Reserve Account for Seasonal Payroll
CancelAdd payee
Long contentChanged

Merge waits for review

4 stories unchanged, 1 changed. A person approves or rejects the new baseline.

Long content · baseline vs this build

Changed region outlined

Baseline · 1.8.0

Add payee×
Northern Territories Community Credit Union, Operating Reserve Account for Seasonal Payroll
CancelAdd payee

This build

Add payee×
Northern Territories Community Credit Union, Operating Reserve Account for Seasonal Payroll
CancelAdd payee
Footer buttons now stack when the title wraps. Intended?
Accept baselineReject

Docs version

Deployed with each release
1.7.2▴
1.8.0Latest
1.7.2Payments pins this
1.6.1Archived docs
1.5.0Archived docs

Payments runs @tessera/react 1.7.2, so its engineers read the 1.7.2 props, stories and states.

Release pairing

One version, two artifacts
npmPublished

@tessera/react@1.8.0

Private registry · 09/16/2026 4:31 PM

StorybookPublished

Tessera docs 1.8.0

Private deploy · 09/16/2026 4:31 PM

Every component ships

loadingemptyerrordenselong-content
03

Storybook Interactive Documentation

On screen

The modal's loading, empty, error, dense and long-content stories under visual regression, one change held for review, and the docs version picker set to the release Payments still runs.

Live component playground and developer documentation hosted on private Storybook server.

Storybook is the contract: every component ships with its states (loading, empty, error, dense, long-content) as stories, so a consumer can see what they're getting without reading the source. Visual regression runs over those stories in CI, and the private deploy is versioned alongside the npm package, so a team on an older release reads the docs for the version it actually has.

What shipped
  • Loading, empty, error and long-content states as stories
  • Visual regression run over the stories in CI
  • Docs versioned with the package, not just the latest
Outcome

What moved after the redesign

-40%

Design handoff to merged frontend

100%

WCAG accessibility

12 of 12

Apps on single system

Build velocity is measured as time from design handoff to merged frontend for comparable feature work, before and after, across the four teams. The accessibility figure is a third-party audit of the component library rather than of every consuming surface. Twelve of twelve is adoption of the npm package, not of every pattern within it.

Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.

What it settled

Principles that held

  • Unified component library cut the time from design handoff to merged frontend by 40% on comparable feature work.

    The velocity gain was in the second and third use, not the first: the library cost more to adopt than to ignore once, and less every time after.

  • Strict WCAG AA accessibility tokens eliminated UI compliance defects.

    Defects went to zero because the fix has one home. The same missing focus state had previously needed correcting eleven times, with no way to verify it.

  • Private npm package powered consistent design across 12 product web applications.

    Shipping as a versioned package instead of a shared folder is what made twelve of twelve possible: teams could adopt on their own schedule and still converge.

How design and engineering worked together

  • A cross-functional team of 4 worked on a fixed price, phased basis over 14 weeks, covering Design tokens, React component library, Documentation. We ran a weekly demo and a shared board they could read at any time. Their team took over day-to-day operation before the engagement ended, with handover built into the last phase.

    The team embedded with squad leads through migration instead of delivering a library and a deprecation notice. That's the difference between a design system that gets adopted and one that becomes the thirteenth surface. The four teams had all been given libraries before, and had all routed around them for reasons that turned out to be specific and fixable.

The fix has one home

One missing focus state: eleven fixes, or one

The compliance review found the same defect on eleven of twelve surfaces. With nine different buttons, that meant eleven fixes by four teams and no way to check them. With one Button component, it's one change in one release. Switch tabs, or use the arrow keys once one is focused.

A fix to one button fixed one surface. The compliance review found the same missing focus state on 11 of 12 surfaces, so it became 11 tickets in four backlogs.

Onboarding team

Own backlog, own sprint

  • Sign-up flowButton A

    Patched, unverifiedPatched in sprint 31

  • Identity checkButton B

    ScheduledScheduled for sprint 33

  • Welcome dashboardButton C

    OpenNot started

Payments team

Own backlog, own sprint

  • TransfersButton A

    Patched, unverifiedPatched in release 4.12

  • Bill payButton D

    Patched, unverifiedPatched, different ring color

  • Payee adminButton E

    OpenIn backlog

Lending team

Own backlog, own sprint

  • Loan applicationButton F

    ScheduledWaiting on vendor form

  • Underwriting consoleButton G

    OpenNot started

  • RepaymentsButton F

    Patched, unverifiedPatched on links only

Wealth team

Own backlog, own sprint

  • PortfolioButton C

    ScheduledScheduled for Q4

  • StatementsButton H

    OpenNot started

  • Advisor consoleButton I

    Had a focus state

11

of 12 surfaces missing the focus state

11

separate fixes, one per surface

4

teams, on four schedules

None

way to verify it was fixed everywhere

Architecture

From a Figma variable to twelve apps

A value changes in one place and travels through the same five stages every time, so the design file, the generated code, the docs and the shipped product can't quietly disagree.

  1. 1 · Source

    Figma variables

    • Color, type and spacing defined once, as variables
    • A designer changes the value, not a ticket

    One source of truth: nothing else defines a token

  2. 2 · Ingestion

    JSON token export

    • Variables exported to tokens.json
    • Committed next to the code that reads it

    The Tailwind theme is generated from it, never hand-maintained

  3. 3 · Engine

    Build and checks in CI

    • Tailwind theme and CSS properties generated
    • Drift check against the committed theme
    • Visual regression over every story

    A token that drifts fails the build

  4. 4 · State

    Versioned release

    • @tessera/react on the private npm registry
    • Storybook deployed at the same version
    • Primitives keyboard- and screen-reader-tested first

    Nothing enters the library untested against WCAG AA

  5. 5 · Delivery

    Consuming apps

    • Twelve apps across four squads install the package
    • Each squad upgrades on its own schedule

    The docs a team reads match the version it installed

Shared without breaking anyone

Change safety & accessibility

Visual regression on every story

Each component ships its loading, empty, error, dense and long-content states as stories, and visual regression runs over all of them in CI. A change to how a component renders is caught in the library, before any team installs it.

Versioned, adopted on each team's schedule

The library ships as a versioned private npm package with docs deployed for each version, so a release reaches a product only when its squad upgrades. Migration was done embedded with each squad lead, with zero UI regressions.

Accessibility tested before entry

Every interactive primitive is keyboard- and screen-reader-tested against WCAG AA before it joins the library. A third-party audit put the component library at 100% WCAG AA; it covers the library, not every surface built with it.

Product teams shipping the same component four different ways? Scope your build in 3 minutes.

Scope your build
Have a project?

Let's talk

Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.