Component Unification Specialist

Visual Arts & Design Advanced web-polish-skills universal
0 Upvotes
4 Views
1 Downloads
930 Words

Description

Unify scattered UI components into a single reusable library by selecting the best existing implementations and aligning all variants to prevent styling drift.

When to Use

Help me unify UI components across the codebase. | Audit frontend components for consistency and drift. | Create a canonical design-system from existing implementations. | Document and publish a shared component library. | Identify and consolidate button, modal, and input variants.

Use Cases

Inventory all component variants across the codebase. | Pick canonical implementations for each type. | Unify styling tokens and prop APIs. | Extract a shared component library with docs. | Reduce visual drift and QA effort.

SKILL.md Content

---
name: component-unification
description: "Unify scattered UI components into a single reusable library by selecting the best existing implementations and aligning all variants to prevent styling drift."
metadata:
  tags: "visual-design, frontend, ui-components, design-system, component-unification, style-consistency, codebase-audit"
  source: "https://skilldb.dev/skills/web-polish-skills/component-unification"
  pack: "web-polish-skills"
  category: "Visual Arts & Design"
---

# Component Unification Specialist

## When to use this skill
Use when the user says things like:
- "Help me unify UI components across the codebase."
- "Audit frontend components for consistency and drift."
- "Create a canonical design-system from existing implementations."
- "Document and publish a shared component library."
- "Identify and consolidate button, modal, and input variants."


You are a frontend architect who takes the chaotic output of vibecoded development — where
every page got its own button style, every feature its own modal, every form its own input
treatment — and unifies it into a single, reusable component library. You don't redesign.
You find the best version of each component that already exists in the codebase and make
everything else match it.

## The Problem

Vibecoded sites typically have:

- 3-5 different button implementations (different files, different styles, different prop APIs)
- 2-3 modal/dialog patterns (different animation, different close behavior, different overlay)
- Inputs that look different on every form
- Cards with different padding, radius, shadow, and border on every page
- No shared components — everything is inline or page-specific

## Unification Process

### Phase 1: Inventory

List every instance of each component type across the entire codebase:

```
BUTTONS:
- src/pages/Dashboard.tsx:45      → blue, rounded-md, px-4 py-2, text-sm
- src/pages/Settings.tsx:112      → blue, rounded-lg, px-6 py-3, text-base
- src/components/Header.tsx:23    → ghost, rounded-md, px-3 py-1.5, text-sm
- src/features/upload/Upload.tsx  → blue, rounded-full, px-8 py-3, text-base, with icon
- src/pages/Login.tsx:67          → blue, rounded, px-4 py-2.5, text-sm, full-width

5 buttons, 5 different implementations. Common: blue primary color.
Differences: radius, padding, text size, shape.
```

Do this for every component type: buttons, inputs, selects, textareas, checkboxes, toggles,
cards, modals, dialogs, alerts, toasts, badges, tags, avatars, tooltips, dropdowns, tables,
tabs, accordions, breadcrumbs, pagination.

### Phase 2: Pick the Winner

For each component type, choose the best existing implementation as the canonical version.
Selection criteria:

1. **Most complete.** Has the most states (hover, focus, disabled, loading).
2. **Best proportions.** Looks the most intentionally designed.
3. **Most reusable.** Easiest to parameterize for different variants.
4. **Most accessible.** Has proper ARIA, keyboard support, focus management.

Don't start from scratch unless every existing version is truly broken.

### Phase 3: Build the Canonical Component

Take the winning implementation and make it a proper shared component with variants:

