The brief was a homepage. The problem was a ten-year-old codebase.

The brief was a homepage. The site was load bearing.

Four companies had worked on this WordPress site over the years, and each one left a layer. Theme components at the bottom, a page builder on top, and now a new homepage to build. The same install also runs the B2B entry points, the appointments and the coupons, which meant a design change and an outage were the same event.

01 / The problem

Every new page made the next one harder

Four companies had maintained this site over the years. Each one left a layer.

At the bottom sits a theme with its own components, written in PHP. Newer sections sit on top of it, built in a page builder. The two do not share a styling model or a breakpoint model, so updating one could break the other, and it often did. Over time people got careful. Nobody wanted to touch anything, because nobody could predict what a change would hit, so new work got bolted on rather than built in.

Behind the marketing pages sit the parts nobody demos: B2B entry points, an appointment system, promotions, coupon management. These are real tools with real users, and they run on the same install as the homepage. A bad line of CSS in the global header does not simply make a page look wrong. It reaches the booking form and the coupon tool at the same moment, while people are using them.

A design change and an outage were the same event.
Most of what follows comes out of that one fact.
one WordPress install global header + footer (theme level), shared by everything below WHAT GETS DEMOED Homepage Product pages News WHAT MAKES MONEY B2B entry points Appointments Promotions and coupons The homepage and the coupon tool share a header. Touch it and you have shipped to both.
The half of the site everyone looks at is the half that does not make money.
02 / What we changed the brief to

An opt-in system, not a redesign

The business requirements were real and had to be met. But meeting them on their own would have fixed one quarter and made the following year worse, because the new homepage would have become a fifth layer on a site that already had four.

A rewrite was never on the table. You cannot freeze a site that is taking bookings. So the homepage became the first application of something larger: a shared set of components and values that the rest of the site could adopt gradually, at whatever pace the client could afford.

That produced an unusual first requirement. The system had to be capable of doing nothing. A design system that arrives and restyles things is useless here, because “things” includes the appointment form. So the tokens publish across the whole site, which is safe because they are only declarations and nothing changes until a page picks them up. Every actual style sits behind a class that only the new page carries. The site opts in one page at a time, and it can stop at any point without leaving anything broken.

03 / The system

Values, then components, then the page

The design work was mostly a search for the parts of the homepage that were not really about the homepage: the shapes that would come back on a product page, a news archive, a contact page. That gave us 25 components. Around a third of them appear only once, but each was defined as though it would be reused, because eventually it will be.

The build order matters, because each layer is only correct if the one underneath it is. Tokens hold every value. Text styles and components reference those tokens rather than containing numbers of their own. Layouts are templates you instance and detach. The page is an output of all of it.

TOKENS 102 values tokens.json STYLES 25 text styles linked, not typed COMPONENTS 25 structure only LAYOUTS templates instance, detach PAGE an output, never a source A component holds a reference to a value, not the value itself. A hardcoded colour inside a component is therefore treated as a bug, not a preference.
Every colour, size and space in every component points at a token, so no value is typed in twice.

Two decisions turned out to matter more than they looked. We kept two grey ramps rather than one, a cool grey for text and a warm grey for surfaces. Merging them would have been simpler, and would have made the site quietly wrong in a way that is hard to point at.

We also separated modes from variants. A gutter that goes from 60 to 20 on mobile is a mode: one token, two values. A menu that becomes a burger is a variant, because the structure genuinely changed. Confusing the two leads to duplicating components in order to store numbers, which is a common way for a library to grow without getting better. In the end, eight values change between breakpoints and nine components rearrange. Nothing else responds at all.

04 / A correction that changed the plan

Header and footer rollout, reordered around risk

My plan was to start with the header and footer. They appear on every page, so you get the most reuse soonest. It seemed obvious.

Then the engineer mentioned, more or less in passing, that the header and footer are not built in the page builder at all. They are theme templates. He was answering a different question and did not appear to think it was significant.

It changed the whole plan. Replacing a theme template does not affect the homepage, it affects every page at once, including the appointment system and the coupon tool. The reuse that makes the header attractive to build first is the same property that makes a mistake in it expensive. Building it first would have spent the entire risk budget on the least tested work in the
project.

So we inverted the order. The homepage gets its own template, which renders a new header and footer while the rest of the site keeps the old ones. Everything stays scoped and reversible. Only after the whole page has been built and tested does the header and footer swap go site wide, and by that point it is a change that has already been proven in place.

THE ORIGINAL PLAN 1. Header and footer reaches every page before anything is proven 2. Homepage body 3. Test Risk is spent first, on the least tested work. A mistake appears everywhere at once. WHAT WE DID 1. Inside of the homepage scoped 2. Header and footer, homepage only scoped 3. Test the whole page 4. Roll out to the whole site a swap that has already been tested Risk is spent last, on the most tested work. The dangerous step becomes a routine one. Same four tasks, reordered around what can be undone.
Build the reversible things first, and leave the one you cannot undo until last.
05 / What the audit found

One was visible.

A routine check turned up a frame collapsed to a sliver, quietly cutting off its own content. The cause was a quirk in the tool: resizing a frame silently locks its height, even immediately after you have told it not to. Rather than fix the one we could see, we swept for the whole class of bug.

55 collapsed frames. The one we spotted, the mobile footer, the action bar, a row on the homepage, and every swatch on the Foundations page, which is the reference for the entire colour system.

Fifty four of them were invisible until somebody went looking.
06 / Checking the library against the design

The check found the system wrong, not the build

Once the library existed we rebuilt the original design out of it, measured both, and compared. The point was to test the system rather than the page.

It failed. Three type sizes were wrong on desktop, and had been wrong for six versions.

