Talk To Connekz
CNEX
Back to Blog
DesignJAN 28, 2026·12 min read

Building Design Systems at Scale: Lessons from the Trenches

How we built a design system that serves 12 product teams across 3 time zones — and the hard-won lessons that made it work.

Design SystemsComponent LibrariesUI/UXEngineering Culture
Share

Why Most Design Systems Fail

Here's an uncomfortable truth: most design systems die. Not with a dramatic failure, but with a slow fade into irrelevance as teams quietly stop using them and build their own components instead.

After building (and, honestly, failing at) design systems at three different companies, we've identified the pattern that separates living systems from abandoned ones.

A design system is not a component library. It's a shared language between design and engineering, and like any language, it dies if nobody speaks it.

The Foundation: Principles Over Pixels

Before writing a single line of CSS, we established five non-negotiable principles:

  1. Adoption is optional — If teams choose our system, it's because it's genuinely better than the alternative
  2. Composition over configuration — Small, focused primitives that compose into complex patterns
  3. Accessible by default — WCAG 2.1 AA compliance is not a feature, it's a baseline
  4. Performance budget — Every component has a bundle size budget, enforced in CI
  5. Documentation is the product — If it's not documented, it doesn't exist

Tip

The single most impactful decision we made was making adoption optional. It forced us to build something genuinely useful rather than mandating usage through organisational authority.

Architecture Decisions

Token-First Design

Everything starts with design tokens. Colours, spacing, typography, shadows — all defined as tokens that flow from Figma to code through an automated pipeline.

{
  "color": {
    "primary": {
      "50": { "value": "#eef6ff", "type": "color" },
      "500": { "value": "#2f7bff", "type": "color" },
      "700": { "value": "#0d44e1", "type": "color" },
      "900": { "value": "#14338f", "type": "color" }
    },
    "semantic": {
      "success": { "value": "{color.secondary.500}", "type": "color" },
      "warning": { "value": "#f59e0b", "type": "color" },
      "error": { "value": "#ef4444", "type": "color" }
    }
  },
  "spacing": {
    "xs": { "value": "4px", "type": "spacing" },
    "sm": { "value": "8px", "type": "spacing" },
    "md": { "value": "16px", "type": "spacing" },
    "lg": { "value": "24px", "type": "spacing" },
    "xl": { "value": "32px", "type": "spacing" }
  }
}

Component API Design

We follow a strict API design philosophy for every component:

<script setup lang="ts">
// Every component follows this pattern:
// 1. Props with sensible defaults
// 2. Named slots for composition
// 3. Emits for parent communication
// 4. Expose for imperative access (rare)

interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
}

withDefaults(defineProps<ButtonProps>(), {
  variant: 'primary',
  size: 'md',
  disabled: false,
  loading: false,
});

defineEmits<{
  click: [event: MouseEvent];
}>();
</script>

Info

We enforce a rule: no component may have more than 8 props. If you need more, you're building a page, not a component. Decompose further.

The Hardest Part: Governance Without Bureaucracy

Design systems need governance to stay coherent, but too much process kills velocity. We landed on a model we call "Propose, Prove, Promote":

Propose

Any engineer or designer can propose a new component or pattern. They open an RFC (Request for Comments) — a lightweight document answering three questions:

  1. What problem does this solve?
  2. Who else has this problem?
  3. What alternatives did you consider?

Prove

The proposer builds a working prototype. Not a full production component — just enough to validate the approach. This runs for 2-4 weeks in a real product.

Promote

If the prototype proves valuable, it enters the design system backlog. A rotating "Systems Guild" of 4 engineers (one from each product area) reviews and builds the production version.

Our contribution rate went from 2 components per quarter to 8 after introducing this model. More importantly, rejection rate dropped to near zero because components are battle-tested before they enter the system.

Measuring Success

Adoption metrics tell you if teams are using the system. Quality metrics tell you if they should be.

What We Track

  • Component coverage: What percentage of UI elements in production come from the design system? (Target: >80%)
  • Time to first component: How long does it take a new engineer to use their first design system component? (Target: <1 hour)
  • Contribution velocity: How many components are contributed per quarter?
  • Bug escape rate: How many component bugs make it to production? (Target: <2 per quarter)
  • Bundle impact: Total JS/CSS added by design system components per page

Current Numbers

After 18 months:

  • 87% component coverage across all products
  • 23 minutes average time to first component for new hires
  • 8-10 components contributed per quarter
  • 1.2 bugs escaped to production last quarter
  • 34KB average bundle impact per page (gzipped)

Lessons Learned the Hard Way

1. Don't Abstract Too Early

We initially built a highly configurable DataTable component with 23 props. Nobody used it because it was harder to understand than writing a custom table. We replaced it with three focused components: SimpleTable, SortableTable, and PaginatedTable. Adoption tripled overnight.

2. Documentation Must Include "Why Not"

For every component, we document when not to use it. This builds trust. Teams know we're not trying to force everything through the design system.

3. Invest in Dev Tooling

Our Storybook instance has:

  • Live Figma embeds next to every component
  • Accessibility audit results per story
  • Bundle size badges
  • Copy-paste-ready code snippets with real data

This investment paid for itself within the first month.

4. Dark Mode from Day One

Retrofitting dark mode into an existing design system is a nightmare. We learned this the hard way at a previous company. At CNEX, dark mode support was a day-one requirement for every component. It's enforced in our CI pipeline — if a component doesn't pass visual regression in both themes, it doesn't ship.

Warning

If you're building a design system today and not supporting dark mode from the start, you're creating technical debt that will be extremely expensive to pay off later.

What's Next for Our System

We're currently working on three major initiatives:

  1. AI-assisted component generation — Using our design tokens and component patterns to generate new component variants automatically
  2. Cross-platform tokens — Extending our token pipeline to iOS (SwiftUI) and Android (Compose)
  3. Usage analytics dashboard — Real-time visibility into which components are used where, helping us prioritise maintenance

The design system is never "done." It evolves with the products it serves, the teams that use it, and the platforms it targets. That's what makes it a living system rather than a static library.


Building a design system for your organisation? Let's talk about how we can help.

C

Written by

CNEX Team

Building the next generation of enterprise software at CNEX. Passionate about AI, cloud-native architecture, and elegant solutions to complex problems.