Color System Repair

Visual Arts & Design Intermediate web-polish-skills universal
0 Upvotes
3 Views
0 Downloads
856 Words

Description

Diagnose and repair a site's color system by consolidating a single neutral scale, mapping semantic tokens, and enforcing contrast and dark-mode consistency.

When to Use

I need to fix color chaos on my site | Consolidate my color palette into a single neutral scale | Map semantic colors for UI elements | Ensure accessible contrast and dark mode consistency | Audit a site for color consistency and tokens

Use Cases

Consolidate near-duplicate grays into one neutral scale | Map semantic colors for links, buttons, and alerts | Enforce accessible contrast for text and UI | Harmonize colors across light and dark modes | Replace hardcoded colors with design tokens

SKILL.md Content

---
name: color-system-repair
description: "Diagnose and repair a site's color system by consolidating a single neutral scale, mapping semantic tokens, and enforcing contrast and dark-mode consistency."
metadata:
  tags: "visual-design, color-system, design-tokens, color-contrast, dark-mode, web-accessibility, ui-consistency"
  source: "https://skilldb.dev/skills/web-polish-skills/color-system-repair"
  pack: "web-polish-skills"
  category: "Visual Arts & Design"
---

# Color System Repair

## When to use this skill
Use when the user says things like:
- "I need to fix color chaos on my site"
- "Consolidate my color palette into a single neutral scale"
- "Map semantic colors for UI elements"
- "Ensure accessible contrast and dark mode consistency"
- "Audit a site for color consistency and tokens"


You are a color specialist who fixes the visual incoherence caused by vibecoded color
decisions. Every AI prompt generates its own color values — `#f1f5f9` here, `#f3f4f6`
there, `#fafafa` somewhere else — and the accumulated result is a site with 30 "slightly
different grays" that make everything feel subtly wrong.

## The Diagnosis

### What Bad Color Looks Like

- **Near-duplicate values.** `#333333`, `#2d2d2d`, `#374151`, `#3f3f46` all used as "dark text"
  across different components. The differences are invisible but the inconsistency is real.
- **No semantic mapping.** The same blue is used for links, primary buttons, info alerts, and
  decorative backgrounds. When one needs to change, everything changes.
- **Orphan colors.** A green that appears exactly once on a success badge. A specific purple
  used in one header. Colors with no family or purpose.
- **Contrast failures.** Light gray text on white backgrounds. Low-contrast placeholder text.
  Colored buttons with unreadable text.
- **Partial dark mode.** Some components respect dark mode, others have hardcoded white backgrounds.

### The Audit

```bash
# Extract every color value
grep -roh '#[0-9a-fA-F]\{3,8\}' src/ --include="*.tsx" --include="*.css" --include="*.scss" | sort | uniq -c | sort -rn

# Extract Tailwind color classes
grep -roh '\(bg\|text\|border\|ring\|shadow\)-\(gray\|slate\|zinc\|neutral\|stone\|red\|orange\|amber\|yellow\|lime\|green\|emerald\|teal\|cyan\|sky\|blue\|indigo\|violet\|purple\|fuchsia\|pink\|rose\)-[0-9]*' src/ | sort | uniq -c | sort -rn

# Find hardcoded colors (not using variables/tokens)
grep -rn 'color:\s*#' src/ --include="*.css" --include="*.scss"
grep -rn 'style.*color.*#' src/ --include="*.tsx" --include="*.jsx"
```

## The Fix: A Minimal, Purposeful Palette

### Step 1: Choose Your Neutral Scale

Every site needs exactly ONE neutral scale. Pick based on undertone:

```
Slate  (blue undertone)  — Cool, techy, modern
Gray   (true neutral)    — Safe, works everywhere
Zinc   (slightly warm)   — Balanced, slightly sophisticated
Neutral (pure neutral)   — Clean, no bias
Stone  (warm undertone)  — Earthy, editorial, warm
```

Consolidate ALL grays/near-blacks/near-whites to your chosen scale:

```css
/* BEFORE: 5 different "grays" from different Tailwind palettes */
.card      { background: #f1f5f9; }  /* slate-100 */
.sidebar   { background: #f3f4f6; }  /* gray-100 */
.panel     { background: #fafafa; }  /* neutral-50 */
.dropdown  { background: #f4f4f5; }  /* zinc-100 */
.tooltip   { background: #f5f5f4; }  /* stone-100 */

/* AFTER: one scale, one "light surface" token */
.card, .sidebar, .panel, .dropdown, .tooltip {
  background: var(--color-bg-secondary); /* maps to zinc-100: #f4f4f5 */
}
```

### Step 2: Define Color Roles

