Design Token Extractor

Visual Arts & Design Intermediate web-polish-skills universal
0 Upvotes
6 Views
2 Downloads
1,064 Words

Description

Extract and consolidate colors, typography, spacing, borders, and shadows from messy codebases into a unified token system for scalable design.

When to Use

I need to extract design tokens from a messy codebase. | Help me consolidate colors, typography, and spacing into tokens. | I want to migrate CSS variables into a token system. | I need a unified design token palette for a design system. | Revise old UI values into a scalable token library.

Use Cases

Consolidate scattered color values into a single palette. | Create a minimal typography scale from font sizes and weights. | Normalize spacing and radii across components. | Migrate legacy CSS vars into a token-driven system.

SKILL.md Content

---
name: design-token-extraction
description: "Extract and consolidate colors, typography, spacing, borders, and shadows from messy codebases into a unified token system for scalable design."
metadata:
  tags: "design-systems, design-tokens, token-extraction, css-tokens, palette-consolidation, ui-consistency, reverse-engineering"
  source: "https://skilldb.dev/skills/web-polish-skills/design-token-extraction"
  pack: "web-polish-skills"
  category: "Visual Arts & Design"
---

# Design Token Extractor

## When to use this skill
Use when the user says things like:
- "I need to extract design tokens from a messy codebase."
- "Help me consolidate colors, typography, and spacing into tokens."
- "I want to migrate CSS variables into a token system."
- "I need a unified design token palette for a design system."
- "Revise old UI values into a scalable token library."


You are a design systems engineer who specializes in reverse-engineering messy codebases into
clean token systems. You take the 47 different grays, 12 font sizes, and 23 spacing values
scattered across a vibecoded site and distill them into a coherent, minimal set of design
tokens that every component can reference.

## The Problem You Solve

Vibecoded sites accumulate design debt through isolation — each prompt generates its own
hardcoded values. The result:

```css
/* Actual mess found in a vibecoded codebase */
.card { background: #f9fafb; border-radius: 8px; padding: 16px; }
.modal { background: #fafafa; border-radius: 12px; padding: 24px; }
.panel { background: #f8f9fa; border-radius: 10px; padding: 20px; }
/* Three components, three "light gray" backgrounds, three radii, three paddings */
```

Your job: consolidate these into tokens and rewire everything to use them.

## Token Extraction Process

### Step 1: Harvest All Raw Values

Scan the entire codebase and extract every visual value into categorized lists:

**Colors:** Every hex, rgb, hsl, oklch, Tailwind color class, and CSS variable reference.
Group by visual similarity (all the "almost white" backgrounds, all the "dark text" colors,
all the "blue primary" variants).

**Typography:** Every font-family, font-size, font-weight, line-height, letter-spacing.
Group by usage (headings, body, captions, code, labels).

**Spacing:** Every padding, margin, gap value. Group into the nearest clean scale values.

**Borders:** Every border-width, border-color, border-radius. Group by component type.

**Shadows:** Every box-shadow value. Group by elevation level.

**Transitions:** Every transition-duration, transition-timing-function. Group by interaction type.

**Z-indexes:** Every z-index value. Map to a logical stacking order.

### Step 2: Consolidate to a Clean Scale

For each category, reduce the harvested values to the minimum needed set:

**Colors — Target: 1 primary, 1-2 accents, 1 neutral scale, semantic states**

```css
:root {
  /* Neutral scale (consolidate all grays) */
  --color-gray-50:  #fafafa;
  --color-gray-100: #f4f4f5;
  --color-gray-200: #e4e4e7;
  --color-gray-300: #d4d4d8;
  --color-gray-400: #a1a1aa;
  --color-gray-500: #71717a;
  --color-gray-600: #52525b;
  --color-gray-700: #3f3f46;
  --color-gray-800: #27272a;
  --color-gray-900: #18181b;
  --color-gray-950: #09090b;

  /* Primary (pick the dominant brand color, normalize variants) */
  --color-primary-50:  #eff6ff;
  --color-primary-100: #dbeafe;
  --color-primary-500: #3b82f6;
  --color-primary-600: #2563eb;
  --color-primary-700: #1d4ed8;

  /* Semantic (map to standard states) */
  --color-success: #22c55e;
  --color-warning: #f59e0b;
  --color-error:   #ef4444;
  --color-info:    #3b82f6;

  /* Semantic aliases (what tokens should components actually use) */
  --color-bg-primary:    #ffffff;
  --color-bg-secondary:  var(--color-gray-50);
  --color-bg-tertiary:   var(--color-gray-100);
  --color-bg-inverse:    var(--color-gray-900);

  --color-text-primary:   var(--color-gray-900);
  --color-text-secondary: var(--color-gray-500);
  --color-text-tertiary:  var(--color-gray-400);
  --color-text-inverse:   #ffffff;
  --color-text-link:      var(--color-primary-600);

  --color-border-default: var(--color-gray-200);
  --color-border-strong:  var(--color-gray-300);
  --color-border-focus:   var(--color-primary-500);
}
```

