---
name: component-library
description: Build an accessible component library from a color system. Assesses existing variables/palette, proposes components for the project type, creates proper semantic token architecture with emphasis tokens, and enforces WCAG 2.2 AAA/AA contrast on every component, state, and variant.
---

# Component Library Builder

Build a complete, accessible component library from a color system in a Figma file. This skill handles token architecture, component creation, and WCAG 2.2 compliance in a structured sequence.

## Running this with Claude and the Figma MCP

Written for Figma's built-in agent, adapted for Claude (Claude Code, Claude Desktop or claude.ai) connected to the official Figma MCP server.

- **Get the file.** If you don't have a Figma link, ask for one. The file key is the segment after `/design/`; a `node-id=1-2` parameter means node `1:2`.
- **Start a session.** Call `create_session` with the file key before any other Figma tool, and pass the returned session ID on every later call.
- **Read before writing.** `get_metadata` shows the structure and the user's current selection, `get_variable_defs` lists the variables on a node, `search_design_system` finds published components, variables and styles, and `get_screenshot` shows what's on the canvas.
- **Write with `use_figma`.** It runs Plugin API JavaScript in the file. Load the `figma-use` skill first (plus `figma-generate-library` when you create variables, styles or components) and list them in `skillNames`. Scripts use top-level `await` and `return` everything they create: nothing persists between calls, so pass node and variable IDs forward as string literals. Colors are 0–1, not 0–255. Switch pages with `await figma.setCurrentPageAsync(page)`, at most once per call; for multi-page work, run one call per page in parallel. `figma.notify()` isn't available.
- **Ask, then wait.** Wherever a step says to ask the user, ask in chat (in Claude Code, use the AskUserQuestion tool) and wait for the answer before building.
- **Clear helper fills.** `figma.createAutoLayout()` frames start with a white fill. Set `fills = []` on rows and wrappers that should be transparent, or they show up as white bars (most visibly in dark-mode previews).
- **Check the result.** After a build step, take one screenshot (`await node.screenshot()` inside `use_figma`, or `get_screenshot`) and fix only what's wrong.

## Phase 1: Gather Requirements

### 1A — Determine Components

Ask the user to describe their project (e.g., "a modern portfolio website," "a SaaS dashboard," "an e-commerce app") OR list the specific components they need.

Based on the answer, propose a component set. Common mappings:

- **Portfolio / Marketing site**: Buttons, Navigation, Project/Content Cards, Badges & Tags, Section Headers, Testimonials, Inputs, Alerts & Toasts
- **SaaS / Dashboard**: Buttons, Navigation, Data Cards, Stat Tiles, Tables, Badges & Tags, Inputs, Alerts & Toasts, Modals, Tabs
- **E-commerce**: Buttons, Navigation, Product Cards, Badges & Tags, Inputs, Search, Alerts & Toasts, Rating Stars, Price Display

Share the proposed list and get explicit confirmation before proceeding. The user may add, remove, or adjust.

### 1B — Assess the Color System

Inspect the file for existing variables and libraries:

1. **Check for variable collections** — look for Primitives (raw color scales) and Semantic (aliased tokens with Light/Dark modes).
2. **Check for enabled libraries** — the file may subscribe to an external color library.
3. **Check for color styles** — older files may use paint styles instead of variables.

Based on findings:

- **Full variable system exists** (primitives + semantic with modes): Proceed. Map the existing token architecture.
- **Partial system** (e.g., primitives only, no semantic layer): Offer to create the semantic alias layer on top.
- **Color styles only, no variables**: Offer to migrate styles to a variable-based architecture.
- **Nothing exists**: Ask the user for a color direction (palette, mood, brand colors) and offer to create the full token stack: primitive scales (50–950 per hue) → semantic aliases (Light/Dark modes).

### 1C — Determine Emphasis Preference

Explain the two treatments with clear examples:

- **Standard**: Light fills in light mode, dark fills in dark mode. Subtle, understated components.
- **Emphasis (inverted)**: Dark fills in light mode, light fills in dark mode. Bold, prominent components that stand out.

Ask the user which components should use which treatment. Common patterns:
- Badges, Tags, Alerts, and Toasts often benefit from emphasis treatment.
- Inputs, Cards, and Navigation typically use standard treatment.
- Buttons can go either way depending on the design direction.

The user may want emphasis on all, some, or none. Record this preference for Phase 2.

## Phase 2: Token Architecture

### 2A — Semantic Token Structure

