Every page the platform migrates to modern Angular is now built on it: teams stopped building their own components, and features ship and test faster.
By this point I'd built the design system three times. This was the one that stuck: the first to cover every component, define how each behaves in every situation, and actually get implemented end to end. I designed about seventeen of them, and owned its largest, most complex component, Tables, the one almost the entire product is built on. Today it's the foundation of the platform's move from AngularJS to modern Angular: every migrated page is assembled from these components instead of each team building its own.
SynergySuite had been built over many years on several different technologies, and it showed: the same control looked and behaved differently from one screen to the next. Users felt it even if they couldn't name it.
I'd built design systems before, but this was the first that was complete, tested and adopted, not a library that covered the easy components and quietly gave up on the hard ones. Every component was defined all the way down to how it behaves in each state and edge case, then tested again once it was live.
We split the library so each designer owned a set of components end to end. These are the seventeen I designed, and the one I asked to own: Tables, which gets its own section below.
Tables were the piece I asked for. Almost the entire product is built on them, and their inconsistency had bothered me for a long time: which type belonged where, how each element inside behaved, all of it had quietly diverged. It became the largest file in the whole system.
"A table isn't one component. It's a system of behaviours: column types, sorting, selection, density, empty and loading states, row actions, inline editing, sticky headers, responsive rules."
Why Tables were the hard one
Getting it right meant setting a standard and a behaviour for every element inside, so any team could build a table that behaved correctly without re-deciding it each time. The payoff was one predictable table model the whole product could rely on, and the single biggest lever for consistency in the system.
Every one of those behaviours was written down as a rule, with a do and a don't wherever there was a wrong way that looked right. Three of them:
I built the table itself on Figma's grid auto layout, as two base components: a header cell and a row cell. Every variable a table needs lives in their properties: right or left alignment, sort hover and ascending or descending sort for headers; default, hover, selected and focused for rows, plus accordion rows, action menus and inline editing. Each of them works across five densities, from 64px down to 24px.
The one with the widest reach was the location selector, the component that decides which data you're looking at, opened from the Report Header on almost every screen. I designed it in two layers. For quick picks it's a light dropdown with search and Select All; Advanced opens the full selector as a side panel, with four consistent tabs: a flat Locations list, Business Units (HQ, franchises, brand owned), Categories, and a deep Hierarchy (Zone → State → Region → Location). Selections persist as you move between tabs and gather in an "Added Filters" panel. Because it turns up almost everywhere, getting its behaviour, states and empty states right had an outsized effect on how consistent the whole product feels.
The other was the alert. Working through real cases, I realised a single line alert wasn't always enough: sometimes you need to explain several errors at once and point the user to exactly where they are, for example errors sitting inside a table. So on top of the standard alert, in every status, I designed an expanded variant that carries a numbered list of problems, each tied to its row and column, with an action and a way to collapse it.
We deliberately chose a single, living Figma file as the source of truth over written docs. We knew nobody would read long text, so the system had to be as easy to open and read as possible. Each component carried its full anatomy, its behaviour, a prototype where it earned one, and real examples of how it applies across the product.
The lever that kept design and code honest was the "examples" page: a live library where developers place every implemented component, and what we test against. The rule was simple: the implemented examples page has to match the design's. That turned "keep design and code in sync" from a hope into a testable contract.
The system also lives in my AI workflow now. I've brought the whole library into Claude, so when I'm working on a feature I can generate prototypes built from our real components, with their real states and rules, instead of starting from a blank canvas. Because the components were documented so completely, the prototypes come out on system from the first draft, and I spend my time on the problem rather than on redrawing buttons.
What I value most from this is the shift from making screens to making the material screens are made of. Owning Tables taught me to think in behaviours and edge cases rather than layouts, and the shared "examples" page, one reference every implemented component had to match, turned "do design and code line up?" from a hope into a test you could actually run. And it changed the scale I design at: one decision inside a component quietly shapes every screen that uses it.
It's now paying off in the platform's migration to modern Angular. Every page that moves over is built from these components, so teams no longer build their own, there are fewer bugs, and testing is faster: components are tested once on their own, so testing a feature can focus on the feature itself.
The design system was a team effort across designers, developers and QA. This case study focuses on my own contribution (the components I designed and the Tables system I owned) and on the process and governance. Component visuals are from the system's documentation; Tables and a few others live in a separate Figma file. Outcomes are described qualitatively, from the migration in progress, rather than as tracked metrics.