**Typography — Target: 6-8 sizes on a ratio scale**

```css
:root {
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;

  --text-xs:   0.75rem;   /* 12px */
  --text-sm:   0.875rem;  /* 14px */
  --text-base: 1rem;      /* 16px */
  --text-lg:   1.125rem;  /* 18px */
  --text-xl:   1.25rem;   /* 20px */
  --text-2xl:  1.5rem;    /* 24px */
  --text-3xl:  1.875rem;  /* 30px */
  --text-4xl:  2.25rem;   /* 36px */

  --leading-tight:  1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.625;

  --font-normal:   400;
  --font-medium:   500;
  --font-semibold: 600;
  --font-bold:     700;
}
```

**Spacing — Target: 8px base scale**

```css
:root {
  --space-0:  0;
  --space-1:  0.25rem;  /* 4px */
  --space-2:  0.5rem;   /* 8px */
  --space-3:  0.75rem;  /* 12px */
  --space-4:  1rem;     /* 16px */
  --space-5:  1.25rem;  /* 20px */
  --space-6:  1.5rem;   /* 24px */
  --space-8:  2rem;     /* 32px */
  --space-10: 2.5rem;   /* 40px */
  --space-12: 3rem;     /* 48px */
  --space-16: 4rem;     /* 64px */
  --space-20: 5rem;     /* 80px */
  --space-24: 6rem;     /* 96px */
}
```

**Borders and Radii — Target: 3-4 radius values**

```css
:root {
  --radius-sm:   0.25rem;  /* 4px — small elements like badges */
  --radius-md:   0.375rem; /* 6px — inputs, buttons */
  --radius-lg:   0.5rem;   /* 8px — cards, containers */
  --radius-xl:   0.75rem;  /* 12px — modals, large panels */
  --radius-2xl:  1rem;     /* 16px — hero cards, feature sections */
  --radius-full: 9999px;   /* pills, avatars */

  --border-width-default: 1px;
  --border-width-thick:   2px;
}
```

**Shadows — Target: 3-4 elevation levels**

```css
:root {
  --shadow-sm:  0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md:  0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg:  0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl:  0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}
```

**Transitions — Target: 2-3 duration/easing pairs**

```css
:root {
  --duration-fast:   150ms;
  --duration-normal: 200ms;
  --duration-slow:   300ms;

  --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in:      cubic-bezier(0.4, 0, 1, 1);
  --ease-out:     cubic-bezier(0, 0, 0.2, 1);
  --ease-bounce:  cubic-bezier(0.34, 1.56, 0.64, 1);
}
```

**Z-index — Target: named layers**

```css
:root {
  --z-dropdown:  10;
  --z-sticky:    20;
  --z-overlay:   30;
  --z-modal:     40;
  --z-popover:   50;
  --z-toast:     60;
  --z-tooltip:   70;
}
```

### Step 3: Map Old Values to New Tokens

Create a migration map showing what each old value becomes:

```
OLD VALUE                → NEW TOKEN
#f9fafb, #fafafa, #f8f9fa → var(--color-bg-secondary)
#333, #334, #2d2d2d       → var(--color-text-primary)
8px, 10px, 12px radius    → var(--radius-lg)
16px, 20px, 24px padding  → var(--space-4) / var(--space-6)
```

### Step 4: Implement the Replacement

For each component file, replace hardcoded values with token references.
Work file-by-file, test after each file. This is a mechanical find-and-replace
operation — don't change component structure, just swap values for tokens.

### Step 5: Validate

After migration, visually scan every page. Look for:
- Components that got the wrong token (a heading that's now body-sized)
- Edges where old and new tokens meet (a refactored card next to an un-refactored one)
- Dark mode breakage if tokens support theming

## Tailwind-Specific Token Strategy

If the site uses Tailwind CSS, tokens go in `tailwind.config.js`:

```js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          50:  '#eff6ff',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
        },
        surface: {
          primary:   'var(--color-bg-primary)',
          secondary: 'var(--color-bg-secondary)',
          tertiary:  'var(--color-bg-tertiary)',
        },
      },
      borderRadius: {
        DEFAULT: '0.375rem',
        lg: '0.5rem',
        xl: '0.75rem',
        '2xl': '1rem',
      },
      boxShadow: {
        sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
        DEFAULT: '0 4px 6px -1px rgb(0 0 0 / 0.1)',
        lg: '0 10px 15px -3px rgb(0 0 0 / 0.1)',
      },
    },
  },
};
```

Then replace scattered classes like `bg-[#f9fafb]`, `bg-gray-50`, `bg-[#fafafa]` with a
single `bg-surface-secondary` everywhere.

## Anti-Patterns

- Don't create tokens nobody uses. Every token should map to actual component usage.
- Don't create so many semantic tokens that finding the right one is harder than hardcoding.
- Don't change visual appearance during token extraction. The site should look identical after — you're
  only changing how values are stored.
- Don't skip the harvest step. You must know what exists before you can consolidate.