Every component library needs these semantic token groups. Create them as variable aliases pointing to primitives, with Light and Dark mode values. Set `scopes` on every variable you create (for example `FRAME_FILL` and `SHAPE_FILL` for backgrounds, `TEXT_FILL` for text, `STROKE_COLOR` for borders). The default exposes each variable in every property picker.

**Core tokens** (always needed):
- `bg/primary`, `bg/secondary`, `bg/tertiary`, `bg/inverse`
- `surface/default`, `surface/raised`, `surface/overlay`
- `text/primary`, `text/secondary`, `text/tertiary`, `text/inverse`, `text/on-accent`
- `border/default`, `border/strong`, `border/subtle`

**Per-accent-color tokens** (e.g., for each of cream, sage, lavender, plum — or whatever the palette contains):
- `{color}/subtle` — Light tint for backgrounds (*/100 light, */800 dark)
- `{color}/default` — Mid-tone for decorative use (*/500 light, */400 dark)
- `{color}/strong` — Bold shade for text/borders on subtle fills (*/700 light, */200 dark)

**Status tokens** (success, warning, error, info — each aliased to an appropriate hue):
- `status/{name}/subtle` — Background fill
- `status/{name}/default` — Mid-tone accent
- `status/{name}/strong` — Bold accent (used by destructive buttons, etc.)
- `status/{name}/text` — Readable text
- `status/{name}/border` — Visible border

### 2B — Emphasis Tokens

**CRITICAL RULE: Never modify existing token values to create an inverted appearance. Always create NEW dedicated tokens.**

Changing a token's value (e.g., making `subtle` dark) breaks every component that references it — buttons, inputs, nav — because those components expect `subtle` to mean "light." Instead, create parallel tokens:

For each accent color that needs emphasis treatment:
- `{color}/emphasis` — Dark fill in light mode, light fill in dark mode (*/700 light, */200 dark)
- `{color}/on-emphasis` — Contrasting foreground (*/100 light, */800 dark)

For each status that needs emphasis treatment:
- `status/{name}/emphasis` — Same pattern as accent emphasis
- `status/{name}/on-emphasis` — Same pattern as accent on-emphasis

Components that use emphasis treatment bind to these tokens. Components that use standard treatment bind to the original subtle/strong tokens. Both sets coexist without conflict.

### 2C — Contrast Validation at the Token Level

Before building any components, validate every token pairing that will be used:

**Text pairings (target AAA 7:1, accept AA 4.5:1):**
- `text/primary` on `bg/primary`, `surface/default`, `surface/raised` — both modes
- `text/secondary` on the same backgrounds — both modes
- `{color}/strong` on `{color}/subtle` — both modes
- `{color}/on-emphasis` on `{color}/emphasis` — both modes
- `status/{name}/text` on `status/{name}/subtle` — both modes
- `status/{name}/on-emphasis` on `status/{name}/emphasis` — both modes
- `text/inverse` on accent fills (buttons) — both modes

**Non-text / graphic pairings (target AAA 4.5:1, accept AA 3:1):**
- `border/default` and `border/strong` on backgrounds — both modes
- Component fills vs. page background (boundary contrast) — both modes
- Icon circles, dots, close buttons against their container fills — both modes
- Focus rings on input surfaces — both modes

**Contrast calculation** — use sRGB relative luminance:

luminance(rgb) = 0.2126 * linearize(r) + 0.7152 * linearize(g) + 0.0722 * linearize(b)
contrastRatio = (lighter + 0.05) / (darker + 0.05)

If a pairing fails AA, adjust the primitive step (e.g., move from */600 to */700) BEFORE building components. Fixing tokens after components exist causes cascading rework.

## Phase 3: Component Construction

### 3A — Page Organization

Create a dedicated page for each component type. This keeps the library navigable and allows focused iteration.

### 3B — Component Variant Strategy

For each component, define the variant axes:
- **Visual style variants** (Primary, Secondary, Ghost, Destructive, etc.)
- **State variants** (Default, Hover, Focus, Disabled, Error)
- **Size variants** where applicable (Default, Small, Large)
- **Content property variants** (with/without icon, with/without description)

Build variants as components within a component set (use `figma.combineAsVariants(components, parent)`). Expose text content as component TEXT properties, optional elements as BOOLEAN properties. A TEXT property has one default for the whole set, so it replaces any variant-specific sample text (e.g. a "NOW" label on a Current variant); set that text on instances instead.

