The Brief Design System: re-architecting the foundation

The MUI-based design system I built for Ad Studio, which then became the design language for redesigning the whole Creatopy platform. A three-layer token architecture and a full component library, built to code with engineering in Storybook.

Role
Senior Product Designer, system owner
Outcome
95% component adoption across the product, in an engineering code audit after v1
Team
Me as the only designer, with 1 engineer building the Storybook library
Company
The Brief AI (formerly Creatopy)
When
2022 to 2024
Scope
Token architecture, component library, type system, design-to-code parity, platform rollout
The full system on one board: atoms, molecules and organisms laid out together with the foundations and component library⤢ Click to zoom
The whole system on one board, from atoms up to organisms. Click to zoom in and pan across it.
01

Why rebuild it

The platform had outgrown its original system. As the editor grew more complex, the old library couldn't keep pace: inconsistent components, values that drifted between design and code, and no clean foundation to build new surfaces on.

So I re-architected the whole thing on a new MUI foundation, rebuilding the structure from the tokens up so the system could scale with the product.

It started with one product. Ad Studio was the first surface built on the new system, the proving ground for the tokens, the components and the engineering workflow. Once it held up there, the same system became the design language for redesigning the rest of the platform. See Ad Studio, the product it first shipped in →

02

How it composes: atoms, molecules, and a full element matrix

The system is built to compose. Atoms are the smallest sanctioned pieces: a label, a select, a text field, a toggle, an icon button, a colour control. Molecules assemble those atoms into the functional rows of the editor's properties bar. Nothing in a molecule introduces a new value; it only arranges atoms that already exist.

AtomsSanctioned smallest pieces
Label · select · text field · toggle · checkbox · icon button · colour palette · slider · alignment controls.
↓ assembled into ↓
MoleculesProperty-bar rows
Functional groupings built only from existing atoms. No new values, just a careful arrangement.
↓ resolved across every element type ↓
MatrixElement type × property
Text · Button · Image · Shape · Illustration · Logo · Line · Audio · Mask · Video · Countdown, each mapped to the property bar it needs.

The hardest part was the combinatorial problem underneath: the editor supports many element types, each with a different set of editable properties. A text layer, a video, a countdown and a particle effect don't expose the same controls.

So I mapped the whole space as a matrix: every element type against every property, with the correct property bar resolved for each combination, all from the same atoms and molecules. That's what keeps the editor coherent no matter what the user selects, and it's the structure that makes adding the next element type a known, bounded task instead of a redesign.

The element-type × property matrix: every element type across the top, every property down the side, with the resolved property bar for each combination⤢ Click to zoom
The element-type × property matrix: every element type against every property, resolved from the same atoms and molecules. Click to zoom in and pan across it.
Anatomy of the Button element: its property bar annotated and broken into property groups (Basic actions, Layer, Label, Background, Actions)⤢ Click to zoom
Zooming into one element: the Button property bar broken into its groups (Layer, Label, Background, Actions). Each group is molecules assembled from existing atoms. Click to zoom in.
The molecules page: property-bar rows, each one assembled entirely from existing atoms⤢ Click to zoom
Molecules: the property-bar rows, each built only from existing atoms. Click to zoom in and read across them.
03

The architecture: tokens and type

Underneath the components sits a three-layer token model, each layer building on the one beneath it. Primitives define the raw palette and scales; a semantic layer maps those primitives onto meaning; and components consume only the semantic layer, so the rest of the system never references a raw hex or pixel value directly. It references intent.

Layer 1Primitives
The raw scale. Grey/Grey 900 · radius steps · the type ramp. Values with no opinion about where they're used.
↓ aliased to meaning ↓
Layer 2Semantic tokens
Text/Primary · Text/Secondary · Action/Hover · Action/Selected · Other/Divider · Accent/Main. Named for intent, not appearance.
↓ consumed by ↓
Layer 3Components
Atoms and assemblies that only ever reference semantic tokens, so one change at the foundation cascades everywhere, predictably.

This is the whole point of the structure: because components reference semantic tokens and semantic tokens reference primitives, a single change at the bottom flows through the entire system without hunting through files. You rename an intent once instead of hunting down a thousand hex values.

Text/Primary
#212121
Text/Secondary
#666666
Text/Disabled
#9E9E9E
Accent/Main
#005BE5
Action/Selected
#F2F7FE
Action/Hover
#EBEBEB
Other/Divider
#F1F1F1
Action/Active
#757575
Warning/Main
#C39634
Warning/Alert/Bg
#F3E7CE

Real tokens from the system. Note the naming: intent first (Action, Text, Warning), never the colour. Even alerts get their own semantic sub-group.

Type follows the same rule. Inter carries the whole UI, and every step is a named token with a fixed size, weight, line-height and letter-spacing, so type is applied by role rather than eyeballed per screen.

Overline
Inter · 600
Overline label, 12 / 28
Body 3
Inter · 400
Body copy at 12 / 18, the platform's reading size.
Input Text
Inter · 500
Field input, 11 / 24

Real type tokens, named by role so the ramp stays consistent across every surface.

04

Governance: what's in the system, and what isn't

