Company Logo
Company Logo

Build A Design System For Your Startup with AI in Days, Not Months

hero image for article
15 Aug 2025 10 mins read

For early-stage startups, building a design system feels like a luxury they can't afford. With limited resources and tight deadlines, most teams prioritize shipping features over establishing design foundations. The traditional approach requires dedicated designers, months of planning, and significant engineering bandwidth – resources that simply don't exist in the startup world.

But here's the reality: inconsistent UI hurts user experience and slows down development in the long run. What if I told you that AI could help you build a production-ready design system in days, not months, without needing a dedicated design team?

In this guide, we'll leverage AI tools to build a comprehensive design system using React, Tailwind CSS, and Radix UI primitives. By the end, you'll have a solid foundation that scales with your startup – all built with AI as your design partner, not an expensive design team.

Setting Up the Foundation

Let's start with a clean Vite + React + Tailwind setup. This gives us the perfect foundation for rapid development.

You can either set this up manually or use your favorite AI coding tool to speed up the process:

Manual Setup

# Create a new Vite project
npm create vite@latest my-design-system -- --template react-ts
cd my-design-system

# Install dependencies
npm install

# Add Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# Install Radix UI primitives (we'll add more as needed)
npm install @radix-ui/react-slot class-variance-authority clsx tailwind-merge

AI-Powered Setup

Alternatively, you can use AI tools like Claude Code, Cursor, or GitHub Copilot to automate this setup. Here's a prompt you can use:

Set up a new React TypeScript project for a design system with the following requirements:

PROJECT SETUP:
- Use Vite as the build tool with React TypeScript template
- Configure Tailwind CSS with PostCSS
- Install design system dependencies: @radix-ui/react-slot, class-variance-authority, clsx, tailwind-merge

CONFIGURATION:
- Set up tailwind.config.js to scan src directory
- Add Tailwind directives to index.css
- Ensure TypeScript is properly configured

Please provide the complete setup commands and initial configuration files.

Most AI coding tools will generate the exact commands and even create the initial configuration files for you, saving valuable setup time.

Update your tailwind.config.js to include the src directory:

/** @type {import('tailwindcss').Config} */
export default {
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
};

And add Tailwind directives to your src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Building Your Design Token System

Here's where things get interesting. A robust design system needs consistent tokens for colors, typography, spacing, and more. Let's create a comprehensive token system that supports both light and dark themes.

Update your tailwind.config.js with a complete token system:

