01 · The situation
Thirteen stacks, one library nobody reused.
The GRC portfolio grew by acquisition and by age. Thirteen products, each with its own front end stack, its own release train, and its own idea of what a button is. One design function covers all of them.
A design system existed on paper. The failure was not the components. It was the delivery mechanism: a spec that a developer reads, interprets, and reimplements in whatever framework their product runs. Every reimplementation is a fork, and every fork drifts. We were correcting drift slower than we were creating it.
The usual fix is a styled component library. That fix assumes every consumer shares a framework. Ours did not, and never would.
Old path
- DesignComponent designed once
- SpecWritten up in documentation
- ReadDeveloper interprets the spec
- ReimplementPer stack, thirteen times
- DriftEach copy ages separately
The gap: the spec is a description of a component, not the component. Anything a human has to retype, a human will retype differently.
02 · What I took on, and what I left alone
A system that ships beats a system that looks right.
The tempting goal was visual consistency across all thirteen products. I did not chase it. A system with no adoption mechanism is shelfware, however good it looks, and unifying pixels across thirteen release trains would have consumed the whole effort before a single team adopted anything.
Took on
- Behaviour and accessibilityThe parts that are expensive to get right and identical everywhere.
- TokensOne source for colour, spacing, type and radius.
- DistributionGetting the real component into the codebase, which is where every previous attempt failed.
Deliberately left alone
- Pixel parity across productsThirteen products on different release trains cannot converge at once, and forcing it would have stalled adoption.
- Rewriting legacy screensNew and changed surfaces adopt first. Old screens migrate when they are touched anyway.
- One framework for everythingA migration nobody asked for and no roadmap had room for.
03 · The call
Unstyled primitives, served as a tool.
Two decisions carry this project. Build on unstyled primitives so the system is portable by construction, and distribute it through an MCP server so the component arrives in the codebase rather than in a document.
The second one is the part that is genuinely new. Developers increasingly write code with an AI assistant sitting next to them. That assistant is a consumer of the design system, and it had no way to read ours. Serving the system over MCP makes the assistant a first class consumer: it queries the real component and its rules, so the code it suggests is the system rather than an approximation of it.
- Decision
- Build on MUI Base, unstyled.
- Because
- Thirteen stacks cannot share styled components, but they can share behaviour, state handling and accessibility, which is the expensive part.
- Rejected
- A fully styled React library, which would have served the React products and stranded the rest.
- Cost
- Every consuming team writes its own theme layer, so the first adoption in each stack is slower.
- Decision
- Serve the system through an MCP server.
- Because
- A spec has to be read and retyped. A tool call returns the component and its constraints directly, to a developer or to the assistant writing alongside them.
- Rejected
- Better documentation. We had already tried that. Documentation quality was never the bottleneck, the retyping was.
- Cost
- The system now has runtime surface to maintain, not just a package to publish.
- Decision
- Tokens as the contract, components as the convenience.
- Because
- A product that cannot adopt a component this quarter can still adopt the tokens, so it moves toward the system instead of away from it.
- Rejected
- All or nothing adoption, which in a thirteen product portfolio means nothing.
- Cost
- Partial adoption is harder to measure and easier for a team to declare finished too early.
04 · How it works
One source, many consumers.
Source of truth
Distribution
Consumers
The AI assistant is a first class consumer, not an afterthought. That is the difference between a system that survives the way software is written now and one that quietly drifts.
New path
- DesignComponent designed once
- PublishPrimitive and tokens, one source
- PullDeveloper or AI tool requests it over MCP
- ThemeStack applies its own skin
What changed: the reimplementation step is gone. Nobody retypes a component from a description, so there is nothing to drift from.
05 · The hard tradeoff
Portability cost us polish, up front.
Unstyled primitives mean the system ships no opinion about how anything looks. Each team writes a theme layer before it sees value, which is real friction at exactly the moment adoption is most fragile. We accepted that because the alternative served three products and abandoned ten.
| Option | Stacks served | Drift risk | Cost per team | Call |
|---|---|---|---|---|
| Styled React library | Only the React products | Low where adopted, unchanged everywhere else | Low to start | Rejected |
| Per stack forks of one design | All thirteen | High, thirteen copies aging apart | Low each, unbounded in total | Rejected |
| Unstyled primitives, served over MCP | All thirteen, plus AI tools | Low, one source and no retyping | Higher first adoption, a theme layer per stack | Chosen |
06 · Where it landed
What is proven, and what is not.
Measured
Adoption is still rolling out across the portfolio, so there is no defensible before and after number yet. Rather than publish a figure I cannot show the method for, this stays empty until the first full quarter of adoption data lands.
Observed
The reimplementation step is gone for teams that have adopted. A component pulled over MCP is the component, so the class of bug where a product's button behaves differently from the system's button no longer starts.
Design and engineering now argue about the same object. Previously a disagreement about a component was really a disagreement about whose interpretation of a spec was correct.
Too early to claim
Whether portability actually reduces total maintenance across thirteen stacks, or just relocates it into thirteen theme layers. The honest answer arrives after the second or third product adopts, not the first.
Whether AI assisted development materially improves output quality when the assistant can read the system. It is the bet behind the MCP work and it is not yet settled.