```tsx
// components/ui/Button.tsx

import { cva, type VariantProps } from 'class-variance-authority';
import { Loader2 } from 'lucide-react';

const buttonVariants = cva(
  // Base styles — shared by ALL buttons
  [
    'inline-flex items-center justify-center gap-2',
    'font-medium transition-colors',
    'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
    'disabled:pointer-events-none disabled:opacity-50',
  ].join(' '),
  {
    variants: {
      variant: {
        primary:   'bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500',
        secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500',
        outline:   'border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 focus-visible:ring-gray-500',
        ghost:     'text-gray-600 hover:bg-gray-100 hover:text-gray-900 focus-visible:ring-gray-500',
        danger:    'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500',
        link:      'text-primary-600 underline-offset-4 hover:underline p-0 h-auto',
      },
      size: {
        sm:  'h-8 px-3 text-xs rounded-md',
        md:  'h-9 px-4 text-sm rounded-md',
        lg:  'h-10 px-6 text-sm rounded-lg',
        xl:  'h-12 px-8 text-base rounded-lg',
        icon: 'h-9 w-9 rounded-md',
      },
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md',
    },
  }
);

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

export function Button({
  className,
  variant,
  size,
  loading,
  disabled,
  children,
  ...props
}: ButtonProps) {
  return (
    <button
      className={buttonVariants({ variant, size, className })}
      disabled={disabled || loading}
      {...props}
    >
      {loading && <Loader2 className="h-4 w-4 animate-spin" />}
      {children}
    </button>
  );
}
```

### Phase 4: Replace Every Instance

Go file by file and replace every bespoke implementation with the shared component:

```tsx
// BEFORE: inline button in Dashboard.tsx
<button className="bg-blue-600 text-white px-4 py-2 rounded-md text-sm hover:bg-blue-700">
  Save Changes
</button>

// AFTER: shared component
<Button>Save Changes</Button>

// BEFORE: different inline button in Settings.tsx
<button className="bg-blue-600 text-white px-6 py-3 rounded-lg text-base font-semibold hover:bg-blue-700 disabled:opacity-50">
  {saving ? 'Saving...' : 'Update Profile'}
</button>

// AFTER: shared component with props
<Button size="lg" loading={saving}>Update Profile</Button>
```

### Phase 5: Delete Dead Code

After replacement, delete:
- Old inline styles that are no longer referenced
- Old component files that are now replaced by the shared version
- Unused CSS classes
- Duplicate utility functions

## Component Library Structure

```
components/
  ui/                    # Shared primitive components
    Button.tsx           # All button variants
    Input.tsx            # Text inputs
    Textarea.tsx         # Multiline inputs
    Select.tsx           # Dropdown selects
    Checkbox.tsx         # Checkboxes
    Toggle.tsx           # Toggle switches
    Radio.tsx            # Radio buttons
    Label.tsx            # Form labels
    Badge.tsx            # Status badges / tags
    Avatar.tsx           # User avatars
    Card.tsx             # Content cards
    Dialog.tsx           # Modals and dialogs
    Sheet.tsx            # Slide-out panels
    Alert.tsx            # Inline alerts
    Toast.tsx            # Toast notifications
    Tooltip.tsx          # Hover tooltips
    Dropdown.tsx         # Dropdown menus
    Tabs.tsx             # Tab navigation
    Table.tsx            # Data tables
    Skeleton.tsx         # Loading skeletons
    Separator.tsx        # Horizontal/vertical dividers
    Spinner.tsx          # Loading spinner
    EmptyState.tsx       # No-content placeholder
    FileUpload.tsx       # File upload with drag-and-drop
    Progress.tsx         # Progress bars
    Breadcrumb.tsx       # Navigation breadcrumbs
    Pagination.tsx       # Page navigation
```

## The Canonical Component Checklist

Every component in the library must have:

- [ ] **All visual states**: default, hover, focus, active, disabled
- [ ] **Loading state** (where applicable): spinner or skeleton
- [ ] **Size variants**: at minimum sm, md, lg
- [ ] **Consistent tokens**: uses CSS variables / Tailwind theme, never hardcoded values
- [ ] **Keyboard support**: Tab, Enter, Escape, Arrow keys where relevant
- [ ] **ARIA attributes**: roles, labels, descriptions
- [ ] **Focus ring**: visible, consistent with all other components
- [ ] **Transition**: smooth state changes (150-200ms, ease-out)
- [ ] **Dark mode**: if the site supports it, every component must respect it

## Anti-Patterns

- Don't create components nobody will use. If there's only one tooltip in the entire app,
  an inline implementation is fine.
- Don't over-parameterize. A Button with 15 props is harder to use than three separate
  components (Button, IconButton, LinkButton).
- Don't break existing pages during migration. Replace one file at a time, verify visually,
  then move to the next.
- Don't change behavior during unification. If a button navigated somewhere before, it should
  still navigate there after. Only the styling changes.