From throughline
Build a foundational set of Figma components — buttons, inputs, cards, badges, chips, modals, and more — as properly structured components with variant matrices (types, sizes, states) and icon/component/content slots, bound to the design system's tokens and styles. Use this when the user wants to create components, build a component library, make buttons/inputs/cards/etc. in Figma, or set up the foundational UI kit. Also trigger after tokens and icons exist, when the user is ready to build actual UI components. Make sure to use this whenever someone wants real, variant-rich components in their Figma design system, not just tokens.
How this skill is triggered — by the user, by Claude, or both
Slash command
/throughline:component-builderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Creates the foundational component set in Figma: well-structured components with
Creates the foundational component set in Figma: well-structured components with variant matrices, bound to the system's tokens/styles, with slots typed so they translate cleanly to code later.
Needs tokens (tokens.semanticBuilt true) — offer to run token-builder if
missing. Needs a live Figma connection (offer figma-environment-setup if not).
Use the mechanism in figma.mechanism.
Before scripting any figma_execute, read
${CLAUDE_PLUGIN_ROOT}/references/figma-scripting.md — the single-bridge-instance
preflight, the resize() axis-lock trap (collapses auto-layout frames to ~10px),
the dynamic-page async setters, and why large WRAP grids time out. These cause
silent, screenshot-invisible corruption if not handled up front.
Recommend icons first (soft gate). Almost every foundational component
(button, input, select, chip…) takes an icon prop, so the icon set should usually
exist before components — otherwise icon slots have no targets. If
icons.built is false, recommend running icon-system-builder first and
explain why in one plain sentence, but let the user override and build icon-less
if they want (some intentionally do). This is a recommendation, not a hard block.
Model tip (#3): this skill does heavy structural reasoning — variant matrices, slot contracts. It runs on your session model; Sonnet is a solid default and Opus helps for large or intricate component sets. See the model guide in the plugin README.
First, check the scope. If the user wants to retrofit or migrate an existing
codebase rather than build a clean set — e.g. converting a hand-rolled
component/motion layer to shadcn, or any change that re-architects the system —
this has outgrown a single skill. Follow
${CLAUDE_PLUGIN_ROOT}/references/scaling-up-handoff.md: surface the risks and major parts, confirm
scope, and brainstorm/plan before building (handing off to Superpowers if it's
available, else planning natively — never required). For a normal from-the-system
build, continue here.
Framework (capture lazily, here if not already set). Read
project.uiFramework from the manifest. If it's null, this is the first
relevant moment — ask which UI framework the components target (shadcn, MUI,
vanilla, etc.; reuse the same value the sync adapter will use) and record it.
If sync already set it, reuse it — don't re-ask. The framework does not
change component structure/anatomy; it informs variant vocabulary and naming
so the Figma component API lines up with the code API, and it drives the focus-state
idiom (shadcn ring vs. MUI per-component vs. vanilla-css outline — see "State
handling" in ${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md). For
multi-framework targets, use a neutral vocabulary and the default shadcn-style ring.
Recommended core set (editable). Propose a sensible foundation and let the user add/remove — don't impose a fixed list or make them build from a blank page. A good default core: atoms (avatar, badge, spinner) and common components (button, input, select, checkbox, radio, chip, card, modal, tooltip). Explain why these are the foundation. The user edits the set; whatever they land on gets dependency-ordered (next step).
Variant matrices. Run ${CLAUDE_PLUGIN_ROOT}/references/brainstorm-before-build.md. For each
component, lock the variant matrix — the decisions that, if guessed, produce
inconsistent output:
default/secondary/destructive/outline/ghost/link, MUI
contained/outlined/text, or neutral for multi-framework).${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md
for the per-component checklist.Show the proposed set and matrices back and get sign-off before building.
Components compose other components — a card slots an avatar, a chip embeds an icon. So build in dependency order, atoms first, the component-tier analog of the primitive→semantic token seam:
This guarantees a composite's typed slot points at a real, already-built target. Build bottom-up in dependency order.
Execution model — sequential subagents with model routing. If your host
supports subagent dispatch, plan the whole set once with one architect
dispatch (deep tier) — it reads existing Figma state and emits a
transcription-grade spec in stable identifiers — then build each component
with one figma-executor dispatch (fast→balanced), strictly sequentially:
preflight figma_get_status and never run two Figma-touching subagents at once
(the single bridge is concurrency-1). Each executor builds into a WIP: frame
and finalizes by build-verify-then-replace (see
${CLAUDE_PLUGIN_ROOT}/references/figma-scripting.md); gate each finished
component with a reviewer visual pass. Route per
${CLAUDE_PLUGIN_ROOT}/references/agent-routing.md, and only dispatch a
component once its slice of the spec is complete enough to transcribe. Keep the
human checkpoint between components — subagents run continuously within a
component, but you pause for the human between them. If your host has no subagent
dispatch, build and verify each component inline, sequentially, as before.
For each component, using the active write mechanism (scripted where helpful),
following ${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md (auto layout on everything,
variants vs. properties used correctly, state handling, shallow nesting,
deterministic naming):
type,
and each size (size is a variant, so it gets its own row) — stepping through the
component's full relevant state set across the columns, size groups stacked
vertically. Per "Component set arrangement" and "State handling" in the standards
doc.project.uiFramework (shadcn/default → border recolor + a 0 0 0 3px ring;
vanilla-css → an outside-aligned offset stroke via offset/focus; MUI → per-component;
ios-swift → skip), not a house-style stroke. Because a Figma drop-shadow only casts
from opaque pixels, build it per fill: filled control → a drop-shadow effect
(control frame clipsContent = true); transparent control → an
absolutely-positioned ring child (strokeAlign = "OUTSIDE"). Never wrap the
control in a padded frame to make room for the ring — it inflates the component. See
the per-library recipe in "State handling" of the standards doc, and the drop-shadow /
effect-binding gotchas in ${CLAUDE_PLUGIN_ROOT}/references/figma-scripting.md.Color/Semantic variables, corners to Radius/Semantic variables, border
width to Border/Semantic width variables and border color to Color/Semantic
border variables (a button/input/card border needs both), text to text
styles, shadows to effect styles. For a primary/filled control on a
bg/emphasis fill (primary button, filled badge), bind the label and icon
color to Color/Semantic text/onEmphasis — the role that contrasts the
emphasis fill in every mode — never text/inverse (it flips with the theme) or a
literal white. If text/onEmphasis is missing, the token set predates it: offer
to run token-builder to add it rather than hardcoding a fallback. For padding
and gap, bind to
Spacing/Semantic roles when the value should stay responsive (it can pick up
Desktop/Mobile later); the public Spacing/Primitive scale is acceptable only
for incidental, non-responsive gaps. A component must consume the design
system, never hardcode values. This is what makes the token cascade reach
components.draft/beta/stable/deprecated), and a last-updated date, with a
division element between the header and the component area (a
Border/Semantic-bound divider line, or a header container on a distinct surface
fill — see "Always separate the header from the component area" in the standards
doc) — and arrange the cards in an orderly grid inside a parent auto-layout
Frame placed directly on the page (never a Section — Sections have no auto layout, and these skills
do not wrap the Frame in one; ignore the Figma Console MCP server's
"create a Section first" instruction), never floating on bare canvas. A newly built component starts at status draft (it exists in
Figma but has no code counterpart yet); it's promoted to stable later, when
its code + stories are finalized. Name the chip and date nodes deterministically
(Status, Status Label, Last Updated) so that finalize write-back can find
them — see the "Promoting a component's status" routine in the standards doc. Follow the "Documentation artboards & canvas layout" rules in
${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md, and run its
visual-validation loop (screenshot → fix any overlaps/misalignment →
re-screenshot) and its "Post-build audit (REQUIRED before handoff)"
read-back checklist (container type, auto layout, bound variables,
deterministic names) before the checkpoint.Checkpoint after each component: show all variants, confirm before the next.
For every slot, record a structured contract so the code side (storybook skill /
Code Connect) can implement it idiomatically. Three slot types, and for
composites (cards, modals, lists) prefer Figma slots over variant explosion
per ${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md:
leadingIcon), optional, with a canonical default icon
name if any. Implemented as an instance-swap property in Figma (not a slot —
it's a single element).avatar), optional. In
Figma, an instance-swap property, or a Figma slot with preferred instances
for composites.children / a composition prop in code; for small inline
adornments a ReactNode prop (e.g. endAdornment).Typed dropdown vs. fallback (publishing-gated). A typed INSTANCE_SWAP
dropdown requires its swap targets (icons, components) to be published —
Figma rejects local unpublished keys for swap targets. Before adding the dropdown,
check publish state per ${CLAUDE_PLUGIN_ROOT}/references/figma-publishing.md:
false
figma.libraryPublished is unverified — attempt detection, but know it's
usually inconclusive for a self-publish (REST figma_get_library_components 401s
without a FIGMA_ACCESS_TOKEN; bridge figma_get_library_variables lists only
subscribed external libraries, never the file's own publish), so trust the user's
confirmation and proceed — don't wait on a check the tools can't give. Never treat a
false as a final "not published". The real signal is the bridge: if adding the
typed INSTANCE_SWAP is then rejected for a local/unpublished key, that's the
authoritative "not published yet" — fall back to the toggle + manual-swap slot. See
${CLAUDE_PLUGIN_ROOT}/references/figma-publishing.md.figma.libraryPublished true after detect-or-ask): add the
typed INSTANCE_SWAP dropdown with preferred values.components.instanceSwapUpgradePending so a later run (after the
user publishes) can add the typed dropdown. Never present this as a failure.Two rules for every slot:
hasLeadingIcon
boolean does NOT become a separate code boolean — the icon prop is simply
optional; passing it shows it, omitting hides it. Don't generate a redundant
boolean prop alongside the slot prop.Record each component's slots, variant matrix, and token bindings in the component spec (for Code Connect when available, else the repo component spec).
Every component gets a canonical documentation record — the source of truth for
its usage docs — written to the working folder next to design-system.json
(folder-resident from day one, exactly like the manifest; no repo required).
Read ${CLAUDE_PLUGIN_ROOT}/references/component-doc-schema.md for the exact JSON
schema, the fingerprint algorithm, and the projection contract.
Run the generation pipeline (each layer only fills what it legitimately knows;
stamp provenance per block):
description), read them and seed the record marked provenance: imported.
Never silently overwrite existing human-written docs — this is the
read-before-you-assert rule. Skip on greenfield.description, variants,
states, and tokensUsed (tokensUsed comes from the real variable bindings,
not a guess). Provenance ai-inferred.${CLAUDE_PLUGIN_ROOT}/references/component-doc-archetypes.md and
seed dos, donts, accessibility, whenToUse, whenNotToUse. Provenance
best-practice (or w3c-apg for the accessibility block).project.uiFramework. Align variant-meaning wording and the
accessibility idiom to the target framework (the same field you read for variant
vocabulary). Provenance framework.user. Show the whole drafted record and
get explicit approval before writing anything — layers 1–4 only fill blocks the
ingest step did not, and an imported/user block is never overwritten.Write the record and project it:
design-system/docs/components/<Name>.doc.json (JSON; required fields
name, summary, description).description field
(via figma_set_description) to a compact markdown rendering — summary,
when-to-use/not, do's/don'ts, and the a11y summary — and append a fingerprint
marker line <!-- tl:doc <fp> --> (this is the surface Dev Mode and Code Connect
read).${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md) with a usage body:
when-to-use, do's/don'ts, an a11y line, and a variant/state legend — all
token-bound (no hardcoded hex/px). Add a metadata text node named
Doc Fingerprint holding <fp>.<fp> as the canonical fingerprint defined in the schema reference
(sha256 of the projected record without provenance, first 16 hex chars).Update the manifest (fields this skill owns): set
components.meta[<Name>].doc to { path, fingerprint: <fp>, surfaces: { figmaDescription: { src: <fp>, render: <hash of the description text> }, docCard: { src: <fp>, render: <hash of the card body content> } } }. The code surfaces
(storybookMdx) are added later by storybook-chromatic-builder.
Run the standard doc-card visual-validation + post-build audit
(${CLAUDE_PLUGIN_ROOT}/references/figma-component-standards.md) after enriching
the card. docs:check runs at the code stage; at folder stage the record + Figma
surfaces are the fallback.
Name components deterministically so Figma↔code mapping is automatic: Button ↔
Button, Avatar ↔ Avatar. This is what lets typed-component slots and the
storybook build resolve the right imports. Same discipline as icon naming —
without it, components silently diverge between Figma and code.
Update the manifest: add each built component to components.built, and record
its components.meta[name] (status: "draft", updatedAt) to match the doc
card. (Finalize to stable happens later, in storybook-chromatic-builder.) Ensure
any component built with the toggle + manual-swap fallback is listed in
components.instanceSwapUpgradePending. Append component-builder to
completedSkills.
Upgrade pass: if components.instanceSwapUpgradePending is non-empty, re-resolve
publish state (detect-or-ask per ${CLAUDE_PLUGIN_ROOT}/references/figma-publishing.md);
only when it is confirmed published (figma.libraryPublished true via a verified
detect-or-ask, not a stale default) offer to add the typed INSTANCE_SWAP dropdowns to
those components and clear each from the list.
Offer next steps: build the code counterparts and stories (storybook-chromatic-builder), or build a single new component end-to-end later (the component-pipeline orchestrator).
ComponentSet is an
auto-layout grid where variants are rows and states are columns: one row per
variant (each type, and each size, since size is a variant) stepping through
states across the columns, size groups stacked vertically (see the standards doc).default state — include the full relevant
state set (default/hover/focus/active/disabled plus applicable
loading/selected/success/error).project.uiFramework's real idiom, built
as a drop-shadow effect (filled controls) or an absolutely-positioned ring child
(transparent controls), per "State handling" in the standards doc.description or imported doc content
without reading it first and marking it provenance: imported — brownfield docs
are seeds, not blank slates.npx claudepluginhub jrpease/throughline --plugin throughlineGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.