/** @type {import('tailwindcss').Config} */
export default {
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        // Semantic color system
        background: {
          primary: 'hsl(var(--background-primary))',
          secondary: 'hsl(var(--background-secondary))',
          tertiary: 'hsl(var(--background-tertiary))',
        },
        foreground: {
          primary: 'hsl(var(--foreground-primary))',
          secondary: 'hsl(var(--foreground-secondary))',
          muted: 'hsl(var(--foreground-muted))',
        },
        border: {
          primary: 'hsl(var(--border-primary))',
          secondary: 'hsl(var(--border-secondary))',
          focus: 'hsl(var(--border-focus))',
        },
        accent: {
          primary: 'hsl(var(--accent-primary))',
          secondary: 'hsl(var(--accent-secondary))',
          hover: 'hsl(var(--accent-hover))',
        },
        destructive: {
          primary: 'hsl(var(--destructive-primary))',
          foreground: 'hsl(var(--destructive-foreground))',
        },
        success: {
          primary: 'hsl(var(--success-primary))',
          foreground: 'hsl(var(--success-foreground))',
        },
        warning: {
          primary: 'hsl(var(--warning-primary))',
          foreground: 'hsl(var(--warning-foreground))',
        },
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
      },
      fontSize: {
        xs: ['0.75rem', { lineHeight: '1rem' }],
        sm: ['0.875rem', { lineHeight: '1.25rem' }],
        base: ['1rem', { lineHeight: '1.5rem' }],
        lg: ['1.125rem', { lineHeight: '1.75rem' }],
        xl: ['1.25rem', { lineHeight: '1.75rem' }],
        '2xl': ['1.5rem', { lineHeight: '2rem' }],
        '3xl': ['1.875rem', { lineHeight: '2.25rem' }],
        '4xl': ['2.25rem', { lineHeight: '2.5rem' }],
        '5xl': ['3rem', { lineHeight: '1' }],
        '6xl': ['3.75rem', { lineHeight: '1' }],
      },
      spacing: {
        0.5: '0.125rem',
        1.5: '0.375rem',
        2.5: '0.625rem',
        3.5: '0.875rem',
        18: '4.5rem',
        22: '5.5rem',
      },
      borderRadius: {
        xs: '0.125rem',
        sm: '0.25rem',
        base: '0.375rem',
        md: '0.5rem',
        lg: '0.75rem',
        xl: '1rem',
        '2xl': '1.5rem',
      },
      boxShadow: {
        xs: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
        sm: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
        base: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
        lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
        xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
      },
      animation: {
        'fade-in': 'fadeIn 0.2s ease-in-out',
        'slide-up': 'slideUp 0.3s ease-out',
        'slide-down': 'slideDown 0.3s ease-out',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        slideUp: {
          '0%': { transform: 'translateY(10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
        slideDown: {
          '0%': { transform: 'translateY(-10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
    },
  },
  plugins: [],
};

Now create the CSS custom properties for light and dark themes in your src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    /* Light theme */
    --background-primary: 0 0% 100%;
    --background-secondary: 210 40% 98%;
    --background-tertiary: 210 40% 96%;

    --foreground-primary: 222.2 84% 4.9%;
    --foreground-secondary: 215.4 16.3% 46.9%;
    --foreground-muted: 215.4 16.3% 66.9%;

    --border-primary: 214.3 31.8% 91.4%;
    --border-secondary: 214.3 31.8% 85.4%;
    --border-focus: 221.2 83.2% 53.3%;

    --accent-primary: 221.2 83.2% 53.3%;
    --accent-secondary: 210 40% 96%;
    --accent-hover: 221.2 83.2% 48.3%;

    --destructive-primary: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;

    --success-primary: 142.1 76.2% 36.3%;
    --success-foreground: 210 40% 98%;

    --warning-primary: 47.9 95.8% 53.1%;
    --warning-foreground: 26 83.3% 14.1%;
  }

  .dark {
    /* Dark theme */
    --background-primary: 222.2 84% 4.9%;
    --background-secondary: 217.2 32.6% 17.5%;
    --background-tertiary: 216 33% 20%;

    --foreground-primary: 210 40% 98%;
    --foreground-secondary: 215 20.2% 65.1%;
    --foreground-muted: 215.4 16.3% 56.9%;

    --border-primary: 217.2 32.6% 17.5%;
    --border-secondary: 217.2 32.6% 25.5%;
    --border-focus: 217.2 91.2% 59.8%;

    --accent-primary: 217.2 91.2% 59.8%;
    --accent-secondary: 217.2 32.6% 17.5%;
    --accent-hover: 217.2 91.2% 54.8%;

    --destructive-primary: 0 72.2% 50.6%;
    --destructive-foreground: 210 40% 98%;

    --success-primary: 142.1 70.6% 45.3%;
    --success-foreground: 210 40% 98%;

    --warning-primary: 47.9 95.8% 53.1%;
    --warning-foreground: 26 83.3% 14.1%;
  }
}

* {
  border-color: hsl(var(--border-primary));
}

body {
  background-color: hsl(var(--background-primary));
  color: hsl(var(--foreground-primary));
  font-feature-settings: 'rlig' 1, 'calt' 1;
}

Note that the colors used above are just examples. You can use any color scheme you want. In fact this is the step where you should spend some time thinking about what your branding should be like.

Leveraging AI for Radix UI Component Design

Now comes the fun part – using AI to help us build components. Radix UI gives us the behavior and accessibility, while AI helps us craft the perfect styling and API.

Here's the detailed prompt I use when asking AI to create a component using Radix UI primitives:

Create a [COMPONENT_NAME] component using Radix UI primitives with the following requirements:

DESIGN SYSTEM CONTEXT:
- Use the predefined Tailwind CSS tokens from our design system
- Colors: background-*, foreground-*, border-*, accent-*, destructive-*, success-*, warning-*
- Typography: font sizes from xs to 6xl with proper line heights
- Spacing: follow the extended spacing scale
- Border radius: use xs, sm, base, md, lg, xl, 2xl
- Shadows: xs, sm, base, lg, xl
- Support both light and dark themes automatically

