Skip to main content
skill CX Codex

design-component — Claude Code skill: what it does, how to install

Produces UI component specifications covering anatomy, 8 interactive states, tokens, and accessibility. Useful when designing components in a design system before coding.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add plugin87/ux-ui-agent-skills --skill design-component -a claude-code
Open on GitHub

What design-component does

The skill drafts a detailed UI component specification according to internal design guidelines. It outlines anatomy, size variants, ARIA patterns, and eight states: default, hover, focus, active, disabled, loading, error, and selected. Every dimensional or color attribute maps directly to project design tokens such as sizing.json and states.json.

To ensure quality, the skill requires a test harness file rendering every variant across all states. Verification scripts like verify_states.mjs, axe_audit.mjs, and responsive checks must pass before a component is considered ready. Final visual checks rely on headless Playwright screenshots in system Chrome with transitions disabled.

Without running npx ux-ui-agent-skills init beforehand, the skill immediately halts with a KIT_MISSING error because it depends on token files. Generating framework code is out of scope and belongs to the companion skill design-code.

What it helps with

  • document component anatomy, variants, and size scales
  • define all 8 interactive states including busy and invalid
  • map control styling directly to design token JSON files
  • validate contrast and accessibility rules with automated gate scripts

Example prompts for the agent

  • Design a component specification for Button covering all 8 states and tokens
  • Create an accessible Modal overlay spec with focus trap requirements
  • Generate a spec stub for Combobox using scaffold_component.py

How to install design-component

1Skills CLI (Vercel)

npx skills add plugin87/ux-ui-agent-skills --skill design-component -a claude-code

2Manually: put the files in place

~/.claude/skills/design-component/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/design-component/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about design-component

How to install design-component in Claude Code?

Run npx skills add plugin87/ux-ui-agent-skills --skill design-component -a claude-code in your terminal. This configures the skill for Claude Code.

What does design-component do?

It drafts complete UI component specifications covering anatomy, token mappings, ARIA rules, and interactive states. It focuses on specifications, leaving framework code creation to design-code.

Why does the skill report KIT_MISSING?

The skill checks for the tokens/ directory on startup. If you only added the skill files via npx skills add, you must run npx ux-ui-agent-skills init to install the required tokens and project rules.

Source text

SKILL.md · MIT
---
name: design-component
description: Design a UI component spec to the house quality bar (anatomy, variants, sizes, the 8 states, token mapping, and accessibility). Use when the user wants to design or document a component (button, input, tabs, toast, combobox, date picker, modal, etc.) at the spec level before or alongside code. For generating framework code, use design-code.
---

# Skill: Design Component

> **Step 0: is the kit here?** This skill reads files from the kit. Check once:
> `ls ${CLAUDE_SKILL_DIR}/../../../tokens >/dev/null 2>&1 && echo KIT_OK || echo KIT_MISSING`
> On `KIT_MISSING` only the skill folders were installed, which is what
> `npx skills add` does. Say so plainly, point the user at
> `npx ux-ui-agent-skills init` or the plugin install, and stop. Do not guess the
> contents of a file you could not open.

Produce a complete component specification matching the project format.

## Steps
1. Read `${CLAUDE_SKILL_DIR}/../../../.claude/rules/components.md` → "Component Quality Bar" (the 8-state table) and "Atomic Design"; the always-on 8-state table is in `${CLAUDE_SKILL_DIR}/../../../CLAUDE.md` → Non-Negotiables.
2. Check if it already exists: `${CLAUDE_SKILL_DIR}/../../../components/atoms.md`, `molecules.md`, `organisms.md`, `templates.md`, `navigation.md`, `feedback.md`, `forms-advanced.md`, `overlays.md`. Match the existing spec format.
3. Pull the ARIA pattern from `${CLAUDE_SKILL_DIR}/../../../accessibility/aria-patterns.md` and contrast/target rules from `${CLAUDE_SKILL_DIR}/../../../accessibility/wcag-checklist.md`.
4. Map every value to tokens (`${CLAUDE_SKILL_DIR}/../../../tokens/*.json`): sizes via `sizing.json`, states via `states.json`.
5. Apply visual judgment from `${CLAUDE_SKILL_DIR}/../../../taste/design-taste.md` (states, focus, no slop).
6. Optional fast start: `python3 ${CLAUDE_SKILL_DIR}/../../../scripts/scaffold_component.py "<Name>"` to emit a stub, then fill it in.

## Output
Spec with: anatomy diagram, variants table, sizes table, all 8 applicable states, token mapping, accessibility (role/keyboard/SR), and a note to render via `${CLAUDE_SKILL_DIR}/../../../frameworks/adapter-protocol.md`.

