← Back to work

Case study

Arbor: Building a Design System With AI, and Documenting Every Decision

the Arbor token system and component library
Client
Self-directed
Period
2026
Role
Designer, Developer, Author
AI WorkflowDesign SystemsPrompt ArchitectureMCPNext.jsVariable FontsDocumentation

Why this exists

I started using AI in my design practice for a practical reason. At Silk, I was the sole designer across multiple brands, and I needed to build a design system while also shipping everything else. AI was how I closed that gap, not a strategy, just the only way to add the value I wanted to add in the time I actually had.

It worked well enough that I wanted to understand it properly. Arbor is the result: my portfolio, built as a complete design system from tokens to production code, with every decision documented as it was made.

This site is the case study. The process is the point.

What was built

  • A full design token system: color, typography, spacing, elevation
  • A custom variable display font, instanced from Fraunces
  • Twenty-six components, designed in Figma and built in code
  • Seven page templates across three breakpoints
  • A Next.js, TypeScript, and Tailwind implementation
  • A Notion workspace holding a decision log, an AI workflow log, and a component tracker

How AI was actually used

Not as a design partner. As leverage on execution.

I orchestrated Claude across three surfaces (Figma via the Plugin API, Notion via MCP, and the codebase via Claude Code), with each one handling the work it was suited to. The design decisions stayed mine. What changed was how quickly a decision could become a real artifact.

The governing principle was that context quality determines output quality. Which is why the documentation is not overhead in this project. It is infrastructure. Every decision logged with its rationale is a decision an AI agent can work from correctly on the next task.

What went wrong, and what I learned

The terracotta problem. The original palette used terracotta for primary buttons. In practice they read as error states: the color carried a warning signal I hadn't accounted for. The fix was a system-wide recolor: blue became the sole interactive color, terracotta was reassigned to errors, and the semantic meaning of the palette was made explicit rather than assumed.

The Figma API. Figma's Plugin API silently reverts auto-layout direction overrides on component instances. There is no error and no warning: the change simply does not persist. I discovered this by building stacking variants that kept collapsing back. The workaround was separate components rather than instance overrides. This is the kind of constraint you only find by building.

The font. I built Arbor Display as a partial instance of Fraunces with two axes baked in. Four glyphs (j, J, f, and g) rendered incorrectly, traced to a variable-axis alternate design. The fix for the lowercase j was a direct glyph transplant from Fraunces itself rather than editing coordinates by hand. Both fonts shared the same units-per-em and advance width, which made them metric-compatible.

Typography drift. An audit found the Figma file roughly eighty-five percent detached from its own text style library. I reconnected 278 exact-match nodes and deliberately left the documentation nodes custom, a distinction worth making explicitly rather than reconnecting everything and losing the annotations.

What this demonstrates

I know where AI creates leverage and where it does not.

It does not make design decisions. It did not find the terracotta problem, discover the Figma constraint, or decide which typography nodes to leave detached. Those required judgment, and judgment came from having done this work by hand for a decade.

What it does is collapse the distance between a decision and its implementation. Under the conditions most designers actually work in (one person, many surfaces, not enough hours), that distance is often the entire problem.

Arbor is the argument that documented reasoning is what makes AI-assisted design work. Not prompts. Context.