Design Systems for Startups: How I Built Mine Backwards
I built my design system backwards. Most advice says “wait for product-market fit.” I started with CSS custom properties on day one because a CLS bug on my personal site taught me the cost of skipping tokens: a Cumulative Layout Shift of 0.493 that took two debugging sessions to trace to an inconsistent spacing value. The fix took 15 minutes. The investigation took 3 hours. Tokens would have prevented the bug entirely.1
TL;DR
Design systems solve coordination problems. Solo developers with one project do not have coordination problems between people, but they have coordination problems between past and future selves. After building the design token system for blakecrosley.com — 10 color tokens, 13 type scale steps, 8 spacing values — I’ve learned that the right sequence is: tokens immediately (day one), patterns when they repeat three times, formal system never (for solo projects). The token investment pays for itself the first time a spacing inconsistency causes a layout bug.
My Design System: Tokens and Nothing Else
The Color Non-Palette
My entire site operates on 10 CSS custom properties with no brand colors:
:root {
--color-bg-dark: #000000;
--color-bg-elevated: #111111;
--color-bg-surface: #1a1a1a;
--color-text-primary: #ffffff;
--color-text-secondary: rgba(255,255,255,0.65);
--color-text-tertiary: rgba(255,255,255,0.4);
--color-border: rgba(255,255,255,0.1);
--color-border-subtle: rgba(255,255,255,0.05);
--color-accent: #ffffff;
}
Four transparency tiers (100%, 65%, 40%, 10%) carry the entire visual hierarchy. Every text tier passes WCAG AAA contrast against the #000000 background. No gradients, no semantic colors, no dark/light mode toggle. The constraint produces a coherent system that a component library could not improve.2
The decision to skip brand colors was a design choice, not a technical one. During my 16 product design studies, I noticed that products I respected most (Linear, Vercel, Raycast) used restrained palettes where typography did the hierarchy work.
The 8-Point Spacing System
Every spacing value derives from an 8-point base:
:root {
--spacing-xs: 0.5rem; /* 8px */
--spacing-sm: 1rem; /* 16px */
--spacing-md: 1.5rem; /* 24px */
--spacing-lg: 2rem; /* 32px */
--spacing-xl: 3rem; /* 48px */
--spacing-2xl: 4rem; /* 64px */
--spacing-3xl: 6rem; /* 96px */
--spacing-4xl: 8rem; /* 128px */
}
No arbitrary values. The system contains eight steps. If a layout requires a spacing that does not exist in the system, the design is wrong, not the system.
The --spacing-2xs Bug
I learned the rule the hard way. I used --spacing-2xs for a subtitle margin-top. The token did not exist in my :root definition. CSS custom properties fail silently — the browser applied no margin rather than throwing an error. The subtitle collapsed against the headline, the layout shifted on load, and my Cumulative Layout Shift score jumped to 0.493.3
The debugging path: Lighthouse flagged CLS. DevTools identified the shifting element. Inspecting the computed styles revealed margin-top: 0 where I expected 4px. Searching the stylesheet for --spacing-2xs showed one usage and zero definitions.
The fix: change --spacing-2xs to --spacing-xs. The broader fix: strict adherence to the token system with no exceptions.
The Typography Scale
13 steps from 12px to 80px, using system fonts:
:root {
--font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text",
"SF Pro Display", "Helvetica Neue", Arial, sans-serif;
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.3125rem; /* 21px */
--font-size-2xl: 1.5625rem; /* 25px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
--font-size-5xl: 2.7rem; /* 43.2px */
--font-size-6xl: 3.25rem; /* 52px */
--font-size-7xl: 3.875rem; /* 62px */
--font-size-display: 5rem; /* 80px */
}
System fonts eliminate font-loading latency entirely. No FOIT (Flash of Invisible Text), no FOUT (Flash of Unstyled Text), zero network requests for fonts. The choice contributes to my 100/100 Lighthouse performance score.4
The Timing Problem
Too Early: The Premature Abstraction Trap
Seed-stage startups face extreme uncertainty about what the product will become. A design system encodes assumptions about interaction patterns, component hierarchy, and visual language. When the product pivots, the design system becomes a liability that resists change.5
A team of three engineers and one designer does not need a component library with documentation, versioning, and a Storybook instance. The coordination overhead exceeds the coordination problem.
But tokens are different. Tokens are not abstractions — they are values. A color token does not impose a component structure. A spacing token does not constrain interaction patterns. Tokens survive pivots because they operate below the component layer.
Too Late: The Design Debt Spiral
Series B companies with 30 engineers and no shared design patterns face the opposite problem. Every feature team invents its own button styles, form layouts, and spacing values. The product feels like three different applications stitched together.6
I see the same pattern at a smaller scale in my own projects. When I start a new project without copying my :root tokens, inconsistencies appear within the first week. The tokens are cheap insurance against a debt spiral that becomes expensive to unwind.
The Progressive Investment Framework
Stage 1: Tokens Only (Day One, Any Team Size)
Define and share only the foundational values. My implementation:
| Token Category | Count | Purpose |
|---|---|---|
| Colors | 10 | Background, text, border, accent |
| Typography | 13 | Font sizes from xs to display |
| Spacing | 8 | 8px base unit scale |
| Border radius | 4 | sm (8px), md (16px), lg (32px), xl (48px) |
| Transitions | 3 | fast (150ms), base (300ms), slow (600ms) |
| Layout | 3 | max-width narrow (800px), default (1400px), wide (1600px) |
41 tokens total. Zero components. Zero documentation site. The goal is preventing divergence at the value level while preserving maximum flexibility for experimentation.7
Stage 2: Pattern Extraction (When Patterns Repeat 3x)
When the same UI element appears in three distinct features, extract the pattern. My site has extracted patterns for:
- Card layouts (project cards, blog cards, social cards): consistent padding, border-radius, hover states
- Navigation underlines (nav links, breadcrumbs): scaleX(0) → scaleX(1) transition on hover
- Glassmorphism header: backdrop-filter: blur(20px) with border-bottom
Each pattern exists because I built the same thing three times and noticed the duplication. I extract patterns from production code rather than designing them in advance. Production patterns encode real requirements.8
Stage 3: Formal System (25+ Engineers, Never for Solo Projects)
At scale, the coordination problem justifies component libraries, Figma mirroring, contribution processes, and versioned changelogs. I have not reached this stage with any personal project and do not expect to. For solo developers, tokens + extracted patterns provide enough structure without the maintenance overhead of a formal system.
What I Skip Entirely
Documentation Site
Public-facing design system documentation sites consume engineering time that produces zero user value for solo projects. My “documentation” is the :root block in critical.css. Any developer (or AI agent) reading the file understands the system immediately.
Multi-Framework Support
My site uses plain CSS. No React components, no Vue wrappers, no Web Components. The tokens work in any framework because CSS custom properties are framework-agnostic. The abstraction layer is CSS itself. The No-Build Manifesto documents how this approach scales to 33 blog posts, 14 interactive components, and 9 languages — all without a bundler.
Premature Accessibility Theater
Accessibility matters — my site achieves WCAG AAA contrast on every text tier. But I built the contrast system first (tokens with known ratios) and verified compliance second. Starting with tokens that have built-in accessibility (every text tier exceeds 7:1 contrast) is more effective than auditing arbitrary color choices after the fact.9
Key Takeaways
For solo developers: - Define CSS custom properties for color, type, spacing, and transitions on day one; the 41-token investment prevents bugs and inconsistencies that cost hours to debug later - Skip component libraries, documentation sites, and Storybook; for solo projects, the maintenance overhead exceeds the coordination benefit - Extract patterns from production code when the same element appears in three places; premature extraction wastes effort on patterns that may not survive the next iteration
For startup design leads: - Start with tokens before product-market fit; tokens survive pivots because they operate below the component layer - Resist the formal system until 25+ engineers create a genuine coordination problem; formalization before that threshold creates maintenance overhead that slows iteration
References
-
Author’s CLS debugging experience. Cumulative Layout Shift of 0.493 traced to undefined
--spacing-2xstoken. Documented in MEMORY.md error entries. ↩ -
Author’s CSS custom properties from
critical.css. 10 color tokens, all derived from white-on-black opacity relationships. ↩ -
Author’s debugging experience.
--spacing-2xsused but never defined in:root. CSS custom properties fail silently, producing zero margin instead of expected value. ↩ -
Author’s typography system. 13-step font scale, system font stack. Zero font-loading latency contributing to 100/100 Lighthouse performance. ↩
-
Cagan, Marty, Inspired: How to Create Tech Products Customers Love, Wiley, 2017. Product-market fit and premature optimization. ↩
-
Curtis, Nathan, “Adopting Design Systems,” EightShapes, 2018. Design debt measurement in scaling companies. ↩
-
Author’s token inventory. 41 CSS custom properties across 6 categories defined in
critical.css. ↩ -
Frost, Brad, Atomic Design, self-published, 2016. Progressive component abstraction methodology. ↩
-
Author’s accessibility approach. WCAG AAA contrast built into token definitions (primary 21:1, secondary 13.7:1, tertiary 8.4:1). ↩