Add workflow tooling: claude2figma skills, Storybook addons, Figma permissions
Install @storybook/addon-designs for embedding Figma frames in stories and claude2figma skills (preflight, component-rules, style-binding, reference-interpreter) for enforcing design token compliance when writing to Figma. Add PostToolUse hook for use_figma QA reminders and pre-allow Figma MCP + Code Connect tools in settings.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,7 +36,32 @@
|
|||||||
"WebFetch(domain:figma.com)",
|
"WebFetch(domain:figma.com)",
|
||||||
"WebFetch(domain:help.figma.com)",
|
"WebFetch(domain:help.figma.com)",
|
||||||
"WebFetch(domain:developers.figma.com)",
|
"WebFetch(domain:developers.figma.com)",
|
||||||
"WebFetch(domain:developer.mozilla.org)"
|
"WebFetch(domain:developer.mozilla.org)",
|
||||||
|
"mcp__figma__whoami",
|
||||||
|
"mcp__figma__get_metadata",
|
||||||
|
"mcp__figma__get_libraries",
|
||||||
|
"mcp__figma__use_figma",
|
||||||
|
"mcp__figma__get_screenshot",
|
||||||
|
"mcp__figma__search_design_system",
|
||||||
|
"mcp__figma__get_variable_defs",
|
||||||
|
"mcp__figma__get_design_context",
|
||||||
|
"mcp__figma__get_code_connect_map",
|
||||||
|
"mcp__figma__add_code_connect_map",
|
||||||
|
"mcp__figma__get_code_connect_suggestions",
|
||||||
|
"mcp__figma__send_code_connect_mappings"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "mcp__figma__use_figma",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "echo 'QA: Verify style/variable bindings on all returned node IDs.'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
.claude/skills/component-rules/SKILL.md
Normal file
71
.claude/skills/component-rules/SKILL.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
name: component-rules
|
||||||
|
description: "Triggers when building any UI element in Figma — 'create a card', 'build a nav', 'add a section', 'make a component'. Enforces library-first component lookup, correct Auto Layout structure, and semantic node naming. For visual property binding (colors, text styles, spacing values), defer to figma-style-binding."
|
||||||
|
disable-model-invocation: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Component Rules
|
||||||
|
|
||||||
|
Governs how Claude constructs UI in Figma. Supplements `figma-generate-design`. For visual property binding, defer to `figma-style-binding`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rule 1 — Library-first hierarchy
|
||||||
|
|
||||||
|
Before building anything, resolve the component source in this order:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. search_design_system → importComponentByKeyAsync → createInstance()
|
||||||
|
2. Local file scan → createInstance()
|
||||||
|
3. Build from scratch — ONLY if nothing matches
|
||||||
|
```
|
||||||
|
|
||||||
|
Never rebuild primitives the DS provides: Button, Input, Checkbox, Toggle, Badge, Tag, Avatar, Icon, Tab, Breadcrumb, Toast, Alert, Spinner.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Library import
|
||||||
|
const comp = await figma.importComponentByKeyAsync("key_from_search");
|
||||||
|
const instance = comp.createInstance();
|
||||||
|
parent.appendChild(instance);
|
||||||
|
|
||||||
|
// For component sets (variants)
|
||||||
|
const set = await figma.importComponentSetByKeyAsync("key");
|
||||||
|
const instance = set.defaultVariant.createInstance();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rule 2 — Auto Layout
|
||||||
|
|
||||||
|
Every container must use Auto Layout. Property order matters:
|
||||||
|
|
||||||
|
1. Set `layoutMode` FIRST (`"VERTICAL"` or `"HORIZONTAL"`)
|
||||||
|
2. Set `layoutSizingHorizontal` / `layoutSizingVertical` AFTER `appendChild`
|
||||||
|
3. Set `layoutMode` BEFORE any `setBoundVariable` call
|
||||||
|
|
||||||
|
| Goal | primaryAxisSizing | counterAxisSizing | layoutSizingH | layoutSizingV |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Hug both | AUTO | AUTO | HUG | HUG |
|
||||||
|
| Fixed card | FIXED | FIXED | FIXED | FIXED |
|
||||||
|
| Full-width section | AUTO | FIXED | FILL | HUG |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rule 3 — Node naming
|
||||||
|
|
||||||
|
Name every node semantically with slash hierarchy. Never leave defaults.
|
||||||
|
|
||||||
|
```
|
||||||
|
Card / Container Card / Title Card / Body Card / Action Row
|
||||||
|
Hero / Background Nav / Link Group Button / Primary
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rule 4 — Incremental building
|
||||||
|
|
||||||
|
One section per `use_figma` call. Validate via screenshot after each step. Return all created/mutated node IDs:
|
||||||
|
|
||||||
|
```js
|
||||||
|
return { created: { card: card.id, title: title.id } };
|
||||||
|
```
|
||||||
117
.claude/skills/figma-preflight/SKILL.md
Normal file
117
.claude/skills/figma-preflight/SKILL.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
name: figma-preflight
|
||||||
|
description: "Triggers on 'let's start', 'begin', 'preflight', 'start the session', or when a Figma file URL is first shared. Verifies MCP connection, reads CLAUDE.md, audits connected libraries, and loads a Token Map of all Styles and Variables — required before any design work."
|
||||||
|
disable-model-invocation: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Figma Preflight
|
||||||
|
|
||||||
|
Run at the start of every design session. Do NOT start design work until all steps pass.
|
||||||
|
|
||||||
|
**Prerequisite:** Load `figma-use` skill before any `use_figma` call.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step A — Connection + Config (parallel)
|
||||||
|
|
||||||
|
Run these two in parallel:
|
||||||
|
|
||||||
|
1. **MCP Connection:** Call `mcp__figma__whoami`. Must return user email and plan. Fail → stop, re-authenticate.
|
||||||
|
2. **CLAUDE.md:** Read CLAUDE.md. Extract Figma file URL (required — stop if missing), font families, session goal. If fonts field is a placeholder, auto-populate after Step C using STRING variables starting with "Family".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step B — File + Libraries (parallel)
|
||||||
|
|
||||||
|
Parse `fileKey` from the Figma URL, then run in parallel:
|
||||||
|
|
||||||
|
1. **File Access:** Call `get_metadata` with extracted nodeId and fileKey. Must return file name and pages.
|
||||||
|
2. **Libraries:** Call `get_libraries` with fileKey. Store subscribed libraries as **Library Registry** (name, libraryKey, description). These enable `search_design_system` to find library styles and components during design work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step C — Styles + Variables + Components (single use_figma call)
|
||||||
|
|
||||||
|
Combine all three inventories in one script:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const textStyles = await figma.getLocalTextStylesAsync();
|
||||||
|
const paintStyles = await figma.getLocalPaintStylesAsync();
|
||||||
|
const collections = await figma.variables.getLocalVariableCollectionsAsync();
|
||||||
|
const variables = await figma.variables.getLocalVariablesAsync();
|
||||||
|
|
||||||
|
const grouped = {};
|
||||||
|
for (const v of variables) {
|
||||||
|
const key = v.resolvedType;
|
||||||
|
if (!grouped[key]) grouped[key] = [];
|
||||||
|
grouped[key].push({ name: v.name, scopes: v.scopes });
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = {};
|
||||||
|
for (const page of figma.root.children) {
|
||||||
|
await figma.setCurrentPageAsync(page);
|
||||||
|
const sets = page.findAll(n => n.type === "COMPONENT_SET");
|
||||||
|
const solos = page.findAll(n => n.type === "COMPONENT" && n.parent.type !== "COMPONENT_SET");
|
||||||
|
if (sets.length > 0 || solos.length > 0) {
|
||||||
|
components[page.name] = {
|
||||||
|
sets: sets.map(c => c.name),
|
||||||
|
solos: solos.map(c => c.name).slice(0, 15),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
textStyles: textStyles.map(s => s.name),
|
||||||
|
paintStyles: paintStyles.map(s => s.name),
|
||||||
|
collections: collections.map(c => c.name),
|
||||||
|
variableCount: variables.length,
|
||||||
|
byType: Object.fromEntries(
|
||||||
|
Object.entries(grouped).map(([type, vars]) => [type, vars.map(v => v.name)])
|
||||||
|
),
|
||||||
|
components
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Store **names only** in context. IDs are looked up on-demand during design work. Library styles/variables are discovered via `search_design_system`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Token Map
|
||||||
|
|
||||||
|
After Step C, derive a semantic index from variables grouped by `scopes`:
|
||||||
|
|
||||||
|
| Role | Scope | Example names |
|
||||||
|
|---|---|---|
|
||||||
|
| Background fill | `FRAME_FILL`, `SHAPE_FILL` | `background/surface`, `color/neutral-100` |
|
||||||
|
| Text color | `TEXT_FILL` | `text/primary`, `color/neutral-900` |
|
||||||
|
| Border / stroke | `STROKE_COLOR` | `border/default`, `color/neutral-300` |
|
||||||
|
| Gap | `GAP` | `gap/sm`, `spacing/xxs` |
|
||||||
|
| Padding | `PADDING` | `padding/md`, `spacing/section-xl` |
|
||||||
|
| Border radius | `CORNER_RADIUS` | `radius/sm`, `radius/full` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status Report
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ MCP Connection — [name] ([email]) · [plan]
|
||||||
|
✅ CLAUDE.md — Font: [primary] / [code] · Goal: [goal]
|
||||||
|
✅ Figma File — [file name] · [N] pages
|
||||||
|
✅ Libraries — [N] connected: [names]
|
||||||
|
✅ Styles — [N] text + [N] paint
|
||||||
|
✅ Variables — [N] across [N] collections
|
||||||
|
✅ Components — [N] sets across [N] pages
|
||||||
|
|
||||||
|
── Token Map ──────────────────────────────
|
||||||
|
Background : [names]
|
||||||
|
Text : [names]
|
||||||
|
Border : [names]
|
||||||
|
Gap : [names]
|
||||||
|
Padding : [names]
|
||||||
|
Radius : [names]
|
||||||
|
────────────────────────────────────────────
|
||||||
|
|
||||||
|
Ready to design.
|
||||||
|
```
|
||||||
|
|
||||||
|
If any step fails, output ❌ with error and stop.
|
||||||
137
.claude/skills/figma-style-binding/SKILL.md
Normal file
137
.claude/skills/figma-style-binding/SKILL.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
---
|
||||||
|
name: figma-style-binding
|
||||||
|
description: "Triggers on any visual property change in Figma — creating text, setting colors, adjusting spacing/padding/gap/radius. Enforces that ALL values bind to Figma Styles or Variables, never hardcoded. Includes post-write QA verification."
|
||||||
|
disable-model-invocation: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Style Binding + QA
|
||||||
|
|
||||||
|
Every visual value must come from the design system. Supplements `figma-generate-design`. Prerequisite: `figma-preflight` must have run this session.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Binding Hierarchy
|
||||||
|
|
||||||
|
For any visual property, follow this order. Stop at the first match.
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Connected Library → search_design_system → import → apply
|
||||||
|
2. Local Style → Style Registry → apply by ID
|
||||||
|
3. Local Variable → Variable Registry → apply by ID
|
||||||
|
4. Gap found → Report to user, wait for decision
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Text
|
||||||
|
|
||||||
|
Every text node must use `textStyleId`. Individual font properties (`fontSize`, `fontFamily`, etc.) are forbidden.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const style = await figma.getStyleByIdAsync("<id>");
|
||||||
|
await figma.loadFontAsync(style.fontName);
|
||||||
|
node.textStyleId = "<id>";
|
||||||
|
```
|
||||||
|
|
||||||
|
If no local style matches, search libraries via `search_design_system`. If no match anywhere:
|
||||||
|
```
|
||||||
|
⚠️ Text style gap: no style for "[role]". Available: [top 5]. Use closest, or add missing style?
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Color Fills
|
||||||
|
|
||||||
|
Every fill/stroke must bind to a COLOR Variable (preferred, supports theming) or Paint Style.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Variable binding (preferred)
|
||||||
|
const variable = await figma.variables.getVariableByIdAsync("<id>");
|
||||||
|
const fill = { type: "SOLID", color: { r: 0, g: 0, b: 0 } };
|
||||||
|
node.fills = [figma.variables.setBoundVariableForPaint(fill, "color", variable)];
|
||||||
|
|
||||||
|
// Paint Style binding
|
||||||
|
node.fillStyleId = "<id>";
|
||||||
|
```
|
||||||
|
|
||||||
|
Never use raw `{ r, g, b }` without a binding.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spacing, Padding, Gap, Radius
|
||||||
|
|
||||||
|
Bind to FLOAT Variables. `layoutMode` must be set BEFORE `setBoundVariable`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
node.setBoundVariable("paddingTop", spacingVar);
|
||||||
|
node.setBoundVariable("paddingBottom", spacingVar);
|
||||||
|
node.setBoundVariable("paddingLeft", spacingVar);
|
||||||
|
node.setBoundVariable("paddingRight", spacingVar);
|
||||||
|
node.setBoundVariable("itemSpacing", spacingVar);
|
||||||
|
node.setBoundVariable("cornerRadius", radiusVar);
|
||||||
|
```
|
||||||
|
|
||||||
|
Spacing can fall back to raw values temporarily with user confirmation. Color and text cannot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Forbidden / Required
|
||||||
|
|
||||||
|
| Forbidden | Required |
|
||||||
|
|---|---|
|
||||||
|
| `node.fontSize = 24` | `node.textStyleId = id` |
|
||||||
|
| `node.fills = [{ type: "SOLID", color: { r: .2, g: .4, b: 1 } }]` | Variable or Style binding |
|
||||||
|
| `node.paddingLeft = 16` | `node.setBoundVariable("paddingLeft", var)` |
|
||||||
|
| `node.cornerRadius = 8` | `node.setBoundVariable("cornerRadius", var)` |
|
||||||
|
| Creating a Button from scratch | `importComponentByKeyAsync` from library |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## QA Verification
|
||||||
|
|
||||||
|
After every `use_figma` call that creates or modifies nodes, run this verification on the returned node IDs:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const nodeIdsToAudit = [/* paste returned IDs */];
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const id of nodeIdsToAudit) {
|
||||||
|
const node = await figma.getNodeByIdAsync(id);
|
||||||
|
if (!node) { results.push({ id, status: "NOT_FOUND" }); continue; }
|
||||||
|
|
||||||
|
const checks = [];
|
||||||
|
|
||||||
|
if (node.type === "TEXT") {
|
||||||
|
checks.push({ prop: "textStyleId", bound: !!node.textStyleId });
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("fills" in node && Array.isArray(node.fills) && node.fills.length > 0) {
|
||||||
|
const bound = !!node.fillStyleId || (node.boundVariables?.fills?.length > 0);
|
||||||
|
checks.push({ prop: "fills", bound });
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("layoutMode" in node && node.layoutMode !== "NONE") {
|
||||||
|
for (const p of ["paddingLeft","paddingRight","paddingTop","paddingBottom","itemSpacing"]) {
|
||||||
|
if (p in node) checks.push({ prop: p, bound: !!(node.boundVariables && p in node.boundVariables) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("cornerRadius" in node && node.cornerRadius > 0) {
|
||||||
|
checks.push({ prop: "cornerRadius", bound: !!(node.boundVariables && "cornerRadius" in node.boundVariables) });
|
||||||
|
}
|
||||||
|
|
||||||
|
const failed = checks.filter(c => !c.bound);
|
||||||
|
results.push({ id, name: node.name, type: node.type, status: failed.length === 0 ? "PASS" : "FAIL", failed: failed.map(c => c.prop) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { auditResults: results };
|
||||||
|
```
|
||||||
|
|
||||||
|
**If FAIL:** Fix each unbound property using the binding rules above, then re-audit. Do not proceed to the next design step until all pass.
|
||||||
|
|
||||||
|
**Report format:**
|
||||||
|
```
|
||||||
|
✅ All [N] nodes passed.
|
||||||
|
// or
|
||||||
|
❌ FAIL "Card" (FRAME) — paddingTop, cornerRadius unbound. Fixing...
|
||||||
|
```
|
||||||
88
.claude/skills/reference-interpreter/SKILL.md
Normal file
88
.claude/skills/reference-interpreter/SKILL.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
name: reference-interpreter
|
||||||
|
description: "Triggers when user shares a screenshot, image, URL, or design description — or says 'analyze this', 'make a brief', 'interpret this reference'. Outputs a structured Design Brief mapping visual intent to design system tokens. Waits for 'confirmed' before designing."
|
||||||
|
disable-model-invocation: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Reference Interpreter
|
||||||
|
|
||||||
|
Analyze a reference (screenshot, URL, or description) and produce a **Design Brief** that maps visual observations to design system tokens. Output the Brief, then **stop and wait for user confirmation** before building anything.
|
||||||
|
|
||||||
|
Prerequisite: `figma-preflight` should have run so the Token Map and Style Registry are available.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 — Analyze
|
||||||
|
|
||||||
|
Examine the reference across these dimensions:
|
||||||
|
|
||||||
|
1. **Layout** — structure, columns, section heights, grid width, alignment
|
||||||
|
2. **Typography** — heading/body hierarchy, weight contrast, tracking
|
||||||
|
3. **Color** — dark/light sections, accent usage, neutral dominance
|
||||||
|
4. **Spacing** — generous vs compact, section padding, internal gap
|
||||||
|
5. **Visual Anchor** — what draws the eye: large type, hero image, illustration
|
||||||
|
6. **Components** — what UI elements are visible: cards, buttons, forms, nav
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2 — Map to Design System
|
||||||
|
|
||||||
|
For each observation, identify the specific Token or Style from the session's Token Map:
|
||||||
|
|
||||||
|
```
|
||||||
|
"Large dark headline" → Text Style: heading/h1 · Color: text/primary
|
||||||
|
"Neutral section bg" → Variable: background/surface-2
|
||||||
|
"Tight card spacing" → Gap: gap/xs · Padding: padding/sm
|
||||||
|
```
|
||||||
|
|
||||||
|
If no token exists, flag it:
|
||||||
|
```
|
||||||
|
⚠️ Gap: [observation] — no matching token. Options: (a) nearest match: [name], (b) add token first.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3 — Output the Design Brief
|
||||||
|
|
||||||
|
```
|
||||||
|
## Design Brief
|
||||||
|
|
||||||
|
**Reference**: [source]
|
||||||
|
**Section**: [what this covers]
|
||||||
|
**Aesthetic**: [3-5 keywords]
|
||||||
|
|
||||||
|
### Layout
|
||||||
|
[structure, height, alignment]
|
||||||
|
|
||||||
|
### Typography
|
||||||
|
- Heading: [Style name] — [why]
|
||||||
|
- Body: [Style name]
|
||||||
|
|
||||||
|
### Colors
|
||||||
|
- Background: [Variable] — [context]
|
||||||
|
- Text: [Variable]
|
||||||
|
- Accent: [Variable] — [used for]
|
||||||
|
|
||||||
|
### Spacing
|
||||||
|
- Section padding: [Variable]
|
||||||
|
- Internal gap: [Variable]
|
||||||
|
|
||||||
|
### Components needed
|
||||||
|
- [Name]: [from library? source]
|
||||||
|
|
||||||
|
### Gaps
|
||||||
|
- [Gap description] — awaiting decision
|
||||||
|
- (none) [if all mapped]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4 — Wait
|
||||||
|
|
||||||
|
Output exactly:
|
||||||
|
|
||||||
|
```
|
||||||
|
Brief complete. Type `confirmed` to begin building, or tell me what to adjust.
|
||||||
|
```
|
||||||
|
|
||||||
|
Do NOT call `use_figma` or place any nodes until user types `confirmed`.
|
||||||
@@ -9,6 +9,7 @@ const config: StorybookConfig = {
|
|||||||
"@storybook/addon-vitest",
|
"@storybook/addon-vitest",
|
||||||
"@storybook/addon-a11y",
|
"@storybook/addon-a11y",
|
||||||
"@storybook/addon-docs",
|
"@storybook/addon-docs",
|
||||||
|
"@storybook/addon-designs",
|
||||||
"@storybook/addon-mcp"
|
"@storybook/addon-mcp"
|
||||||
],
|
],
|
||||||
"framework": "@storybook/react-vite"
|
"framework": "@storybook/react-vite"
|
||||||
|
|||||||
@@ -107,6 +107,20 @@ src/
|
|||||||
- Key tools: `get_design_context`, `get_variable_defs`, `get_screenshot`, `search_design_system`, `use_figma`
|
- Key tools: `get_design_context`, `get_variable_defs`, `get_screenshot`, `search_design_system`, `use_figma`
|
||||||
- Design tokens extracted via `get_variable_defs` → mapped to `@theme` values in `tokens.css`
|
- Design tokens extracted via `get_variable_defs` → mapped to `@theme` values in `tokens.css`
|
||||||
|
|
||||||
|
### Code Connect
|
||||||
|
- Links Figma components to their React implementations
|
||||||
|
- Once linked, `get_design_context` returns actual component code instead of generic markup
|
||||||
|
- Mapped as we build each component via `add_code_connect_map` (label: "React")
|
||||||
|
|
||||||
### Storybook MCP
|
### Storybook MCP
|
||||||
- Available at `localhost:6006/mcp` when Storybook dev server is running
|
- Available at `localhost:6006/mcp` when Storybook dev server is running
|
||||||
- Provides: component listing, documentation retrieval, story generation, a11y testing
|
- Provides: component listing, documentation retrieval, story generation, a11y testing
|
||||||
|
- `@storybook/addon-designs` embeds Figma frames in the story panel for side-by-side comparison
|
||||||
|
- `@storybook/addon-mcp` serves the MCP endpoint
|
||||||
|
|
||||||
|
### claude2figma Skills
|
||||||
|
- 4 skills in `.claude/skills/` that enforce design system compliance when writing to Figma
|
||||||
|
- **figma-preflight**: Validates MCP connection, audits libraries, builds a Token Map of all styles/variables
|
||||||
|
- **component-rules**: Library-first lookup, Auto Layout conventions, semantic node naming
|
||||||
|
- **figma-style-binding**: All visual values must bind to Figma Styles or Variables, never hardcoded; includes post-write QA
|
||||||
|
- **reference-interpreter**: Converts screenshots/references into structured Design Briefs mapped to design tokens
|
||||||
|
|||||||
69
package-lock.json
generated
69
package-lock.json
generated
@@ -20,6 +20,7 @@
|
|||||||
"@chromatic-com/storybook": "^5.2.1",
|
"@chromatic-com/storybook": "^5.2.1",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@storybook/addon-a11y": "^10.4.0",
|
"@storybook/addon-a11y": "^10.4.0",
|
||||||
|
"@storybook/addon-designs": "^11.1.3",
|
||||||
"@storybook/addon-docs": "^10.4.0",
|
"@storybook/addon-docs": "^10.4.0",
|
||||||
"@storybook/addon-mcp": "^0.6.0",
|
"@storybook/addon-mcp": "^0.6.0",
|
||||||
"@storybook/addon-vitest": "^10.4.0",
|
"@storybook/addon-vitest": "^10.4.0",
|
||||||
@@ -916,6 +917,27 @@
|
|||||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@figspec/components": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@figspec/components/-/components-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-PFKBX2oFz+vhThKTNsu7Mh4ZT3X7YCiM694UkAMT36j/p0tdmXs9Je0Sf88stTEcMgwYvNv9TZtvniYmgaE+bw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@figspec/react": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@figspec/react/-/react-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-xflqJ3XQZVzm8+7dsm8OFxVAmBNNA3Mg65sqwNHiq7VRSMSD7qwH4BPsBy07ZaX+9nHeaacBpFZd3Q0aIsISqw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@figspec/components": "^2.0.1",
|
||||||
|
"@lit-labs/react": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@fontsource-variable/public-sans": {
|
"node_modules/@fontsource-variable/public-sans": {
|
||||||
"version": "5.2.7",
|
"version": "5.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource-variable/public-sans/-/public-sans-5.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/public-sans/-/public-sans-5.2.7.tgz",
|
||||||
@@ -1056,6 +1078,26 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lit-labs/react": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-OD9h2JynerBQUMNzb563jiVpxfvPF0HjQkKY2mx0lpVYvD7F+rtJpOGz6ek+6ufMidV3i+MPT9SX62OKWHFrQg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit/react": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lit/react": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "17 || 18 || 19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mdx-js/react": {
|
"node_modules/@mdx-js/react": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
|
||||||
@@ -2125,6 +2167,33 @@
|
|||||||
"storybook": "^10.4.0"
|
"storybook": "^10.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@storybook/addon-designs": {
|
||||||
|
"version": "11.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@storybook/addon-designs/-/addon-designs-11.1.3.tgz",
|
||||||
|
"integrity": "sha512-AK+ij478Y6S16TCNPwm7H90OipVe2wZApOlHjC6qDvMW61zyd4yP1icrRtjehSadw5SCoz8HcAmIYfQCOY6E4A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@figspec/react": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@storybook/addon-docs": "^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"storybook": "^10.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@storybook/addon-docs": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@storybook/addon-docs": {
|
"node_modules/@storybook/addon-docs": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.4.0.tgz",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"@chromatic-com/storybook": "^5.2.1",
|
"@chromatic-com/storybook": "^5.2.1",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@storybook/addon-a11y": "^10.4.0",
|
"@storybook/addon-a11y": "^10.4.0",
|
||||||
|
"@storybook/addon-designs": "^11.1.3",
|
||||||
"@storybook/addon-docs": "^10.4.0",
|
"@storybook/addon-docs": "^10.4.0",
|
||||||
"@storybook/addon-mcp": "^0.6.0",
|
"@storybook/addon-mcp": "^0.6.0",
|
||||||
"@storybook/addon-vitest": "^10.4.0",
|
"@storybook/addon-vitest": "^10.4.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user