## Accuracy: verify every state, don't assume (mandatory when code is produced)
A component is only "correct" when **every variant × state** renders right, not just the resting default. Build a **states harness**: render the component in each applicable state (default, hover, focus, active, disabled, loading `aria-busy`, error `aria-invalid`, selected `aria-pressed`/`aria-selected`) × each variant in one HTML file (see `${CLAUDE_SKILL_DIR}/../../../examples/component-states/button.html`). Then RUN the gates and report their real output (CLAUDE.md → Verification Protocol):
- `node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs verify_states.mjs <harness> [--dark]`: contrast of every element in default/hover/focus
- `node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs axe_audit.mjs <harness> [--dark]`: ARIA/role/name/label correctness
- `node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs measure_render.mjs <harness> [--dark]`: every text element AA
- overlays/modals also: `node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs verify_focustrap.mjs <harness> --open=<trigger>`
Every state must pass in light AND dark before the component is "done". Never claim a state is correct without a gate proving it.

## Gates prove contrast/a11y. They do NOT prove pixels. RENDER AND LOOK.
The contrast/axe gates pass while the UI is still visibly broken: a checkbox that doesn't toggle, a dash sitting at the bottom of its box, a checkmark and an indeterminate dash with mismatched stroke weight, a control that's too heavy. **You must screenshot the harness and inspect it** before claiming done, for every state, and after interaction. Playwright + system Chrome:
```js
const b = await chromium.launch({channel:'chrome'});
const p = await b.newPage({deviceScaleFactor:4});
await p.goto('file://'+abs); await p.addStyleTag({content:'*{transition:none!important}'});
await p.mouse.move(2000,2000);                 // park pointer OFF the component
await p.locator('.stack').first().screenshot({path:'/tmp/x.png'});
```
Read the PNG. Then look for, specifically:
- **Functional**: click each interactive element and assert the state actually changed (`await loc.click(); expect(await loc.isChecked())`). A custom control whose overlay box covers the real `<input>` will not toggle unless the box has `pointer-events:none` (or an enclosing `<label>` forwards the click).
- **Geometry**: glyphs centered, not stacked/offset. If a `display:grid` box holds an `opacity:0` sibling plus a `::after`, the pseudo lands in row 2 → use `display:none` on the hidden sibling, or one container child.
- **Stroke consistency**: a checkmark and its indeterminate dash must use the **same** rendering method (one `<svg>`, two `<path>` toggled by state, same `stroke-width`), never an svg check vs a CSS `::after` rect (they read as different weights).
- **Transition artifact**: screenshot WITHOUT disabling transitions and a just-clicked control looks half-faded mid-animation. That is not a bug. Always disable transitions and park the pointer before judging a state.

**Consistency across files is non-negotiable.** The same component (e.g. checkbox) must use byte-identical CSS + markup in every harness/page. A checkbox that looks thin in `form-controls` and heavy (native `accent-color`) in `data-table` is a bug. Factor one pattern, reuse it verbatim.

### Verified custom checkbox/radio pattern (thin, token-driven, gated + eyeballed)
Real `<input>` underneath (keeps native a11y + keyboard); a drawn `.box` overlay with `pointer-events:none`; check + dash as two `<path>` in one `<svg>` toggled by `:checked` / `:indeterminate`; 1.5px `border-strong`, `.25rem` radius, `.62rem` glyph, `stroke-width:2` round caps. Reference: `${CLAUDE_SKILL_DIR}/../../../examples/component-states/form-controls.html` and `data-table.html` (select-all uses `indeterminate`). Native `accent-color` renders too heavy. Do not use it when the house look is "thin".