TECHNICAL REQUIREMENTS:
- Use TypeScript with proper type definitions
- Implement class-variance-authority (cva) for variant management
- Use clsx and tailwind-merge for conditional classes
- Export both the base component and any sub-components
- Include forwardRef for proper ref handling
- Follow React best practices

COMPONENT SPECIFICATIONS:
- Variants: [specify variants like size, variant, etc.]
- States: hover, focus, active, disabled
- Accessibility: ensure proper ARIA attributes and keyboard navigation
- Animation: use the predefined animations where appropriate

STYLING APPROACH:
- Use semantic color tokens, not hardcoded colors
- Implement proper focus states with border-focus
- Add smooth transitions for interactive states
- Ensure proper contrast ratios
- Make it responsive if needed

Please provide:
1. The complete component code with TypeScript
2. Usage examples showing different variants
3. Brief explanation of customization options

Let's see this in action with a Button component:

// src/components/Button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"

const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-base text-sm font-medium ring-offset-background-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        primary: "bg-accent-primary text-white hover:bg-accent-hover",
        secondary: "bg-accent-secondary text-foreground-primary hover:bg-accent-secondary/80 border border-border-primary",
        destructive: "bg-destructive-primary text-destructive-foreground hover:bg-destructive-primary/90",
        outline: "border border-border-primary bg-background-primary hover:bg-accent-secondary",
        ghost: "hover:bg-accent-secondary",
        link: "text-accent-primary underline-offset-4 hover:underline",
      },
      size: {
        sm: "h-9 rounded-md px-3",
        base: "h-10 px-4 py-2",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "base",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button"
    return (
      <Comp
        className={twMerge(clsx(buttonVariants({ variant, size, className })))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"

export { Button, buttonVariants }

Creating Custom Components with AI

While Radix UI covers many common component patterns, your startup will inevitably need custom components that don't have Radix primitives. This is where AI truly shines – instead of spending hours crafting pixel-perfect components from scratch, you can describe what you need and get production-ready code in minutes.

The key is being specific about your requirements while leveraging the design tokens you've already established. AI excels at creating components that feel cohesive with your existing system when given clear constraints and examples.

For components that don't have Radix primitives, here's my go-to AI prompt:

Create a custom [COMPONENT_NAME] component for our React design system with the following specifications:

DESIGN SYSTEM INTEGRATION:
- Use our predefined Tailwind CSS tokens (background-*, foreground-*, border-*, etc.)
- Follow our spacing scale, typography scale, and border radius tokens
- Support automatic light/dark theme switching
- Use semantic color tokens, never hardcoded values

COMPONENT REQUIREMENTS:
- TypeScript with comprehensive type definitions
- Use class-variance-authority for variant management
- Implement proper state management (if stateful)
- Include proper accessibility attributes
- Support ref forwarding
- Handle edge cases gracefully

STYLING STANDARDS:
- Consistent with existing components
- Smooth transitions for interactive states
- Proper focus indicators using border-focus
- Responsive design if applicable
- Loading and error states if relevant

FUNCTIONALITY:
[Describe specific functionality needed]

VARIANTS NEEDED:
[List specific variants like size, color, etc.]

Please provide:
1. Complete TypeScript component implementation
2. Proper prop interface definitions
3. Usage examples with different configurations
4. Any additional utility functions needed

Let's see this in action by creating a Badge component. Here's the exact prompt I used with the template above:

Create a custom Badge component for our React design system with the following specifications:

DESIGN SYSTEM INTEGRATION:
- Use our predefined Tailwind CSS tokens (background-*, foreground-*, border-*, etc.)
- Follow our spacing scale, typography scale, and border radius tokens
- Support automatic light/dark theme switching
- Use semantic color tokens, never hardcoded values

COMPONENT REQUIREMENTS:
- TypeScript with comprehensive type definitions
- Use class-variance-authority for variant management
- Include proper accessibility attributes
- Support ref forwarding
- Handle edge cases gracefully

STYLING STANDARDS:
- Consistent with existing components
- Smooth transitions for interactive states
- Proper focus indicators using border-focus
- Responsive design if applicable

FUNCTIONALITY:
- Small inline element to display status, categories, or labels
- Should be non-interactive by default (no click handlers)
- Display short text content (single word or short phrase)
- Used for categorization, status indication, or highlighting important information
- Should be visually distinct but not overwhelming
- Support both light and dark theme variants automatically

VARIANTS NEEDED:
- variant: primary, secondary, destructive, success, warning, outline
- size: sm, base, lg

Please provide:
1. Complete TypeScript component implementation
2. Proper prop interface definitions
3. Usage examples with different configurations

Here's the Badge component that AI generated from this prompt:

// src/components/Badge.tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"

const badgeVariants = cva(
  "inline-flex items-center rounded-sm font-medium transition-colors select-none",
  {
    variants: {
      variant: {
        primary: "bg-accent-primary text-white",
        secondary: "bg-accent-secondary text-foreground-primary border border-border-primary",
        destructive: "bg-destructive-primary text-destructive-foreground",
        success: "bg-success-primary text-success-foreground",
        warning: "bg-warning-primary text-warning-foreground",
        outline: "border border-border-primary text-foreground-primary bg-background-primary",
      },
      size: {
        sm: "text-xs px-2 py-0.5 rounded-xs",
        base: "text-sm px-2.5 py-0.5 rounded-sm",
        lg: "text-base px-3 py-1 rounded-md",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "base",
    },
  }
)

export interface BadgeProps
  extends React.HTMLAttributes<HTMLSpanElement>,
    VariantProps<typeof badgeVariants> {}

const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
  ({ className, variant, size, ...props }, ref) => {
    return (
      <span
        ref={ref}
        className={twMerge(clsx(badgeVariants({ variant, size }), className))}
        {...props}
      />
    )
  }
)
Badge.displayName = "Badge"

export { Badge, badgeVariants }

Setting Up Storybook for Documentation

No design system is complete without proper documentation. Storybook is perfect for this:

# Initialize Storybook
npx storybook@latest init

# Install additional addons
npm install --save-dev @storybook/addon-docs @storybook/addon-controls @storybook/addon-a11y

Update your .storybook/main.ts:

import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-onboarding',
    '@storybook/addon-interactions',
    '@storybook/addon-docs',
    '@storybook/addon-controls',
    '@storybook/addon-a11y',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
};

export default config;

Create a story for your Button component:

// src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  parameters: {
    layout: 'centered',
    docs: {
      description: {
        component: 'A versatile button component built on top of our design system tokens.',
      },
    },
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: { type: 'select' },
      options: ['primary', 'secondary', 'destructive', 'outline', 'ghost', 'link'],
    },
    size: {
      control: { type: 'select' },
      options: ['sm', 'base', 'lg', 'icon'],
    },
  },
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
  args: {
    children: 'Button',
    variant: 'primary',
  },
};