### 3C — Token Binding Rules

**Standard components** (inputs, cards, nav, buttons):
- Fills → `subtle`, `surface/*`, or `bg/*` tokens
- Text → `text/*`, `strong` tokens
- Borders → `border/*`, `strong` tokens
- Focus rings → `{accent}/default` (mid-tone, visible on surfaces in both modes)
- Error states → `status/error/strong` for borders, `status/error/strong` for labels (these are non-inverted, dark in light mode, light in dark mode — visible on surfaces)

**Emphasis components** (badges, tags, alerts, toasts — per user preference):
- Fills → `emphasis` tokens
- Text → `on-emphasis` tokens
- Borders/strokes → `on-emphasis` tokens
- Dots, decorative elements → `on-emphasis` tokens
- **Icon circles / enclosed decorative shapes** → original `subtle` token (light in light mode = contrasts with dark emphasis fill; dark in dark mode = contrasts with light emphasis fill)
- **Marks inside icon circles** (checkmarks, icons) → `emphasis` token (matches fill color, contrasts with the light icon circle)

### 3D — Nested Contrast Awareness

Components with layered elements (like an alert with an icon circle containing a checkmark) have multiple contrast relationships:

[Alert fill: emphasis (dark)] → [Icon circle: subtle (light)] → [Checkmark: emphasis (dark)]
                              → [Title text: on-emphasis (light)]
                              → [Close button: on-emphasis (light)]

Each adjacent pair must independently meet contrast requirements. Validate:
1. Icon circle against alert fill (3:1 minimum)
2. Checkmark against icon circle (3:1 minimum)
3. Text against alert fill (4.5:1 minimum)
4. Close button against alert fill (3:1 minimum)
5. Alert fill against page background (3:1 boundary minimum)

### 3E — Icon and Graphic Accessibility

- **Never use text characters as icons** (e.g., "✓", "✕", "i", "!"). These render as text but serve as graphical elements. Use proper vector shapes or built-in icons.
- All graphical UI elements (icons, dots, focus rings, borders) must meet 3:1 contrast against their adjacent background.
- Decorative elements that don't convey information are exempt.

### 3F — Instance Reuse

When one component references another (e.g., a card contains badge instances), always use actual component instances — never recreate the visual with ad-hoc frames. This ensures token changes propagate correctly and maintains a single source of truth.

## Phase 4: Verification

### 4A — Per-Component Audit

After building each component, run a contrast check on EVERY variant × state combination in BOTH Light and Dark modes. Do not batch-check at the end — catch issues immediately.

For each variant, verify:
- All text fills against their background (4.5:1+ for AA, 7:1+ for AAA)
- All non-text elements against adjacent backgrounds (3:1+ for AA, 4.5:1+ for AAA)
- Component boundary against page background (3:1+)
- Focus/selected states maintain contrast (don't just check default)
- Disabled states are exempt from contrast requirements but should still be perceivable

### 4B — Cross-Component Consistency

After all components are built:
- Verify shared tokens produce consistent contrast across all components that use them.
- Confirm instances (like badge instances in cards) inherited the correct tokens.
- Check that emphasis and standard components don't accidentally share token references that would create coupling.

### 4C — Failure Response

If a contrast check fails:
1. **Prefer adjusting the primitive step** (e.g., */600 → */700) over creating overrides.
2. **If a step change would break another pairing**, create a new dedicated semantic token rather than changing an existing one.
3. **Never add hard-coded colors** — all fills must be variable-bound.
4. **Re-check all dependent pairings** after any token adjustment.

## Key Principles

1. **New tokens over modified tokens.** If a component needs different color behavior, create a new semantic variable. Never repurpose an existing token — it will break other components.
2. **Validate before you build.** Check token-pair contrast at the architecture level (Phase 2C) before spending time on component construction.
3. **Test both modes, every variant.** A pairing that passes in Light mode may fail in Dark mode. Always check both.
4. **Aim high, accept pragmatic.** Target AAA (7:1 text, 4.5:1 non-text). Accept AA (4.5:1 text, 3:1 non-text) when AAA would require colors that conflict with the design direction. Never ship below AA.
5. **Icons are graphics, not text.** Visual symbols follow the 3:1 non-text contrast rule (SC 1.4.11), not the 4.5:1 text rule — but they must be actual shapes, not text characters.
6. **One source of truth.** Every color reference is a variable binding. Every repeated element is a component instance. No ad-hoc duplicates.