## Responsive: every component, no sideways scroll (gated)
Build mobile-first; a fixed-px width that can't shrink is a bug. Run `node ${CLAUDE_SKILL_DIR}/../../../scripts/gate.mjs verify_responsive.mjs <file|dir>`. It loads each harness at 280/320/414px and fails on any horizontal overflow. The four recurring causes and their fixes:
- **fixed `inline-size:Npx`** → `inline-size:100%;max-inline-size:Npx` (cap, don't pin).
- **`<ul>`/`<ol>` default 40px inline-start padding** (a `*{margin:0}` reset does NOT clear padding) → `padding:0;margin:0` on every list. This also silently mis-aligns a list's edge vs a sibling block (looks like unequal widths). Same fix.
- **non-wrapping flex rows** (breadcrumb, stepper, tabs) → `flex-wrap:wrap`, or for tabs `overflow-x:auto` + `.tab{flex:none}`.
- **`grid minmax(Npx,1fr)` min larger than viewport** → `minmax(min(Npx,100%),1fr)`.

## Motion: tokenized, real easing, animate the thing that moves
Timing/easing are tokens (`--duration-fast|normal|slow`, `--ease-out|in|in-out|emphasized` in the theme; `--transition-micro` = `fast ease-out`). Never hardcode ms/curves. A component that toggles open/closed must animate its **height**, not just rotate a chevron. Collapse via `hidden`/`display:none` alone reads as "rigid, no transition". Smooth-height pattern (no JS measuring): wrap content in an inner that clips overflow, animate the grid track:
```css
.panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows var(--duration-base) var(--ease-spring)}
.panel.open{grid-template-rows:1fr}
.panel > .inner{overflow:hidden;min-block-size:0}
```
Keep a11y: expand = remove `hidden` then add `.open` next frame; collapse = remove `.open`, set `hidden` on `transitionend`. Reference: accordion in `${CLAUDE_SKILL_DIR}/../../../examples/component-states/overlays.html`. Always honor `@media(prefers-reduced-motion:reduce){…transition:none}`.

## Layout: fill the space, don't ship AI-empty filler
- **`auto-fit`, never `auto-fill`** for card grids. `auto-fill` keeps empty phantom tracks so 3 cards cluster left with a void on the right; `auto-fit` collapses empties so cards stretch to fill the row. Always `repeat(auto-fit,minmax(min(Npx,100%),1fr))`.
- **Equal-height panels in a row**: `align-items:stretch` on the grid, AND make the shorter panel's body fill: `.panel{display:flex;flex-direction:column}` + the inner region `flex:1`. A child sized with `block-size:%` (e.g. chart bars) needs a **definite-height** ancestor (a `flex:1` box or explicit height), or the % resolves to 0 and the element collapses. Wrap the bar in a `flex:1` `.barbox` and give the bar `block-size:%` of that.
- **A main region that's 80% whitespace reads as machine-generated.** Fill a dashboard with real, plausible content (stats row + activity list + a chart), not one lonely widget. Intentional density is the difference between "designed" and "AI slop".
- **Trailing gap in a toolbar/header**: a flex item with `flex:1` capped by `max-inline-size` stops growing and leaves empty space *after* the last item. Push the right-hand cluster with `margin-inline-start:auto` on its first element.
- **Mobile nav must not overlap.** Putting the sidebar and main in the same grid area makes an opened sidebar paint over content. On mobile switch the shell to `display:block` so opening the sidebar pushes main *down*. Reference: `${CLAUDE_SKILL_DIR}/../../../examples/component-states/app-shell.html`.

## Icons: real lucide, referenced by name (never hand-draw paths)
Hand-approximated SVG path data renders as broken glyphs (a help "?" became a dot; settings became a hamburger). Use **verbatim lucide** paths, referenced by name via an injected `<symbol>` sprite: `${CLAUDE_SKILL_DIR}/../../../examples/component-states/icons.js` defines each icon once and `<svg class="ico" aria-hidden="true"><use href="#i-NAME"/></svg>` uses it. No per-use path duplication, no network, offline + gate-safe. Add a new icon to `icons.js` once; never paste raw paths into markup. (Inline lucide is acceptable only if the path is copied verbatim from lucide.) `.ico{stroke:currentColor;fill:none;stroke-width:2}`: color via `currentColor`.

## Graphical / icon-only controls (3:1, theme-stable)
A no-text control (carousel dot, kebab, icon button) is held to **3:1** (WCAG 1.4.11), not 4.5. `verify_states` applies this automatically when an element has no direct text node. Two traps it catches:
- An empty `<button>` keeps the UA `color:buttontext` (≈black) regardless of theme → set its `color` to the actual indicator color and drive the visual via `currentColor` (e.g. dot is a `::before{background:currentColor}`), so the gate measures the real thing.
- **Theme-flipping tokens** (`--color-chart-N`, `--color-surface-brand`) invert between light/dark; white text or a teal indicator on them passes in one mode and fails the other. Use **dark-aware** values (override in `[data-theme="dark"]`) or **stable** tokens (`--color-action-primary`, `--color-text-link` which adapts) for avatars, active dots, and selected states.

© plugin87 · MIT · plugin87/ux-ui-agent-skills

Similar skills

Everything for Design →

From the same set16

design-tokens Create, extend, and validate DTCG design tokens with a three-tier structure. Useful for palettes, typography, theming, and other interface values. CX Codex +1 538installs redesign This skill audits an existing interface and revises its design while preserving working features. Use it to refresh a website or app without changing its core behavior. CX Codex +1 535installs apply-aesthetic Picks a visual direction — an archetype or a named design system — and resolves it into your project's tokens. Useful when you want a specific look, not whatever the agent improvises. CX Codex +1 529installs design-code This skill generates component and screen code for a requested framework. Use it when you need token-based UI with accessibility checks. CX Codex +1 525installs ux-writing Write or review interface text, from button labels to errors and empty states. The skill applies a project’s voice and checks copy against a pre-ship checklist. CX Codex +1 517installs design-qa This skill sets up checks for tokens, contrast, accessibility, and visual regressions. Use it to QA a component or screen before release. CX Codex +1 516installs brandkit Build a token-based design foundation from a product brief, with separate light and dark themes and contrast checks. Use it to establish shared visual rules before designing screens. CX Codex +1 510installs migrate-design-system Maps your design tokens to another system and back: Material 3, Apple HIG, Fluent 2, shadcn/ui, Radix. Useful when you borrow a look or move onto a new stack. CX Codex +1 487installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor