Skip to main content

Design Systems from Scratch: A Small Team Playbook

In 2026, a 1-person team wielding AI tools beats a 3-person team that is not. The catch: you still need a real design system underneath. This is the playbook for building one from scratch without burning a quarter on infrastructure that ships nothing.

Abhishek Sharma· Head of Engg @ Fordel Studios
13 min read min read
Design Systems from Scratch: A Small Team Playbook

Most small engineering teams skip the design system conversation entirely. There is always a more urgent feature, a tighter deadline, a customer escalation that pushes "we should probably standardise our UI" into next quarter. And then next quarter arrives, the codebase has seventeen button variants, spacing is inconsistent across six screens, and the new AI-generated component from Bolt does not match anything the team shipped six months ago.

This is a solvable problem. But you have to approach it in the right order. Teams that start with component libraries fail. Teams that start with tokens survive.

···

Why Tokens First Is Not Just Theory

A design token is the single source of truth for a visual decision — a color, a spacing unit, a border radius, a typeface. It is the layer that sits between your design tool and your component code. Get it right once, and every component you build, every AI-generated widget you accept, every third-party library you integrate, can reference the same values.

When you skip tokens and go straight to components, you end up with hardcoded hex values scattered across 40 files. Changing your brand's primary color means a grep-and-replace operation that breaks tests and requires a full QA pass. With tokens, it is a one-line change.

1-person teamwith AI beats a 3-person team withoutWhen design tokens are properly managed and AI tools are given token context
17+button variantsTypical unchecked codebase after 18 months without a design system
···

The Token Hierarchy That Works

Tokens have layers. Global tokens are raw values — "blue-600: #2563EB". Semantic tokens map meaning onto globals — "color-brand-primary: {blue-600}". Component tokens are the last resort for one-off overrides — "button-primary-background: {color-brand-primary}". Most teams only need two layers. The third is for systems serving multiple brands.

Token LayerExampleChanges whenTeam size needed
Globalblue-600: #2563EBBrand rebrand (rare)Any
Semanticcolor-brand-primary: {blue-600}Design language shiftsAny
Componentbutton-primary-bg: {color-brand-primary}Component-specific overrides3+ designers

For a team under 5 engineers, global and semantic tokens are sufficient. Add component tokens only when you are managing multiple themes or white-label products.

···

shadcn/ui Changed the Game

Before shadcn/ui, the dominant model was: install a component library, fight against its opinions, override CSS specificity until your product looks less like their defaults. shadcn/ui introduced a different model: copy the component source into your project. You own the code. There is nothing to override because there are no vendor styles fighting your styles.

This matters enormously for design systems work because it means your tokens can propagate cleanly. shadcn/ui components are built on Radix UI primitives — accessible, headless, unstyled — with Tailwind classes wiring them to whatever token values you have configured. When your CSS variables (which are your design tokens) change, every shadcn component changes with them.

The practical workflow: define your semantic color tokens as CSS custom properties, configure Tailwind to use those custom properties as color aliases, then run the shadcn/ui CLI to scaffold components. Your components inherit your tokens automatically.

···

Supernova for Token Management at Scale

Once your token set grows beyond 50 entries — or once you have a dedicated designer maintaining Figma variables — you need tooling that keeps Figma and code in sync. Supernova.io is the current best answer for this.

Supernova connects directly to Figma variables, transforms them into any output format (CSS custom properties, Tailwind config, Swift, Kotlin), and generates a PR to your repository on every sync. The feedback loop from design decision to deployed token shrinks from days to minutes.

The alternative, Style Dictionary by Amazon, is powerful but requires engineering time to configure and maintain. Supernova trades some flexibility for the productivity of a managed tool. For teams where a designer is the primary token author, Supernova is the right call.

Supernova vs Style Dictionary
  • Supernova: managed, Figma-native, minimal engineering overhead, $99-$299/month
  • Style Dictionary: open source, fully configurable, requires engineering ownership
  • Supernova wins when: designers own the tokens and engineers should not need to touch it
  • Style Dictionary wins when: you have complex multi-brand transform logic
···

The AI Tooling Problem No One Talks About

v0 by Vercel, Bolt by StackBlitz, Lovable — these tools can generate a polished-looking React component in seconds. They are genuinely useful. They are also quietly destroying design system consistency on teams that have not thought about this problem.

When an AI tool generates a component, it does not know your design tokens exist. It invents inline values — hardcoded hex codes, arbitrary spacing numbers, font sizes that do not match your type scale. The generated component looks right in isolation and looks wrong in context.

The fix is not to avoid AI-generated components. The fix is to give the AI your token context before it generates anything.
Practical design systems work in the AI era

Practically, this means maintaining a tokens.md or DESIGN_SYSTEM.md file at the root of your repo that documents every token name and its value. When opening a new Bolt or v0 session, paste this context into the chat before asking for components. Bolt and v0 both support system prompts or context files — use them. The generated components will reference your tokens instead of inventing their own values.

Brad Frost's Atomic Design taxonomy — atoms, molecules, organisms, templates, pages — remains the clearest mental model for organising component hierarchies. The AI tools do not negate it; they accelerate the production of atoms and molecules while leaving the compositional work to engineers.

···

The Minimum Viable Design System for a 2-Person Team

What to build, in this order

01
Color and spacing tokens

Define 12-20 semantic color tokens (brand, neutral, feedback states) and a spacing scale (4px base, multiples of 4). This alone eliminates most inconsistency.

02
Typography scale

Pick one typeface. Define 5-6 type sizes (xs through 3xl) with corresponding line heights. Wire them to Tailwind or CSS variables.

03
Core atoms

Button (3 variants: primary, secondary, ghost), Input, Badge, and a Link component. These cover 80% of UI surface area.

04
A tokens.md for AI context

Document every token name, value, and intended use. This file is your AI prompt context. Keep it current or the AI will drift.

05
A simple storybook or Ladle setup

Not for documentation-theatre. For catching visual regressions before they ship. Even 10 stories covering your core atoms pay for themselves in the first month.

···

What to Avoid

Do not build a design system before you have something to ship. If you are pre-product, you are optimising for consistency you do not have yet. Build the product first, extract patterns second. The design system should emerge from real UI decisions, not precede them.

Do not build a documentation site before you have three engineers using the components. Documentation is only valuable when there are people who need to discover things they do not already know. A two-person team knows everything — write the docs when the third person joins.

Do not use a component library as a design system proxy. Material UI, Chakra UI, Mantine — these are starting points, not systems. They give you components. They do not give you tokens, they do not reflect your brand decisions, and they do not solve the AI-context problem. Use them as primitives if it speeds up early development, but plan to own your token layer regardless.

···

Design Token Architecture: Primitive → Semantic → Component

A flat list of CSS variables is not a token system. A token system has hierarchy: primitive tokens define raw values, semantic tokens give values meaning, component tokens apply values to specific UI contexts. Without this hierarchy, changing your brand colour requires updating 40 variables instead of one.

Primitive tokens are the source of truth for raw values: --color-blue-500: #3b82f6, --spacing-4: 16px, --font-size-base: 16px. These are never referenced in component CSS directly. Semantic tokens assign meaning: --color-action-primary: var(--color-blue-500), --spacing-component-padding: var(--spacing-4). Component tokens are the most specific: --button-background: var(--color-action-primary), --button-padding-x: var(--spacing-component-padding). A component only references its own component tokens. A semantic token change propagates through all components that reference it automatically.

Tool-level: Style Dictionary (by Amazon) transforms a JSON token definition into CSS variables, iOS Swift constants, Android XML, and Kotlin in a single build step. Define tokens once; generate platform-specific outputs. W3C Design Token Community Group is standardising the token format — Style Dictionary 4.x supports the draft spec, which is the right format to adopt for new systems.

Token tierExampleChanged byConsumed by
Primitive--color-blue-500: #3b82f6Brand/design decisionSemantic tokens only
Semantic--color-action-primary: var(--color-blue-500)UX decision (e.g., rebrand)Component tokens only
Component--button-bg: var(--color-action-primary)Component design decisionComponent CSS/styles
···

Component API Design: Compound Components, Render Props, and Slots

How you design component APIs determines whether your design system is a joy or a burden to use. Three patterns dominate React component API design, each suited to different flexibility requirements. Teams building the design system should understand all three; consumers should not need to. For a concrete example of design system ROI, see our analysis of design system return on investment evidence.

Compound components use React Context to share state between parent and child components that are used together. A Select component exposes Select.Trigger, Select.Content, and Select.Item as named exports. The parent holds the open/value state; children access it via context. This pattern gives consumers full control over composition without prop drilling. Radix UI and Headless UI are built entirely on this pattern.

Render props: the component exposes a prop that accepts a function returning JSX. The component calls this function with its internal state. This pattern is older (pre-hooks) but still useful for cases where you need to share non-visual state (form validation state, drag state) with arbitrary consumer code. The downside is readability — deeply nested render props become illegible.

Slots (via children overloading): the component accepts specific named props that accept ReactNode, allowing consumers to inject content at named injection points. This is React's answer to Vue's slot system. A Dialog component accepts title, description, and footer as ReactNode props. The component controls layout; consumers control content. Simpler than compound components for layout-oriented components that do not need shared state.

  • Default to compound components for interactive components (select, menu, accordion, tabs)
  • Use slots for layout components that need content injection without state sharing
  • Avoid render props for new components — hooks solve most of the same problems more readably
  • Export all compound component parts as named exports — do not force consumers into dot notation
  • Forward refs on all components — consumers building complex interactions will need them
···

Figma-to-Code with Code Connect

Figma Code Connect (released 2024) closes the gap between design intent and implementation reality. Without it, a developer inspecting a Figma component sees CSS properties and asset paths — not the actual component API. With Code Connect, hovering over a Figma component shows the real import and usage code from your codebase. For teams that have invested in a design system, this is the single most impactful workflow improvement available. The full setup process and configuration options are documented in our Figma Code Connect guide.

The Code Connect configuration maps Figma component variants to component prop values. A Figma Button with variant="Primary" maps to <Button variant="primary" />. A Figma Button with size="Large" maps to <Button size="lg" />. The mapping is a TypeScript file checked into your design system repository alongside the component code. When the component API changes, the Code Connect file changes with it.

···

shadcn/ui vs Custom: When to Use Each

shadcn/ui is not a component library in the traditional sense — it is a collection of component recipes that you copy into your own codebase and own. This distinction matters: you are not taking a dependency on a package; you are taking a starting point. The benefit is full control with no upgrade path concerns. The cost is that you own the code and need to maintain it.

Use shadcn/ui when: you are starting a new project and want accessible, well-designed components without building from scratch; your team has the capacity to customise and maintain the copied code; and your design requirements are close enough to shadcn's defaults that customisation is incremental rather than wholesale.

Build custom when: your brand requirements are significantly divergent from shadcn defaults (custom animation patterns, unusual component compositions); you are building a public design system that will be published as a package for external consumers; or you need components that shadcn does not cover and the copy-in model creates more maintenance overhead than a clean implementation.

···

Scaling from 5 to 50 Components

A design system at 5 components and a design system at 50 components are different products. At 5 components, documentation is a README and a few Storybook stories. At 50 components, documentation is a dedicated site, component composition guidelines, migration guides, and a changelog. The transition is not automatic — it requires deliberate investment at around the 15-20 component threshold.

The scaling inflection points: at 10 components, establish naming conventions before adding more (ButtonPrimary vs Button[variant=primary] — pick one and enforce it). At 20 components, add a contribution guide and automated visual regression testing (Chromatic or Percy). At 30 components, start versioning the system with SemVer and maintain a changelog — breaking changes need migration guides. At 50 components, consider splitting into packages (primitives, forms, data display) to avoid consumers importing the entire system for a single component.

Documentation-driven development inverts the usual process: write the component's API documentation and usage examples before writing the implementation. This forces clarity on the component's contract before code exists. It also produces the Storybook stories as a byproduct — the documentation examples become the stories. Teams that adopt this pattern report higher API consistency and fewer breaking changes because the contract is visible and critiqued before it is locked in code.

faster feature development after design system investmentTeams with a mature design system (20+ components, documented, token-based) ship UI features 4-6x faster than teams building from scratch
···

Measuring Design System Impact

A design system without adoption metrics is a library nobody uses. Track: component adoption rate (what percentage of UI instances use system components vs custom implementations), designer-to-developer handoff time (how long from Figma approval to working code), visual consistency score (percentage of pages that pass automated visual regression tests), and new feature velocity (time from design to production for features built with system components vs without). For teams also measuring the broader ROI of design systems, these metrics provide the hard data that justifies continued investment.

The adoption curve follows a predictable pattern: early adopters use the system immediately, the middle majority adopts when the system covers their common use cases (usually around 20-30 components), and holdouts never adopt unless their existing custom components are deprecated. The most effective adoption strategy is not mandating usage — it is making the system so convenient that using it is faster than building custom. When engineers reach for the design system because it saves them time, not because a policy requires it, you have succeeded.

The best design systems are not the ones with the most components. They are the ones where engineers never think about design decisions because the system has already made them — correctly, consistently, and accessibly.
Build with us

Need this kind of thinking applied to your product?

We build AI agents, full-stack platforms, and engineering systems. Same depth, applied to your problem.

Newsletter

Enjoyed this? Get the weekly digest.

Research highlights and AI news, delivered every Thursday. No spam.

Loading comments...