On the atoms page I split every element into “components used” versus “variants or ideas only.” Sanctioned atoms on one side, exploratory work clearly fenced off on the other.

That line matters more than it looks. It tells anyone opening the file exactly what's safe to build with and what's still thinking-out-loud, which is what stops a system from quietly sprawling into a dozen near-duplicate buttons.

The atoms page, split into “components used” versus “variants or ideas only” so the sanctioned set is fenced off from exploration⤢ Click to zoom
The atoms page: sanctioned “components used” on one side, “variants or ideas only” on the other. Click to zoom in.
05

Key decisions

D1

Build on MUI, then bend it to fit

I based the system on Material UI rather than inventing every primitive. It gave engineering a structure they already understood and a known accessibility baseline. The defaults didn’t fit, though. None of MUI’s three sizes worked: even the smallest was too big for a control-heavy properties panel, and buttons elsewhere needed to be larger. So I rebuilt the scale at 28, 36 and 42px and restyled radius, padding and colours to match.

TradeoffEvery size and style we changed became an override to maintain on top of MUI, re-checked with every upgrade. The 28px controls are denser than MUI intends, a deliberate call for a desktop editor that still clears WCAG 2.2’s 24px minimum target. In return, engineering kept a structure they knew, with focus and keyboard behaviour built in.
D2

Three layers of tokens

I split tokens across primitives, a semantic layer, and the components that consume them, rather than letting anything reference raw values directly. It's more upfront structure, but it's what makes the system maintainable at scale.

TradeoffMore indirection to set up and explain up front. After that, a change cascades predictably through every component.
D3

Govern by drawing a hard line

I made the sanctioned-versus-exploratory split explicit in the file itself, rather than keeping it in my head. Anyone opening the file can see what’s safe to use.

TradeoffKeeping that boundary current was ongoing work, and as system owner it was mine. It kept the system coherent instead of letting it sprawl into duplicates.
06

Design-to-code parity

I worked directly with engineering to build the components in Storybook, the full set: inputs, buttons, selects and the rest, so the design source of truth and the coded source of truth were the same thing.

The shared MUI base and the semantic token layer are what made that parity practical: when design and code speak the same structural language, handoff stops being a translation exercise and the gap where drift creeps in mostly closes.

We proved the whole pipeline in Ad Studio first, design tokens to Figma components to Storybook to shipped UI, then rolled the same components out as the platform was redesigned around them.

The CDSButton component: its full variant set in Figma on the left, the same component built in Storybook on the right⤢ Click to zoom
Design-to-code parity: the CDSButton variant set in Figma, beside the same component built in Storybook. Click to zoom in.
07

How it got to 95%

After Ad Studio, Brand Kit, Templates, the share link and the Dashboard moved onto the system, with Storybook as engineering’s single source of truth.

Adoption was checked twice. Every Figma component had a matching name in Storybook, so developers could find the coded version of anything in a design. In design review we compared each build against its Figma component, down to the label’s size, colour, weight and family and the button’s size, radius and background. In code review, engineers checked that every component came from Storybook.

Before the system, several developers could each build their own version of the same thing, like a dropdown with icons and text. The system shipped one selector with its variants, the menu that opens from it and the items that menu can hold, and we saw fewer QA issues as a result.

The pushback was speed. Developers hit cases the library didn’t cover yet, and the Storybook check in code review added time. So surfaces moved onto the system when a feature touched them anyway, and a missing component was added when a story needed it. That way adoption rode along with roadmap work.

The last 5% was legacy screens, a few specialised one-offs and corners of the product that rarely saw development work. None of it mattered enough to migrate for its own sake, and users didn’t feel the difference.

The selector field in all its variants: filled and outlined, in default, hover, focus, disabled and error states, with and without an icon, value or helper text, in three size groups⤢ Click to zoom
The selector: filled and outlined, every state from default to error, with icons, values and helper text, at the three sizes from D1.
The menus that open from the selector: plain options, options with icons, ad sizes with their dimensions, removable items, and font lists for brand and web fonts⤢ Click to zoom
The menus it opens: plain options, icons, ad sizes with their dimensions, removable items and font lists.
Menu item variants: text only, with an icon, with dimensions and with a trailing action, in default, hover, disabled and selected states⤢ Click to zoom
And the items a menu can hold, in every state.
08

Outcome

95%component adoption across the product.

Method Code audit by the engineering team after v1 shipped: 95% of the UI components in the product’s front-end code came from the system library.

  • From one product to the whole platform. Proven in Ad Studio, then adopted as the design language for redesigning the rest of the Creatopy platform.
  • A scalable foundation the platform's newer AI-native surfaces are built on, instead of a library actively slowing the work down.
  • Predictable change. The three-layer token model means a foundational update flows through the whole system without manual hunting.
  • Tighter handoff. Storybook parity and a shared MUI vocabulary cut the translation work between design and engineering.
  • A coherent system, held together by an explicit line between what is sanctioned and what is exploratory.
09

Reflection

The speed pushback taught me to budget for the gaps. Adding components only when a story needed them kept teams waiting. Time set aside each sprint for missing pieces would have taken that friction out sooner.