export const AllVariants: Story = {
  render: () => (
    <div className="flex gap-4 flex-wrap">
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="link">Link</Button>
    </div>
  ),
};

export const AllSizes: Story = {
  render: () => (
    <div className="flex gap-4 items-center">
      <Button size="sm">Small</Button>
      <Button size="base">Base</Button>
      <Button size="lg">Large</Button>
      <Button size="icon">🎉</Button>
    </div>
  ),
};

Update .storybook/preview.ts to include your CSS:

import type { Preview } from '@storybook/react';
import '../src/index.css';

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
    docs: {
      toc: true,
    },
  },
  globalTypes: {
    theme: {
      description: 'Global theme for components',
      defaultValue: 'light',
      toolbar: {
        title: 'Theme',
        icon: 'circlehollow',
        items: ['light', 'dark'],
        dynamicTitle: true,
      },
    },
  },
};

export default preview;

Wrapping Up

Building a design system doesn't have to be a months-long endeavor. With the right foundation (React + Tailwind + Radix UI) and AI as your design partner, you can create a robust, scalable design system in days rather than weeks.

The key is having solid design tokens upfront – they're the foundation everything else builds on. Once those are in place, AI becomes incredibly effective at helping you create consistent, accessible components that follow your design language.

Start small with a few core components like Button, Input, and Card. Get those right, document them in Storybook, and then expand from there. Before you know it, you'll have a design system that your entire team can rely on.

The future of design systems is collaborative – designers, developers, and AI working together to create better user experiences faster than ever before.

Liked this article?

Let's connect and build something together! Or if not yet impressed, check out my work.