LibraryThe design
Section headings28px46pxhalf size, on every section
Stat numbers28px60pxa bug we had introduced
Hero headline48px60pxone step too small

The section headings were the instructive case. The component itself was built correctly. The page was simply calling it at the wrong size, and nothing in the file could have told us that. A component can be flawless and still be used wrong.

I got one of these wrong twice. I had measured the stat numbers at 48px and written that into three documents. The client said they looked too small and that the original was 60. He was right. I had misread a grouping in my own measurements, and then treated my own output as evidence. Measure the source, not your notes.

A design system that has never been checked against the original design is an interpretation, and interpretations drift.

The same check saved something we were about to flatten. On mobile, the stat numbers are deliberately larger than the headline. It looks like an error and it is not, because the numbers are the argument the page is making. A tidier type scale would have quietly removed it.

07 / Source of truth

Splitting the job between Figma and code

Teams argue about this endlessly. Is Figma the source of truth, or the code? Both answers tend to fail the same way, because both files end up holding the same information and slowly disagreeing about it.

We split the responsibility instead. tokens.json owns every value: colour, size, space, radius, shadow, breakpoints. The Figma library owns the components: what exists, what variants it has, what slots it exposes. Values flow in one direction, from code into Figma.

This does not make drift impossible. A token reference can break, a component can be edited without rebinding, an engineer can implement something the library never described. What it removes is the most common failure, where two files each define the same value on their own and diverge over time. The two systems no longer define the same values independently, and the failures that remain are the kind you can go looking for, which is what the routine in chapter 10 is for.

tokens.json owns the VALUES colour, size, space, radius, shadow, breakpoints Figma library owns the COMPONENTS what exists, its variants, its slots, its structure one direction Neither file defines something the other one also defines. Drift is still possible through broken references, but neither file defines a value the other also owns.
The common Figma-to-code drift comes from both files claiming the same ground.
08 / Documentation

The rules you cannot see by looking at Figma

Every decision went into a file, and every file went into git. Decisions that stay in a chat thread get forgotten within about a week. Six months from now nobody asks what a value is. They ask why it is that value, and whether they are allowed to change it.

The most useful document is the one the engineer reads first, before the component list. Each of these rules has already caused a bug on this project, and none of them are visible in the design file.

Handover, page 1 of 24
  1. Capitals are CSS, never typed.The site runs in three languages. Typed capitals can interfere with translation matching and assistive pronunciation, and they remove the accents from Greek capital letters, which is a spelling error rather than a style choice.
  2. No hardcoded values.If you are typing a colour, something has gone wrong upstream.
  3. Bands run edge to edge, content stays in a box.Adding side padding to a band turns it back into a card.
  4. Shadows need room.A 70px blur needs roughly 100px of space. No hidden overflow anywhere near one.
  5. Two colour numerals are two elements.The red plus in "10.000+" is a span, never an image and never the whole string.
  6. The configurator is hidden on mobile, not removed.It stays in the page source and a media query hides it.
A design system that only its author can apply has not really been delivered.
09 / Handover and the platform decision

Two handover documents, one for a person and one for a model

The engineer gets the brief above. His AI assistant gets a second one, written for a model rather than a person, which turns out to be a different document. Less “here is how it works”, more “here is the confident wrong thing you are about to do”. Do not remap the global colours, because it restyles every old page the moment you save. Do not put styles in the page builder, because they end up in the database instead of git. The library says this number is 48, but it is 60, so trust the tokens.

Whether to buy the paid page builder

The site runs an old, free version of its page builder, and the obvious move was to buy the paid tier. We had assumed we would. Two things changed the answer.

The paid tier’s headline feature lets you write CSS on any element from inside the editor, and stores it in the database. Not in git, not searchable, and not reviewable in any practical way. For a design system that is a slow leak rather than a sudden failure, and it is hard to detect until the styling has already fragmented.

The second reason was less philosophical. A current licence requires a current version of the builder, and this site was three years behind. Buying the licence would have forced an upgrade the client had deliberately deferred, and put a risky migration onto the critical path of a homepage build.

Without per-element CSS, every style had to go into the stylesheet and into version control, because there was nowhere else to put it. The limitation produced a better architecture than the paid tier would have.

The real cost of not buying it came to two custom shortcodes.

10 / Keeping it alive

The routine

Design systems rarely decay because people are careless. They decay because nothing obliges anyone to look. So the last thing we shipped was a loop, run after every completed change.

01 Re-check the tokens The default is no change. A new token has to be argued for. 02 Sweep the library Loose colours. Unstyled text. Broken instances. Clipped shadows. 03 Update the docs Both of them. New entries on top, old ones are never deleted. 04 Sweep collapsed frames Added after the bug that found 55. Every step here arrived the same way. after every completed change The list grows. Each step is there because something broke and nothing caught it. Step 04 did not exist until a footer collapsed.
Four steps, run as a loop after every completed change.

It is written into three places, the repo, the Figma file and the AI agent’s instructions, so that it runs whether or not anyone remembers to. The list will keep growing.

Outcome

What exists now

25

Components

102

Tokens

0

Hardcoded values

2

Sources of truth

The homepage can be built without touching a single old page, and without the bookings or the coupons noticing that anything happened. The next page will be cheaper than this one, and the migration can stop at any point without leaving the site half converted. The handover works for an engineer and for the AI agent sitting next to him, and the routine should keep it honest after I have moved on.

What I would do differently

Check the library against the design much earlier. Three type sizes were wrong for six versions because we kept checking the build against the library, and never the library against the design. That check belonged in version one, and it cost almost nothing to run.

Ask what the header is made of on day one. The entire build order depended on an implementation detail that nobody thought to mention, because from the engineering side it did not look like a design question. It turned out to be the most consequential one on the project.