Skip to content
Pra
All work

Social Housing Provider · 2026 · Frontend developer

A component library two brands could share

Extracting a themeable component library from two diverging codebases, with visual regression tests as the safety net.

cover
Shared components
64
Duplicate CSS removed
~40%
Visual regression cases
180

Stack

  • React
  • TypeScript
  • Design tokens
  • Storybook
  • Playwright
  • SCSS

The problem

Two sites, built a year apart by different teams, implementing what was nominally the same design language. In practice there were three button implementations, two card patterns that were subtly different, and enough copy-pasted CSS that a fix in one place had to be remembered in three others.

The trap in this situation is to unify by extracting the components as they exist. That produces a library with a prop for every difference the two sites happen to have today, which is worse than the duplication it replaces.

The approach

We started with tokens rather than components. Colour, type, spacing, radii, elevation and motion were defined as a two-layer system: a primitive palette nobody references directly, and a semantic layer that carries the actual promises — --color-text-muted, --color-surface-raised, --color-border-strong.

Naming by role rather than appearance was the rule we enforced hardest, because it's what makes a token survive a design change. --color-accent outlives a rebrand; --color-orange doesn't.

With the semantic layer in place, theming stopped being a set of component overrides and became a redefinition of about thirty variables. Components didn't need to know a theme existed.

Enforcement

A convention that isn't checked is a suggestion, so two things were automated: a lint rule failing any raw colour value in a component file, and a build that doesn't export the primitive layer at all. If --blue-500 isn't reachable from a component, it can't be misused.

Storybook became the development surface, and Playwright's screenshot comparison ran against every story in both themes at three viewport widths. That was what made the consolidation safe — 180 visual cases meant we could refactor aggressively and see immediately what moved.

The outcome

64 components with one implementation each, themed by token redefinition rather than branching. The measurable win was a large reduction in duplicated CSS; the real win was that a design decision now changes in one place and both sites agree.