Every color must have a job. If it doesn't have a role, it doesn't belong:

```css
:root {
  /* === BACKGROUNDS === */
  --color-bg-primary:    #ffffff;           /* Main content area */
  --color-bg-secondary:  var(--zinc-50);    /* Cards, sidebars, sections */
  --color-bg-tertiary:   var(--zinc-100);   /* Inset areas, table headers */
  --color-bg-inverse:    var(--zinc-900);   /* Dark sections, tooltips */

  /* === TEXT === */
  --color-text-primary:    var(--zinc-900);   /* Headings, primary content */
  --color-text-secondary:  var(--zinc-600);   /* Supporting text, descriptions */
  --color-text-tertiary:   var(--zinc-400);   /* Placeholders, disabled text */
  --color-text-inverse:    #ffffff;            /* Text on dark backgrounds */

  /* === BORDERS === */
  --color-border-default:  var(--zinc-200);   /* Cards, inputs, dividers */
  --color-border-strong:   var(--zinc-300);   /* Emphasized borders */
  --color-border-subtle:   var(--zinc-100);   /* Barely-visible dividers */

  /* === INTERACTIVE === */
  --color-primary-50:  #eff6ff;     /* Primary tinted background */
  --color-primary-100: #dbeafe;     /* Primary hover background */
  --color-primary-500: #3b82f6;     /* Primary default */
  --color-primary-600: #2563eb;     /* Primary hover */
  --color-primary-700: #1d4ed8;     /* Primary active */

  /* === STATUS === */
  --color-success-bg:     #f0fdf4;  --color-success:     #22c55e;  --color-success-text:  #166534;
  --color-warning-bg:     #fffbeb;  --color-warning:     #f59e0b;  --color-warning-text:  #92400e;
  --color-error-bg:       #fef2f2;  --color-error:       #ef4444;  --color-error-text:    #991b1b;
  --color-info-bg:        #eff6ff;  --color-info:        #3b82f6;  --color-info-text:     #1e40af;
}
```

### Step 3: Dark Mode (If Applicable)

Dark mode means remapping semantic tokens, not inverting colors:

```css
[data-theme="dark"], .dark {
  --color-bg-primary:    var(--zinc-950);
  --color-bg-secondary:  var(--zinc-900);
  --color-bg-tertiary:   var(--zinc-800);
  --color-bg-inverse:    var(--zinc-100);

  --color-text-primary:    var(--zinc-50);
  --color-text-secondary:  var(--zinc-400);
  --color-text-tertiary:   var(--zinc-500);
  --color-text-inverse:    var(--zinc-900);

  --color-border-default:  var(--zinc-800);
  --color-border-strong:   var(--zinc-700);
  --color-border-subtle:   var(--zinc-800);

  /* Primary gets slightly lighter for dark backgrounds */
  --color-primary-500: #60a5fa;
  --color-primary-600: #3b82f6;
}
```

### Step 4: Contrast Verification

Check every text/background combination:

```
PASS: zinc-900 on white      = 17.4:1  (AAA)
PASS: zinc-600 on white      = 5.7:1   (AA)
PASS: zinc-400 on white      = 3.5:1   (AA Large only — only for large text!)
FAIL: zinc-300 on white      = 2.6:1   (FAIL — never use for text)

PASS: white on primary-600   = 7.8:1   (AAA)
PASS: white on primary-500   = 4.6:1   (AA)
FAIL: white on primary-400   = 3.2:1   (FAIL — button text won't be readable)
```

Tools: Use browser DevTools color picker, or a contrast checker extension. Every text
element must pass WCAG AA (4.5:1 for normal text, 3:1 for large text).

### Step 5: Mechanical Replacement

Replace hardcoded colors file by file:

```
#f9fafb, #fafafa, #f3f4f6, #f4f4f5 → var(--color-bg-secondary)
#333, #374151, #27272a, #1f2937    → var(--color-text-primary)
#6b7280, #71717a, #52525b          → var(--color-text-secondary)
#e5e7eb, #e4e4e7, #d4d4d8          → var(--color-border-default)
```

## Anti-Patterns

- Don't use opacity to create lighter shades. `bg-blue-500/20` looks different from `bg-blue-100`
  and breaks when layered on non-white backgrounds.
- Don't mix Tailwind color families (slate + gray + zinc in the same component). Pick one.
- Don't use `text-gray-300` for text on white backgrounds. It fails contrast.
- Don't hardcode dark mode colors. Use CSS variables that remap in dark context.
- Don't create custom colors for one-off elements. Use the palette you have.
- Don't use more than one accent color unless the brand requires it. One primary + neutral scale
  handles 95% of UI needs.