@radicool/throughline 0.21.3 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/adapters/codex/prompts/component-builder.md +7 -6
- package/adapters/codex/prompts/design-system-audit.md +3 -1
- package/adapters/codex/prompts/figma-environment-setup.md +28 -30
- package/adapters/codex/prompts/storybook-chromatic-builder.md +4 -0
- package/adapters/codex/prompts/token-builder.md +1 -1
- package/adapters/codex/prompts/token-sheet-builder.md +1 -1
- package/adapters/codex/prompts/token-sync-layer.md +6 -3
- package/adapters/cursor/.cursor/rules/component-builder.mdc +7 -6
- package/adapters/cursor/.cursor/rules/design-system-audit.mdc +3 -1
- package/adapters/cursor/.cursor/rules/figma-environment-setup.mdc +28 -30
- package/adapters/cursor/.cursor/rules/storybook-chromatic-builder.mdc +4 -0
- package/adapters/cursor/.cursor/rules/token-builder.mdc +1 -1
- package/adapters/cursor/.cursor/rules/token-sheet-builder.mdc +1 -1
- package/adapters/cursor/.cursor/rules/token-sync-layer.mdc +6 -3
- package/adapters/generic/skills/component-builder/SKILL.md +7 -6
- package/adapters/generic/skills/design-system-audit/SKILL.md +3 -1
- package/adapters/generic/skills/figma-environment-setup/SKILL.md +28 -30
- package/adapters/generic/skills/storybook-chromatic-builder/SKILL.md +4 -0
- package/adapters/generic/skills/token-builder/SKILL.md +1 -1
- package/adapters/generic/skills/token-sheet-builder/SKILL.md +1 -1
- package/adapters/generic/skills/token-sync-layer/SKILL.md +6 -3
- package/package.json +1 -1
- package/references/figma-component-standards.md +17 -19
- package/references/figma-scripting.md +2 -1
- package/references/manifest-schema.md +6 -4
- package/references/state-baseline.md +25 -0
- package/scripts/README.md +3 -2
- package/scripts/build-state-baseline.mjs +69 -0
- package/scripts/guard-token-removal.mjs +4 -4
- package/scripts/lib/component-states.mjs +24 -6
- package/scripts/lib/source-scan.mjs +3 -3
package/README.md
CHANGED
|
@@ -73,9 +73,9 @@ Everything above was created during a single working session and synced directly
|
|
|
73
73
|
|
|
74
74
|
| | |
|
|
75
75
|
|---|---|
|
|
76
|
-
| **A supported agent** | Required — [Claude Code](https://docs.claude.com/en/docs/claude-code) (native plugin), or **Cursor**, **Codex**, or any **AGENTS.md**-aware agent via `npx @radicool/throughline@0.
|
|
76
|
+
| **A supported agent** | Required — [Claude Code](https://docs.claude.com/en/docs/claude-code) (native plugin), or **Cursor**, **Codex**, or any **AGENTS.md**-aware agent via `npx @radicool/throughline@0.22.0 init`. |
|
|
77
77
|
| **Figma** | Required, **desktop app** (the browser version causes connection errors). **Professional plan or higher recommended** — multi-mode variables (Light/Dark, brand themes) need it. |
|
|
78
|
-
| **Figma access token** |
|
|
78
|
+
| **Figma access token** | Optional — only for the few Figma calls the Desktop Bridge can't make, like reading a published library. Without it, you work through the bridge alone. The setup skill walks you through it; your token stays yours and is never shared in chat. |
|
|
79
79
|
| **GitHub** (or similar) | Optional — only when you're ready to graduate to a real remote repo with PRs and CI. |
|
|
80
80
|
|
|
81
81
|
### What it runs on your machine
|
|
@@ -114,9 +114,9 @@ Update anytime with `/plugin marketplace update throughline-marketplace`.
|
|
|
114
114
|
Run the installer in your project — it stamps in the skills, the reference docs, the scripts, and the Figma MCP config for your tool:
|
|
115
115
|
|
|
116
116
|
```
|
|
117
|
-
npx @radicool/throughline@0.
|
|
118
|
-
npx @radicool/throughline@0.
|
|
119
|
-
npx @radicool/throughline@0.
|
|
117
|
+
npx @radicool/throughline@0.22.0 init --target=cursor # → .cursor/rules + .cursor/mcp.json
|
|
118
|
+
npx @radicool/throughline@0.22.0 init --target=codex # → prompts/ + AGENTS.md index + codex-mcp.toml
|
|
119
|
+
npx @radicool/throughline@0.22.0 init --target=generic # → skills/ + AGENTS.md index
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
It's safe to re-run (it merges `AGENTS.md` and `.cursor/mcp.json` non-destructively) and stages everything the skills read into `.throughline/`. Then open the `figma-environment-setup` skill/rule/prompt for your tool to begin. For Codex, add the printed `codex-mcp.toml` block to your Codex config to enable Figma access.
|
|
@@ -127,7 +127,7 @@ ThroughLine is more than a pile of skills — it's a small system designed to st
|
|
|
127
127
|
|
|
128
128
|
**🛠 Twelve skills — the steps.** Each owns one stage of the journey, from connecting Figma to retrofitting an existing system to standing up CI. They're sequenced, and each knows its prerequisites.
|
|
129
129
|
|
|
130
|
-
**📐
|
|
130
|
+
**📐 The reference docs — the constitution.** Shared standards every skill obeys: Figma component rules (auto-layout-on-everything, slot contracts, naming-as-contract, a required post-build audit), the brainstorm-before-build protocol, the sync-adapter specs, the manifest schema, coding-level adaptivity, the publishing flow, and the brownfield-retrofit discipline (read-before-assert, the safe migration sequence, the crosswalk contract). This is *why* two different runs produce the same structure — the rules live in one place, not scattered per skill.
|
|
131
131
|
|
|
132
132
|
**🧭 An orchestration layer — the memory.** A `design-system.json` manifest in your project records exactly what's set up, with a versioned schema and immutability rules. Every skill reads it, tells you what it's about to do, and offers to run anything missing first. Run **`/design-system-status`** anytime for a plain-language picture of where you stand.
|
|
133
133
|
|
|
@@ -162,7 +162,7 @@ For the technically curious — how the machine actually runs.
|
|
|
162
162
|
|
|
163
163
|
**Modes, within Figma's limits.** Light/Dark lives on the semantic collections; brand variants live on the primitive palette. Splitting the two axes across two collections keeps each one under Figma's 4-modes-per-collection cap on the Professional plan while still resolving correctly (`bg/default` → `{gray/50}` → the active brand's gray).
|
|
164
164
|
|
|
165
|
-
**Model routing.** ThroughLine routes work by *cognition, not guesswork* — the expensive thinking on the best model, the mechanical doing on a cheap one.
|
|
165
|
+
**Model routing.** ThroughLine routes work by *cognition, not guesswork* — the expensive thinking on the best model, the mechanical doing on a cheap one. Inside a skill, the deciding — a token architecture, a variant matrix, a slot contract — is planned once on the strongest tier, while the long mechanical part — placing Figma nodes, transcribing component code, running SVGR — runs on a cheap tier and self-verifies. You get the best model where it matters and a cheap one where it doesn't, without micromanaging it. The recommended mapping is **Haiku → Sonnet → Opus** for the `fast < balanced < deep` tiers, but nothing is hardcoded — the ladder resolves against whatever models your install actually has, and on hosts without subagents skills degrade cleanly to a single model. You can still steer the session yourself (`/model opus` for the heaviest authoring, back to `/model sonnet` after); nothing forces an expensive model on you.
|
|
166
166
|
|
|
167
167
|
## Who it's for
|
|
168
168
|
|
|
@@ -212,7 +212,7 @@ Future improvements and planned capabilities. Have a request? **[Open an issue](
|
|
|
212
212
|
- **Deeper Code Connect coverage** — richer Figma-to-code mappings as more plans support it.
|
|
213
213
|
- **Expanded component starters** — a larger foundational kit out of the box.
|
|
214
214
|
- **Richer status & auditing** — more from `/design-system-status`, including drift detection between Figma and code.
|
|
215
|
-
- **Built-in accessibility checks** — the color half is shipped. Eight semantic text-on-surface pairs are checked against WCAG AA's 4.5:1 when tokens are created in Figma, and derived again from the token source when they sync to code — in every mode, so a system that clears in light and fails in dark fails. A failing pair stops the build rather than warning. Components are checked too: on web frameworks, a Figma build stops when a focus state has no visible ring, and in JSX code the adherence gate
|
|
215
|
+
- **Built-in accessibility checks** — the color half is shipped. Eight semantic text-on-surface pairs are checked against WCAG AA's 4.5:1 when tokens are created in Figma, and derived again from the token source when they sync to code — in every mode, so a system that clears in light and fails in dark fails. A failing pair stops the build rather than warning. Components are checked too: on web frameworks, a Figma build stops when a focus state has no visible ring, and in JSX code the adherence gate is built to fail an icon-only button with no accessible name and a status badge that relies on color alone — proven by unit tests. The one real app it has run on had almost nothing for it to check, so it hasn't caught a bug in the wild yet. Still ahead: focus-ring contrast (WCAG's 3:1 for non-text).
|
|
216
216
|
- **Token fan-out to more platforms** — the DTCG token source is platform-neutral, and web targets are in daily use. Every target is **validated per build, not assumed**: `tokens:validate-output` checks generated output against its source, native Swift and Kotlin as well as web CSS, because the stock transforms have been measured emitting wrong-but-compiling values. iOS/Swift is a curated adapter whose Style Dictionary configuration ships as tested code, and Android/Kotlin uses the same configuration through the Tier 2 protocol. JavaScript theme output (MUI, a Tailwind v3 config) isn't checked yet.
|
|
217
217
|
- **Native component code generation** — producing a SwiftUI view or a Compose composable the way Storybook components are produced for React. This does not exist yet; it is a separate, larger effort than token fan-out, and the two were previously described as one roadmap item.
|
|
218
218
|
|
|
@@ -8,7 +8,7 @@ translate cleanly to code later.
|
|
|
8
8
|
|
|
9
9
|
Needs tokens (`tokens.semanticBuilt` true) — offer to run `token-builder` if
|
|
10
10
|
missing. Needs a live Figma connection (offer `figma-environment-setup` if not).
|
|
11
|
-
|
|
11
|
+
The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
12
12
|
|
|
13
13
|
**Before scripting any `figma_execute`, read
|
|
14
14
|
`.throughline/references/figma-scripting.md`** — the single-bridge-instance
|
|
@@ -67,8 +67,9 @@ inconsistent output:
|
|
|
67
67
|
- **Sizes** (sm, md, lg). Each size is its **own variant row**, not a state — see
|
|
68
68
|
the layout law below.
|
|
69
69
|
- **States** — **include the full relevant set, don't trim it.** The baseline is
|
|
70
|
-
|
|
71
|
-
wherever they apply (loading,
|
|
70
|
+
the set `.throughline/references/state-baseline.md` owes that component
|
|
71
|
+
(default included); add the conditional states wherever they apply (loading,
|
|
72
|
+
success/error). Decide which conditional
|
|
72
73
|
states a given component can reach, but never drop a state it genuinely has. See
|
|
73
74
|
"State handling" in `.throughline/references/figma-component-standards.md`
|
|
74
75
|
for the per-component checklist.
|
|
@@ -400,9 +401,9 @@ Offer next steps: build the code counterparts and stories
|
|
|
400
401
|
auto-layout grid where **variants are rows and states are columns**: one row per
|
|
401
402
|
variant (each `type`, and each `size`, since size is a variant) stepping through
|
|
402
403
|
states across the columns, size groups stacked vertically (see the standards doc).
|
|
403
|
-
- Never ship a component with only its `default` state — include
|
|
404
|
-
state
|
|
405
|
-
loading/
|
|
404
|
+
- Never ship a component with only its `default` state — include every state
|
|
405
|
+
`.throughline/references/state-baseline.md` owes that component, plus
|
|
406
|
+
`default`, plus any applicable loading/success/error.
|
|
406
407
|
- Never run the component header and the component area together with no division —
|
|
407
408
|
every doc card segments the header from the component area with a divider line or
|
|
408
409
|
a distinct header surface.
|
|
@@ -22,7 +22,9 @@ across levels.
|
|
|
22
22
|
|
|
23
23
|
Read the manifest. This skill needs Figma connected (`figma.connected: true`) for the
|
|
24
24
|
inventory step, and a repo path for the code-surface step (`workspace.localPath`). If
|
|
25
|
-
Figma isn't connected, offer to run `figma-environment-setup` first.
|
|
25
|
+
Figma isn't connected, offer to run `figma-environment-setup` first. Before the
|
|
26
|
+
inventory, do a cheap liveness read to confirm the connection is actually live right
|
|
27
|
+
now; if it fails, offer `figma-environment-setup`. The code-surface
|
|
26
28
|
step works on any repo regardless of `workspace.stage`.
|
|
27
29
|
|
|
28
30
|
## Step 1 — Size the code surface
|
|
@@ -9,8 +9,10 @@ This skill does three foundational jobs, in order:
|
|
|
9
9
|
3. **Connect Codex to Figma** so later skills can read and write the file,
|
|
10
10
|
then verify the connection works.
|
|
11
11
|
|
|
12
|
-
It is the required first step. Every other Figma
|
|
13
|
-
check and, if it fails, points the user back here.
|
|
12
|
+
It is the required first step. Every other skill that calls Figma itself does a
|
|
13
|
+
cheap liveness check and, if it fails, points the user back here. (The orchestrators
|
|
14
|
+
`component-pipeline` and `retrofit-planner` call Figma only through the skills they
|
|
15
|
+
invoke, and `token-crosswalk-builder` works from the manifest and token files.)
|
|
14
16
|
|
|
15
17
|
## Who you're talking to
|
|
16
18
|
|
|
@@ -223,28 +225,23 @@ Make clear it changes only how much detail they get, never what they can build.
|
|
|
223
225
|
This is set now because it colors every later interaction. The Figma phase
|
|
224
226
|
itself doesn't lean on it much, but the code phase (repo, sync, Storybook) does.
|
|
225
227
|
|
|
226
|
-
## Step 3 —
|
|
228
|
+
## Step 3 — Figma write mechanism
|
|
227
229
|
|
|
228
|
-
|
|
230
|
+
ThroughLine connects to Figma through the **Figma Console MCP**. It can create
|
|
231
|
+
many variables at once efficiently (which saves time and usage), and it can read
|
|
232
|
+
your variables on any Figma plan, including free and Pro. The setup is light:
|
|
233
|
+
you'll run a small helper plugin inside Figma, and can optionally create one
|
|
234
|
+
access token.
|
|
229
235
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
your variables on any Figma plan, including free and Pro. Slightly more setup:
|
|
233
|
-
you'll create one access token and run a small helper plugin inside Figma.
|
|
234
|
-
- **Official Figma plugin (simpler fallback).** Less setup, but more limited —
|
|
235
|
-
some bulk operations are slower, and reading variables through it can be
|
|
236
|
-
restricted on non-Enterprise plans. Fine if the user wants the lightest path.
|
|
237
|
-
|
|
238
|
-
Let the user choose. Record their choice in `figma.mechanism`
|
|
239
|
-
(`"console-mcp"` or `"official-plugin"`). Default to `console-mcp` if they have
|
|
240
|
-
no preference.
|
|
236
|
+
Record `figma.mechanism` as `"console-mcp"`. There is nothing for the user to
|
|
237
|
+
choose.
|
|
241
238
|
|
|
242
239
|
The MCP server configuration ships **bundled with this plugin** (in the plugin's
|
|
243
240
|
`.mcp.json`), so the user does not hand-edit any config files. The one thing the
|
|
244
241
|
bundle can't contain is their personal access token — that's a per-user secret,
|
|
245
242
|
handled in the next step.
|
|
246
243
|
|
|
247
|
-
## Step 4 — Connect to Figma
|
|
244
|
+
## Step 4 — Connect to Figma
|
|
248
245
|
|
|
249
246
|
Walk through these one at a time, confirming each before moving on. Never rush
|
|
250
247
|
the user past a step.
|
|
@@ -257,10 +254,16 @@ browser version of Figma causes connection and token errors that are painful to
|
|
|
257
254
|
debug. If they don't have the desktop app, point them to figma.com/downloads to
|
|
258
255
|
install it first. Desktop app, every time.
|
|
259
256
|
|
|
260
|
-
### 4b. Create a Figma access token
|
|
257
|
+
### 4b. Create a Figma access token (optional)
|
|
258
|
+
|
|
259
|
+
A token is like a password that lets Codex talk to Figma on your behalf. It's
|
|
260
|
+
**optional**: the desktop bridge does the reading and writing on its own. The
|
|
261
|
+
token only adds the few calls the bridge can't make, such as reading a published
|
|
262
|
+
library or a file's version history. Offer it, and say plainly that they can skip
|
|
263
|
+
it and add it later in **Configure options**. If they skip, record nothing about
|
|
264
|
+
it, leave the field blank, and go straight to 4c. Nothing later in setup needs it.
|
|
261
265
|
|
|
262
|
-
|
|
263
|
-
them through it:
|
|
266
|
+
If they want it, walk them through it:
|
|
264
267
|
|
|
265
268
|
1. In a browser, go to figma.com and sign in (same account as the desktop app).
|
|
266
269
|
2. Click your account menu, then Settings.
|
|
@@ -309,14 +312,6 @@ Codex and Figma are linked for this session."
|
|
|
309
312
|
If the config was just added (token placed for the first time), the MCP client
|
|
310
313
|
may need a restart to load it. Tell them how, simply, and confirm they're back.
|
|
311
314
|
|
|
312
|
-
## Step 4 (alt) — Connect to Figma (official plugin path)
|
|
313
|
-
|
|
314
|
-
If the user chose the official plugin, the flow is lighter: install the official
|
|
315
|
-
Figma plugin for Codex per its setup, ensure the desktop app is open with
|
|
316
|
-
the file, and authenticate through the desktop app (again: desktop, not
|
|
317
|
-
browser). The same secret-handling rule applies — any token goes into config by
|
|
318
|
-
the user, never through the chat.
|
|
319
|
-
|
|
320
315
|
## Step 5 — Capture the file key
|
|
321
316
|
|
|
322
317
|
Ask the user for the URL of the Figma file they want to build the design system
|
|
@@ -384,7 +379,7 @@ tokens or pairing here is useless. Diagnose in order:
|
|
|
384
379
|
|
|
385
380
|
**B. The server started but the connection failed.** If there's no launch error
|
|
386
381
|
and the server is clearly running, diagnose gently and in order: Is the desktop
|
|
387
|
-
app open with the file?
|
|
382
|
+
app open with the file? If they added a token, was it placed correctly (have them re-check,
|
|
388
383
|
without showing you the value)? Is the bridge plugin running and paired? Did the
|
|
389
384
|
client need a restart? Walk them back through the relevant sub-step.
|
|
390
385
|
|
|
@@ -400,7 +395,8 @@ neutral placeholder built against nothing.
|
|
|
400
395
|
|
|
401
396
|
- Create one trivial throwaway node (e.g. a small frame or text node named
|
|
402
397
|
`__throughline write test`) on any page.
|
|
403
|
-
- Confirm it landed with a quick read or `figma_capture_screenshot`.
|
|
398
|
+
- Confirm it landed with a quick read or `figma_capture_screenshot`. Neither needs
|
|
399
|
+
a token.
|
|
404
400
|
- **Delete it.** If both the write and the delete succeed, writes are proven.
|
|
405
401
|
|
|
406
402
|
Leave `figma.coverPageBuilt` = `false` here — that flag is set later, when the
|
|
@@ -445,7 +441,9 @@ restore point:
|
|
|
445
441
|
`figma_get_file_versions`) or have the user name a Figma version so there's a known-good
|
|
446
442
|
point to restore to. The plugin can read versions; it does not auto-create named
|
|
447
443
|
versions, so if none exists, ask the user to add one ("File → Save to version
|
|
448
|
-
history") and record that you did.
|
|
444
|
+
history") and record that you did. If the user skipped the access token and
|
|
445
|
+
the version read fails, say in one sentence that you can't read version history
|
|
446
|
+
without it, and have them name the version themselves.
|
|
449
447
|
- **Token export** — if the repo already emits tokens, snapshot the current generated
|
|
450
448
|
output (git is the natural baseline once `workspace.stage` is `local-git`+).
|
|
451
449
|
|
|
@@ -16,6 +16,10 @@ This skill needs:
|
|
|
16
16
|
- Figma components (`components.built`) with their slot contracts, so stories
|
|
17
17
|
reflect the real component APIs.
|
|
18
18
|
|
|
19
|
+
The Figma-touching steps (Code Connect in Step 5, the doc-card write-back in Step 6)
|
|
20
|
+
also need a live Figma connection — do a cheap liveness read before the first Figma
|
|
21
|
+
call; if it fails, offer `figma-environment-setup`.
|
|
22
|
+
|
|
19
23
|
Strongly recommend `github` stage so Chromatic's CI integration works; it can be
|
|
20
24
|
set up locally first and wired to CI when the remote exists.
|
|
21
25
|
|
|
@@ -35,7 +35,7 @@ now. If it isn't, say so plainly and offer to run the `figma-environment-setup`
|
|
|
35
35
|
skill first: "Figma isn't connected yet — want me to set that up? It's a
|
|
36
36
|
one-time thing." Don't proceed until Figma is reachable.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
39
39
|
With Console MCP, prefer `figma_execute` to create variables in scripted loops
|
|
40
40
|
rather than one tool call per variable — this is dramatically more
|
|
41
41
|
token-efficient for the potentially hundreds of primitives across modes.
|
|
@@ -24,7 +24,7 @@ a sensible companion step — the sheet and the synced code both reflect Figma.)
|
|
|
24
24
|
|
|
25
25
|
Needs tokens in Figma (`tokens.semanticBuilt` true) and a live Figma connection.
|
|
26
26
|
If tokens don't exist, offer to run `token-builder`. If Figma isn't connected,
|
|
27
|
-
offer `figma-environment-setup`.
|
|
27
|
+
offer `figma-environment-setup`. The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
28
28
|
|
|
29
29
|
**Before scripting any `figma_execute`, read
|
|
30
30
|
`.throughline/references/figma-scripting.md`.** This sheet builds large
|
|
@@ -66,8 +66,8 @@ it.
|
|
|
66
66
|
|
|
67
67
|
## Step 2 — Extract Figma variables to DTCG JSON
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
The plugin talks to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it. Using Console MCP's
|
|
70
|
+
full-design-system extraction (which works on any Figma plan), read
|
|
71
71
|
**every variable collection** (there are now several per tier, e.g.
|
|
72
72
|
`_Color/Primitive`, `Spacing/Primitive`, `Color/Semantic`, …) and normalize them
|
|
73
73
|
into **DTCG-format JSON** (`$value`, `$type`, with semantic tokens expressed as
|
|
@@ -140,7 +140,10 @@ fit in the adapter output.
|
|
|
140
140
|
|
|
141
141
|
## Step 3 — Set up Style Dictionary + adapters
|
|
142
142
|
|
|
143
|
-
Install
|
|
143
|
+
Install Style Dictionary pinned to an exact version with
|
|
144
|
+
`pnpm add -D style-dictionary@4.4.0` (or the project's package manager) in
|
|
145
|
+
`packages/tokens/`; the generated config is verified against 4.4.0, and a newer
|
|
146
|
+
release can rename or add stock transforms. Then configure it. For each chosen
|
|
144
147
|
platform, apply its adapter preset (per `.throughline/references/sync-adapters.md`):
|
|
145
148
|
register the platform, transform group, format, and `outputReferences`
|
|
146
149
|
(true for web → preserves the semantic→primitive cascade; false for native →
|
|
@@ -12,7 +12,7 @@ translate cleanly to code later.
|
|
|
12
12
|
|
|
13
13
|
Needs tokens (`tokens.semanticBuilt` true) — offer to run `token-builder` if
|
|
14
14
|
missing. Needs a live Figma connection (offer `figma-environment-setup` if not).
|
|
15
|
-
|
|
15
|
+
The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
16
16
|
|
|
17
17
|
**Before scripting any `figma_execute`, read
|
|
18
18
|
`.throughline/references/figma-scripting.md`** — the single-bridge-instance
|
|
@@ -71,8 +71,9 @@ inconsistent output:
|
|
|
71
71
|
- **Sizes** (sm, md, lg). Each size is its **own variant row**, not a state — see
|
|
72
72
|
the layout law below.
|
|
73
73
|
- **States** — **include the full relevant set, don't trim it.** The baseline is
|
|
74
|
-
|
|
75
|
-
wherever they apply (loading,
|
|
74
|
+
the set `.throughline/references/state-baseline.md` owes that component
|
|
75
|
+
(default included); add the conditional states wherever they apply (loading,
|
|
76
|
+
success/error). Decide which conditional
|
|
76
77
|
states a given component can reach, but never drop a state it genuinely has. See
|
|
77
78
|
"State handling" in `.throughline/references/figma-component-standards.md`
|
|
78
79
|
for the per-component checklist.
|
|
@@ -404,9 +405,9 @@ Offer next steps: build the code counterparts and stories
|
|
|
404
405
|
auto-layout grid where **variants are rows and states are columns**: one row per
|
|
405
406
|
variant (each `type`, and each `size`, since size is a variant) stepping through
|
|
406
407
|
states across the columns, size groups stacked vertically (see the standards doc).
|
|
407
|
-
- Never ship a component with only its `default` state — include
|
|
408
|
-
state
|
|
409
|
-
loading/
|
|
408
|
+
- Never ship a component with only its `default` state — include every state
|
|
409
|
+
`.throughline/references/state-baseline.md` owes that component, plus
|
|
410
|
+
`default`, plus any applicable loading/success/error.
|
|
410
411
|
- Never run the component header and the component area together with no division —
|
|
411
412
|
every doc card segments the header from the component area with a divider line or
|
|
412
413
|
a distinct header surface.
|
|
@@ -26,7 +26,9 @@ across levels.
|
|
|
26
26
|
|
|
27
27
|
Read the manifest. This skill needs Figma connected (`figma.connected: true`) for the
|
|
28
28
|
inventory step, and a repo path for the code-surface step (`workspace.localPath`). If
|
|
29
|
-
Figma isn't connected, offer to run `figma-environment-setup` first.
|
|
29
|
+
Figma isn't connected, offer to run `figma-environment-setup` first. Before the
|
|
30
|
+
inventory, do a cheap liveness read to confirm the connection is actually live right
|
|
31
|
+
now; if it fails, offer `figma-environment-setup`. The code-surface
|
|
30
32
|
step works on any repo regardless of `workspace.stage`.
|
|
31
33
|
|
|
32
34
|
## Step 1 — Size the code surface
|
|
@@ -13,8 +13,10 @@ This skill does three foundational jobs, in order:
|
|
|
13
13
|
3. **Connect Cursor to Figma** so later skills can read and write the file,
|
|
14
14
|
then verify the connection works.
|
|
15
15
|
|
|
16
|
-
It is the required first step. Every other Figma
|
|
17
|
-
check and, if it fails, points the user back here.
|
|
16
|
+
It is the required first step. Every other skill that calls Figma itself does a
|
|
17
|
+
cheap liveness check and, if it fails, points the user back here. (The orchestrators
|
|
18
|
+
`component-pipeline` and `retrofit-planner` call Figma only through the skills they
|
|
19
|
+
invoke, and `token-crosswalk-builder` works from the manifest and token files.)
|
|
18
20
|
|
|
19
21
|
## Who you're talking to
|
|
20
22
|
|
|
@@ -227,28 +229,23 @@ Make clear it changes only how much detail they get, never what they can build.
|
|
|
227
229
|
This is set now because it colors every later interaction. The Figma phase
|
|
228
230
|
itself doesn't lean on it much, but the code phase (repo, sync, Storybook) does.
|
|
229
231
|
|
|
230
|
-
## Step 3 —
|
|
232
|
+
## Step 3 — Figma write mechanism
|
|
231
233
|
|
|
232
|
-
|
|
234
|
+
ThroughLine connects to Figma through the **Figma Console MCP**. It can create
|
|
235
|
+
many variables at once efficiently (which saves time and usage), and it can read
|
|
236
|
+
your variables on any Figma plan, including free and Pro. The setup is light:
|
|
237
|
+
you'll run a small helper plugin inside Figma, and can optionally create one
|
|
238
|
+
access token.
|
|
233
239
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
your variables on any Figma plan, including free and Pro. Slightly more setup:
|
|
237
|
-
you'll create one access token and run a small helper plugin inside Figma.
|
|
238
|
-
- **Official Figma plugin (simpler fallback).** Less setup, but more limited —
|
|
239
|
-
some bulk operations are slower, and reading variables through it can be
|
|
240
|
-
restricted on non-Enterprise plans. Fine if the user wants the lightest path.
|
|
241
|
-
|
|
242
|
-
Let the user choose. Record their choice in `figma.mechanism`
|
|
243
|
-
(`"console-mcp"` or `"official-plugin"`). Default to `console-mcp` if they have
|
|
244
|
-
no preference.
|
|
240
|
+
Record `figma.mechanism` as `"console-mcp"`. There is nothing for the user to
|
|
241
|
+
choose.
|
|
245
242
|
|
|
246
243
|
The MCP server configuration ships **bundled with this plugin** (in the plugin's
|
|
247
244
|
`.mcp.json`), so the user does not hand-edit any config files. The one thing the
|
|
248
245
|
bundle can't contain is their personal access token — that's a per-user secret,
|
|
249
246
|
handled in the next step.
|
|
250
247
|
|
|
251
|
-
## Step 4 — Connect to Figma
|
|
248
|
+
## Step 4 — Connect to Figma
|
|
252
249
|
|
|
253
250
|
Walk through these one at a time, confirming each before moving on. Never rush
|
|
254
251
|
the user past a step.
|
|
@@ -261,10 +258,16 @@ browser version of Figma causes connection and token errors that are painful to
|
|
|
261
258
|
debug. If they don't have the desktop app, point them to figma.com/downloads to
|
|
262
259
|
install it first. Desktop app, every time.
|
|
263
260
|
|
|
264
|
-
### 4b. Create a Figma access token
|
|
261
|
+
### 4b. Create a Figma access token (optional)
|
|
262
|
+
|
|
263
|
+
A token is like a password that lets Cursor talk to Figma on your behalf. It's
|
|
264
|
+
**optional**: the desktop bridge does the reading and writing on its own. The
|
|
265
|
+
token only adds the few calls the bridge can't make, such as reading a published
|
|
266
|
+
library or a file's version history. Offer it, and say plainly that they can skip
|
|
267
|
+
it and add it later in **Configure options**. If they skip, record nothing about
|
|
268
|
+
it, leave the field blank, and go straight to 4c. Nothing later in setup needs it.
|
|
265
269
|
|
|
266
|
-
|
|
267
|
-
them through it:
|
|
270
|
+
If they want it, walk them through it:
|
|
268
271
|
|
|
269
272
|
1. In a browser, go to figma.com and sign in (same account as the desktop app).
|
|
270
273
|
2. Click your account menu, then Settings.
|
|
@@ -313,14 +316,6 @@ Cursor and Figma are linked for this session."
|
|
|
313
316
|
If the config was just added (token placed for the first time), the MCP client
|
|
314
317
|
may need a restart to load it. Tell them how, simply, and confirm they're back.
|
|
315
318
|
|
|
316
|
-
## Step 4 (alt) — Connect to Figma (official plugin path)
|
|
317
|
-
|
|
318
|
-
If the user chose the official plugin, the flow is lighter: install the official
|
|
319
|
-
Figma plugin for Cursor per its setup, ensure the desktop app is open with
|
|
320
|
-
the file, and authenticate through the desktop app (again: desktop, not
|
|
321
|
-
browser). The same secret-handling rule applies — any token goes into config by
|
|
322
|
-
the user, never through the chat.
|
|
323
|
-
|
|
324
319
|
## Step 5 — Capture the file key
|
|
325
320
|
|
|
326
321
|
Ask the user for the URL of the Figma file they want to build the design system
|
|
@@ -388,7 +383,7 @@ tokens or pairing here is useless. Diagnose in order:
|
|
|
388
383
|
|
|
389
384
|
**B. The server started but the connection failed.** If there's no launch error
|
|
390
385
|
and the server is clearly running, diagnose gently and in order: Is the desktop
|
|
391
|
-
app open with the file?
|
|
386
|
+
app open with the file? If they added a token, was it placed correctly (have them re-check,
|
|
392
387
|
without showing you the value)? Is the bridge plugin running and paired? Did the
|
|
393
388
|
client need a restart? Walk them back through the relevant sub-step.
|
|
394
389
|
|
|
@@ -404,7 +399,8 @@ neutral placeholder built against nothing.
|
|
|
404
399
|
|
|
405
400
|
- Create one trivial throwaway node (e.g. a small frame or text node named
|
|
406
401
|
`__throughline write test`) on any page.
|
|
407
|
-
- Confirm it landed with a quick read or `figma_capture_screenshot`.
|
|
402
|
+
- Confirm it landed with a quick read or `figma_capture_screenshot`. Neither needs
|
|
403
|
+
a token.
|
|
408
404
|
- **Delete it.** If both the write and the delete succeed, writes are proven.
|
|
409
405
|
|
|
410
406
|
Leave `figma.coverPageBuilt` = `false` here — that flag is set later, when the
|
|
@@ -449,7 +445,9 @@ restore point:
|
|
|
449
445
|
`figma_get_file_versions`) or have the user name a Figma version so there's a known-good
|
|
450
446
|
point to restore to. The plugin can read versions; it does not auto-create named
|
|
451
447
|
versions, so if none exists, ask the user to add one ("File → Save to version
|
|
452
|
-
history") and record that you did.
|
|
448
|
+
history") and record that you did. If the user skipped the access token and
|
|
449
|
+
the version read fails, say in one sentence that you can't read version history
|
|
450
|
+
without it, and have them name the version themselves.
|
|
453
451
|
- **Token export** — if the repo already emits tokens, snapshot the current generated
|
|
454
452
|
output (git is the natural baseline once `workspace.stage` is `local-git`+).
|
|
455
453
|
|
|
@@ -20,6 +20,10 @@ This skill needs:
|
|
|
20
20
|
- Figma components (`components.built`) with their slot contracts, so stories
|
|
21
21
|
reflect the real component APIs.
|
|
22
22
|
|
|
23
|
+
The Figma-touching steps (Code Connect in Step 5, the doc-card write-back in Step 6)
|
|
24
|
+
also need a live Figma connection — do a cheap liveness read before the first Figma
|
|
25
|
+
call; if it fails, offer `figma-environment-setup`.
|
|
26
|
+
|
|
23
27
|
Strongly recommend `github` stage so Chromatic's CI integration works; it can be
|
|
24
28
|
set up locally first and wired to CI when the remote exists.
|
|
25
29
|
|
|
@@ -39,7 +39,7 @@ now. If it isn't, say so plainly and offer to run the `figma-environment-setup`
|
|
|
39
39
|
skill first: "Figma isn't connected yet — want me to set that up? It's a
|
|
40
40
|
one-time thing." Don't proceed until Figma is reachable.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
43
43
|
With Console MCP, prefer `figma_execute` to create variables in scripted loops
|
|
44
44
|
rather than one tool call per variable — this is dramatically more
|
|
45
45
|
token-efficient for the potentially hundreds of primitives across modes.
|
|
@@ -28,7 +28,7 @@ a sensible companion step — the sheet and the synced code both reflect Figma.)
|
|
|
28
28
|
|
|
29
29
|
Needs tokens in Figma (`tokens.semanticBuilt` true) and a live Figma connection.
|
|
30
30
|
If tokens don't exist, offer to run `token-builder`. If Figma isn't connected,
|
|
31
|
-
offer `figma-environment-setup`.
|
|
31
|
+
offer `figma-environment-setup`. The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
32
32
|
|
|
33
33
|
**Before scripting any `figma_execute`, read
|
|
34
34
|
`.throughline/references/figma-scripting.md`.** This sheet builds large
|
|
@@ -70,8 +70,8 @@ it.
|
|
|
70
70
|
|
|
71
71
|
## Step 2 — Extract Figma variables to DTCG JSON
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
The plugin talks to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it. Using Console MCP's
|
|
74
|
+
full-design-system extraction (which works on any Figma plan), read
|
|
75
75
|
**every variable collection** (there are now several per tier, e.g.
|
|
76
76
|
`_Color/Primitive`, `Spacing/Primitive`, `Color/Semantic`, …) and normalize them
|
|
77
77
|
into **DTCG-format JSON** (`$value`, `$type`, with semantic tokens expressed as
|
|
@@ -144,7 +144,10 @@ fit in the adapter output.
|
|
|
144
144
|
|
|
145
145
|
## Step 3 — Set up Style Dictionary + adapters
|
|
146
146
|
|
|
147
|
-
Install
|
|
147
|
+
Install Style Dictionary pinned to an exact version with
|
|
148
|
+
`pnpm add -D style-dictionary@4.4.0` (or the project's package manager) in
|
|
149
|
+
`packages/tokens/`; the generated config is verified against 4.4.0, and a newer
|
|
150
|
+
release can rename or add stock transforms. Then configure it. For each chosen
|
|
148
151
|
platform, apply its adapter preset (per `.throughline/references/sync-adapters.md`):
|
|
149
152
|
register the platform, transform group, format, and `outputReferences`
|
|
150
153
|
(true for web → preserves the semantic→primitive cascade; false for native →
|
|
@@ -8,7 +8,7 @@ translate cleanly to code later.
|
|
|
8
8
|
|
|
9
9
|
Needs tokens (`tokens.semanticBuilt` true) — offer to run `token-builder` if
|
|
10
10
|
missing. Needs a live Figma connection (offer `figma-environment-setup` if not).
|
|
11
|
-
|
|
11
|
+
The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
12
12
|
|
|
13
13
|
**Before scripting any `figma_execute`, read
|
|
14
14
|
`.throughline/references/figma-scripting.md`** — the single-bridge-instance
|
|
@@ -67,8 +67,9 @@ inconsistent output:
|
|
|
67
67
|
- **Sizes** (sm, md, lg). Each size is its **own variant row**, not a state — see
|
|
68
68
|
the layout law below.
|
|
69
69
|
- **States** — **include the full relevant set, don't trim it.** The baseline is
|
|
70
|
-
|
|
71
|
-
wherever they apply (loading,
|
|
70
|
+
the set `.throughline/references/state-baseline.md` owes that component
|
|
71
|
+
(default included); add the conditional states wherever they apply (loading,
|
|
72
|
+
success/error). Decide which conditional
|
|
72
73
|
states a given component can reach, but never drop a state it genuinely has. See
|
|
73
74
|
"State handling" in `.throughline/references/figma-component-standards.md`
|
|
74
75
|
for the per-component checklist.
|
|
@@ -400,9 +401,9 @@ Offer next steps: build the code counterparts and stories
|
|
|
400
401
|
auto-layout grid where **variants are rows and states are columns**: one row per
|
|
401
402
|
variant (each `type`, and each `size`, since size is a variant) stepping through
|
|
402
403
|
states across the columns, size groups stacked vertically (see the standards doc).
|
|
403
|
-
- Never ship a component with only its `default` state — include
|
|
404
|
-
state
|
|
405
|
-
loading/
|
|
404
|
+
- Never ship a component with only its `default` state — include every state
|
|
405
|
+
`.throughline/references/state-baseline.md` owes that component, plus
|
|
406
|
+
`default`, plus any applicable loading/success/error.
|
|
406
407
|
- Never run the component header and the component area together with no division —
|
|
407
408
|
every doc card segments the header from the component area with a divider line or
|
|
408
409
|
a distinct header surface.
|
|
@@ -22,7 +22,9 @@ across levels.
|
|
|
22
22
|
|
|
23
23
|
Read the manifest. This skill needs Figma connected (`figma.connected: true`) for the
|
|
24
24
|
inventory step, and a repo path for the code-surface step (`workspace.localPath`). If
|
|
25
|
-
Figma isn't connected, offer to run `figma-environment-setup` first.
|
|
25
|
+
Figma isn't connected, offer to run `figma-environment-setup` first. Before the
|
|
26
|
+
inventory, do a cheap liveness read to confirm the connection is actually live right
|
|
27
|
+
now; if it fails, offer `figma-environment-setup`. The code-surface
|
|
26
28
|
step works on any repo regardless of `workspace.stage`.
|
|
27
29
|
|
|
28
30
|
## Step 1 — Size the code surface
|
|
@@ -9,8 +9,10 @@ This skill does three foundational jobs, in order:
|
|
|
9
9
|
3. **Connect the agent to Figma** so later skills can read and write the file,
|
|
10
10
|
then verify the connection works.
|
|
11
11
|
|
|
12
|
-
It is the required first step. Every other Figma
|
|
13
|
-
check and, if it fails, points the user back here.
|
|
12
|
+
It is the required first step. Every other skill that calls Figma itself does a
|
|
13
|
+
cheap liveness check and, if it fails, points the user back here. (The orchestrators
|
|
14
|
+
`component-pipeline` and `retrofit-planner` call Figma only through the skills they
|
|
15
|
+
invoke, and `token-crosswalk-builder` works from the manifest and token files.)
|
|
14
16
|
|
|
15
17
|
## Who you're talking to
|
|
16
18
|
|
|
@@ -223,28 +225,23 @@ Make clear it changes only how much detail they get, never what they can build.
|
|
|
223
225
|
This is set now because it colors every later interaction. The Figma phase
|
|
224
226
|
itself doesn't lean on it much, but the code phase (repo, sync, Storybook) does.
|
|
225
227
|
|
|
226
|
-
## Step 3 —
|
|
228
|
+
## Step 3 — Figma write mechanism
|
|
227
229
|
|
|
228
|
-
|
|
230
|
+
ThroughLine connects to Figma through the **Figma Console MCP**. It can create
|
|
231
|
+
many variables at once efficiently (which saves time and usage), and it can read
|
|
232
|
+
your variables on any Figma plan, including free and Pro. The setup is light:
|
|
233
|
+
you'll run a small helper plugin inside Figma, and can optionally create one
|
|
234
|
+
access token.
|
|
229
235
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
your variables on any Figma plan, including free and Pro. Slightly more setup:
|
|
233
|
-
you'll create one access token and run a small helper plugin inside Figma.
|
|
234
|
-
- **Official Figma plugin (simpler fallback).** Less setup, but more limited —
|
|
235
|
-
some bulk operations are slower, and reading variables through it can be
|
|
236
|
-
restricted on non-Enterprise plans. Fine if the user wants the lightest path.
|
|
237
|
-
|
|
238
|
-
Let the user choose. Record their choice in `figma.mechanism`
|
|
239
|
-
(`"console-mcp"` or `"official-plugin"`). Default to `console-mcp` if they have
|
|
240
|
-
no preference.
|
|
236
|
+
Record `figma.mechanism` as `"console-mcp"`. There is nothing for the user to
|
|
237
|
+
choose.
|
|
241
238
|
|
|
242
239
|
The MCP server configuration ships **bundled with this plugin** (in the plugin's
|
|
243
240
|
`.mcp.json`), so the user does not hand-edit any config files. The one thing the
|
|
244
241
|
bundle can't contain is their personal access token — that's a per-user secret,
|
|
245
242
|
handled in the next step.
|
|
246
243
|
|
|
247
|
-
## Step 4 — Connect to Figma
|
|
244
|
+
## Step 4 — Connect to Figma
|
|
248
245
|
|
|
249
246
|
Walk through these one at a time, confirming each before moving on. Never rush
|
|
250
247
|
the user past a step.
|
|
@@ -257,10 +254,16 @@ browser version of Figma causes connection and token errors that are painful to
|
|
|
257
254
|
debug. If they don't have the desktop app, point them to figma.com/downloads to
|
|
258
255
|
install it first. Desktop app, every time.
|
|
259
256
|
|
|
260
|
-
### 4b. Create a Figma access token
|
|
257
|
+
### 4b. Create a Figma access token (optional)
|
|
258
|
+
|
|
259
|
+
A token is like a password that lets the agent talk to Figma on your behalf. It's
|
|
260
|
+
**optional**: the desktop bridge does the reading and writing on its own. The
|
|
261
|
+
token only adds the few calls the bridge can't make, such as reading a published
|
|
262
|
+
library or a file's version history. Offer it, and say plainly that they can skip
|
|
263
|
+
it and add it later in **Configure options**. If they skip, record nothing about
|
|
264
|
+
it, leave the field blank, and go straight to 4c. Nothing later in setup needs it.
|
|
261
265
|
|
|
262
|
-
|
|
263
|
-
them through it:
|
|
266
|
+
If they want it, walk them through it:
|
|
264
267
|
|
|
265
268
|
1. In a browser, go to figma.com and sign in (same account as the desktop app).
|
|
266
269
|
2. Click your account menu, then Settings.
|
|
@@ -309,14 +312,6 @@ the agent and Figma are linked for this session."
|
|
|
309
312
|
If the config was just added (token placed for the first time), the MCP client
|
|
310
313
|
may need a restart to load it. Tell them how, simply, and confirm they're back.
|
|
311
314
|
|
|
312
|
-
## Step 4 (alt) — Connect to Figma (official plugin path)
|
|
313
|
-
|
|
314
|
-
If the user chose the official plugin, the flow is lighter: install the official
|
|
315
|
-
Figma plugin for your coding agent per its setup, ensure the desktop app is open with
|
|
316
|
-
the file, and authenticate through the desktop app (again: desktop, not
|
|
317
|
-
browser). The same secret-handling rule applies — any token goes into config by
|
|
318
|
-
the user, never through the chat.
|
|
319
|
-
|
|
320
315
|
## Step 5 — Capture the file key
|
|
321
316
|
|
|
322
317
|
Ask the user for the URL of the Figma file they want to build the design system
|
|
@@ -384,7 +379,7 @@ tokens or pairing here is useless. Diagnose in order:
|
|
|
384
379
|
|
|
385
380
|
**B. The server started but the connection failed.** If there's no launch error
|
|
386
381
|
and the server is clearly running, diagnose gently and in order: Is the desktop
|
|
387
|
-
app open with the file?
|
|
382
|
+
app open with the file? If they added a token, was it placed correctly (have them re-check,
|
|
388
383
|
without showing you the value)? Is the bridge plugin running and paired? Did the
|
|
389
384
|
client need a restart? Walk them back through the relevant sub-step.
|
|
390
385
|
|
|
@@ -400,7 +395,8 @@ neutral placeholder built against nothing.
|
|
|
400
395
|
|
|
401
396
|
- Create one trivial throwaway node (e.g. a small frame or text node named
|
|
402
397
|
`__throughline write test`) on any page.
|
|
403
|
-
- Confirm it landed with a quick read or `figma_capture_screenshot`.
|
|
398
|
+
- Confirm it landed with a quick read or `figma_capture_screenshot`. Neither needs
|
|
399
|
+
a token.
|
|
404
400
|
- **Delete it.** If both the write and the delete succeed, writes are proven.
|
|
405
401
|
|
|
406
402
|
Leave `figma.coverPageBuilt` = `false` here — that flag is set later, when the
|
|
@@ -445,7 +441,9 @@ restore point:
|
|
|
445
441
|
`figma_get_file_versions`) or have the user name a Figma version so there's a known-good
|
|
446
442
|
point to restore to. The plugin can read versions; it does not auto-create named
|
|
447
443
|
versions, so if none exists, ask the user to add one ("File → Save to version
|
|
448
|
-
history") and record that you did.
|
|
444
|
+
history") and record that you did. If the user skipped the access token and
|
|
445
|
+
the version read fails, say in one sentence that you can't read version history
|
|
446
|
+
without it, and have them name the version themselves.
|
|
449
447
|
- **Token export** — if the repo already emits tokens, snapshot the current generated
|
|
450
448
|
output (git is the natural baseline once `workspace.stage` is `local-git`+).
|
|
451
449
|
|
|
@@ -16,6 +16,10 @@ This skill needs:
|
|
|
16
16
|
- Figma components (`components.built`) with their slot contracts, so stories
|
|
17
17
|
reflect the real component APIs.
|
|
18
18
|
|
|
19
|
+
The Figma-touching steps (Code Connect in Step 5, the doc-card write-back in Step 6)
|
|
20
|
+
also need a live Figma connection — do a cheap liveness read before the first Figma
|
|
21
|
+
call; if it fails, offer `figma-environment-setup`.
|
|
22
|
+
|
|
19
23
|
Strongly recommend `github` stage so Chromatic's CI integration works; it can be
|
|
20
24
|
set up locally first and wired to CI when the remote exists.
|
|
21
25
|
|
|
@@ -35,7 +35,7 @@ now. If it isn't, say so plainly and offer to run the `figma-environment-setup`
|
|
|
35
35
|
skill first: "Figma isn't connected yet — want me to set that up? It's a
|
|
36
36
|
one-time thing." Don't proceed until Figma is reachable.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
39
39
|
With Console MCP, prefer `figma_execute` to create variables in scripted loops
|
|
40
40
|
rather than one tool call per variable — this is dramatically more
|
|
41
41
|
token-efficient for the potentially hundreds of primitives across modes.
|
|
@@ -24,7 +24,7 @@ a sensible companion step — the sheet and the synced code both reflect Figma.)
|
|
|
24
24
|
|
|
25
25
|
Needs tokens in Figma (`tokens.semanticBuilt` true) and a live Figma connection.
|
|
26
26
|
If tokens don't exist, offer to run `token-builder`. If Figma isn't connected,
|
|
27
|
-
offer `figma-environment-setup`.
|
|
27
|
+
offer `figma-environment-setup`. The plugin writes to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it.
|
|
28
28
|
|
|
29
29
|
**Before scripting any `figma_execute`, read
|
|
30
30
|
`.throughline/references/figma-scripting.md`.** This sheet builds large
|
|
@@ -66,8 +66,8 @@ it.
|
|
|
66
66
|
|
|
67
67
|
## Step 2 — Extract Figma variables to DTCG JSON
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
The plugin talks to Figma through Console MCP. If `figma.mechanism` holds anything other than `console-mcp`, say that value is no longer supported and offer `figma-environment-setup` to reset it. Using Console MCP's
|
|
70
|
+
full-design-system extraction (which works on any Figma plan), read
|
|
71
71
|
**every variable collection** (there are now several per tier, e.g.
|
|
72
72
|
`_Color/Primitive`, `Spacing/Primitive`, `Color/Semantic`, …) and normalize them
|
|
73
73
|
into **DTCG-format JSON** (`$value`, `$type`, with semantic tokens expressed as
|
|
@@ -140,7 +140,10 @@ fit in the adapter output.
|
|
|
140
140
|
|
|
141
141
|
## Step 3 — Set up Style Dictionary + adapters
|
|
142
142
|
|
|
143
|
-
Install
|
|
143
|
+
Install Style Dictionary pinned to an exact version with
|
|
144
|
+
`pnpm add -D style-dictionary@4.4.0` (or the project's package manager) in
|
|
145
|
+
`packages/tokens/`; the generated config is verified against 4.4.0, and a newer
|
|
146
|
+
release can rename or add stock transforms. Then configure it. For each chosen
|
|
144
147
|
platform, apply its adapter preset (per `.throughline/references/sync-adapters.md`):
|
|
145
148
|
register the platform, transform group, format, and `outputReferences`
|
|
146
149
|
(true for web → preserves the semantic→primitive cascade; false for native →
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radicool/throughline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Build a complete design system end to end — author in Figma, sync tokens to code, generate Storybook. Usable from Claude Code, Cursor, Codex, or any AGENTS.md agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -123,19 +123,17 @@ run, and it mirrors how the states/types map to code props.
|
|
|
123
123
|
|
|
124
124
|
- **Always include every relevant state for the component — completeness is the
|
|
125
125
|
default, not a judgment call.** A component's `state` axis must enumerate its full
|
|
126
|
-
applicable interaction surface, not just `default`. The
|
|
127
|
-
|
|
128
|
-
states whenever they apply to that component: **loading** (anything
|
|
129
|
-
async work — buttons, submit inputs), **selected**
|
|
130
|
-
list/menu items
|
|
131
|
-
async-result buttons). Decide *which* conditional
|
|
132
|
-
state that does apply to keep the matrix small.
|
|
133
|
-
- **
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
- **Checkbox / radio / toggle / chip:** default, hover, focus, active, disabled —
|
|
138
|
-
plus selected (and indeterminate where it applies).
|
|
126
|
+
applicable interaction surface, not just `default`. The states a component owes
|
|
127
|
+
come from the table in `references/state-baseline.md` (see below); add the
|
|
128
|
+
**conditional** states whenever they apply to that component: **loading** (anything
|
|
129
|
+
that triggers async work — buttons, submit inputs), **selected** for components the
|
|
130
|
+
table does not cover (segmented controls, list/menu items), and **success / error**
|
|
131
|
+
(validated inputs, form fields, async-result buttons). Decide *which* conditional
|
|
132
|
+
states apply, but never drop a state that does apply to keep the matrix small.
|
|
133
|
+
- **Which components owe which states is one table**, shared with the CLI's
|
|
134
|
+
`state-incomplete` check: `references/state-baseline.md`, generated from
|
|
135
|
+
`scripts/lib/component-states.mjs`. Add loading, success / error, and
|
|
136
|
+
indeterminate where they apply; those are not in the table.
|
|
139
137
|
- Keep these on a `state` variant axis where they change appearance meaningfully;
|
|
140
138
|
each becomes a column in the set per "Component set arrangement".
|
|
141
139
|
- Distinguish *component states* (part of the component's definition) from
|
|
@@ -557,8 +555,8 @@ For each generated artboard / doc card / icon grid, read the nodes back (via
|
|
|
557
555
|
a header container whose surface fill differs from the component area (both
|
|
558
556
|
token-bound). A card with no header/component segmentation is a fail.
|
|
559
557
|
8. **States complete** — each component set's `state` axis includes every relevant
|
|
560
|
-
state for that component (
|
|
561
|
-
|
|
558
|
+
state for that component (every state the table in `references/state-baseline.md`
|
|
559
|
+
owes it, plus default, plus the applicable loading/success/error), with variants (incl. each
|
|
562
560
|
size) as rows and states as columns. A set shipping only `default` is a fail.
|
|
563
561
|
9. **Focus state matches the library idiom** — the focus state is built as
|
|
564
562
|
`project.uiFramework`'s real pattern (see "State handling"), not a house-style
|
|
@@ -581,10 +579,10 @@ For each generated artboard / doc card / icon grid, read the nodes back (via
|
|
|
581
579
|
error: fix and re-audit like any other item. If the ring is still missing
|
|
582
580
|
when the pass budget below runs out, **stop the build** rather than hand off.
|
|
583
581
|
- **Scope.** It runs only for a component whose name folds (lowercase,
|
|
584
|
-
punctuation stripped) to a
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
`state-incomplete`
|
|
582
|
+
punctuation stripped) to a name in the table in `references/state-baseline.md`
|
|
583
|
+
(not its exempt list), the same
|
|
584
|
+
exact-name match as the state check and the same list the CLI's
|
|
585
|
+
`state-incomplete` reads. It also
|
|
588
586
|
needs `project.uiFramework` to be exactly `shadcn`, `tailwind`, `mui`,
|
|
589
587
|
`vanilla-css` or `vanilla`, or absent or `null`. Anything else (`ios-swift`,
|
|
590
588
|
a tier-2 library, any other value) is skipped, and the skip is said.
|
|
@@ -68,7 +68,8 @@ wrong document.
|
|
|
68
68
|
Run this before the first write, and again right before any replace — the
|
|
69
69
|
build-verify-then-replace finalize deletes the existing node, so it is the write
|
|
70
70
|
that destroys work in the wrong file. The `figma-executor` agent runs it as step 1
|
|
71
|
-
of its contract
|
|
71
|
+
of its contract; the `architect` and `reviewer` agents, which read Figma but cannot
|
|
72
|
+
navigate, run the compare and return `BLOCKED` on a mismatch. **When a skill builds inline instead** (a host with no subagent
|
|
72
73
|
dispatch, or a write-back the skill does itself), nothing runs it for you: the
|
|
73
74
|
skill has to.
|
|
74
75
|
|
|
@@ -162,10 +162,12 @@ bailing or running silently.
|
|
|
162
162
|
- `syncLayer` — `style-dictionary.config.js` or `*.style-dictionary.js` files present at the project root or within immediate subdirectories
|
|
163
163
|
|
|
164
164
|
### `figma`
|
|
165
|
-
- `mechanism` — which write mechanism is active.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
- `mechanism` — which write mechanism is active. `"console-mcp"` is the only
|
|
166
|
+
supported value. Set by skill 0. The adapter layer in every Figma skill reads
|
|
167
|
+
this to know which tool names to use. A manifest written by an earlier version
|
|
168
|
+
may hold `"official-plugin"`; that value is unsupported (no agent or bundled
|
|
169
|
+
MCP server backs it), and re-running `figma-environment-setup` resets it to
|
|
170
|
+
`"console-mcp"`.
|
|
169
171
|
- `fileKey` — the Figma file key the system writes into (extracted from the
|
|
170
172
|
file URL). Lets skills target the right file without re-asking.
|
|
171
173
|
- `connected` — whether setup completed successfully at least once. Note this is
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- GENERATED by scripts/build-state-baseline.mjs from scripts/lib/component-states.mjs. Do not edit; run: node scripts/build-state-baseline.mjs -->
|
|
2
|
+
# State baseline
|
|
3
|
+
|
|
4
|
+
Which interaction states a component owes. This is the one list both checks read:
|
|
5
|
+
`verify:check` (`state-incomplete`) reads it from `scripts/lib/component-states.mjs`,
|
|
6
|
+
and `figma-executor` (its state check and the `focus-indicator` check) reads
|
|
7
|
+
this table. CI fails when the table is stale.
|
|
8
|
+
|
|
9
|
+
A component name matches a row when it folds (lowercase, punctuation and spaces
|
|
10
|
+
stripped) to exactly one of that row's names. `Text Field` folds to `textfield`.
|
|
11
|
+
Matching is exact on purpose: `IconButton` matches nothing, and a component that
|
|
12
|
+
matches nothing owes no states.
|
|
13
|
+
|
|
14
|
+
| Archetype | Names | States owed |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| button | `button` | `hover`, `focus`, `active`, `disabled` |
|
|
17
|
+
| input | `input`, `textfield`, `textinput` | `hover`, `focus`, `disabled` |
|
|
18
|
+
| choice | `checkbox`, `radio`, `toggle`, `switch`, `chip` | `hover`, `focus`, `active`, `selected`, `disabled` |
|
|
19
|
+
|
|
20
|
+
Every state in the table must be a value on the component's `state` axis. On the
|
|
21
|
+
Figma side the matrix also carries `default` as the resting state; the CLI does not
|
|
22
|
+
count it. `checked` counts as `selected`, and `on` counts as `selected`, since that is what shadcn and Radix call it. Conditional states (`loading`, `error`, `success`) are not in the table:
|
|
23
|
+
whether one applies is a design judgment.
|
|
24
|
+
|
|
25
|
+
Names that fold to `card`, `modal`, `dialog`, `badge`, `tag` owe none, so neither check asserts states on them.
|
package/scripts/README.md
CHANGED
|
@@ -9,7 +9,7 @@ tested here; copied verbatim by `token-crosswalk-builder` into the user's
|
|
|
9
9
|
| --- | --- | --- |
|
|
10
10
|
| `validate-crosswalk.mjs` | Resolve every `newToken` against the DTCG token source; assert resolved value == `newValue`, N/N. The CI gate. | `tokens:validate` |
|
|
11
11
|
| `build-reverse-index.mjs` | Emit a `codeToken -> newToken` map from the crosswalk to semi-automate SCSS/Tailwind swaps. | `tokens:reverse-index` |
|
|
12
|
-
| `guard-token-removal.mjs` | Grep `.
|
|
12
|
+
| `guard-token-removal.mjs` | Grep every source file the shared walker reads — `.scss`, `.css`, `.tsx?`, `.jsx?`, `.html` and the rest of `SOURCE_EXT` (minus generated + tests) for about-to-be-deleted symbols; blocks cleanup until zero references remain. | run during the cleanup phase |
|
|
13
13
|
| `validate-token-output.mjs` | Assert generated token output — Swift, Kotlin, or web CSS custom properties — matches its DTCG source: authored-unit fidelity and no mode collisions everywhere, plus no leaked CSS syntax and no bare unit literals in Swift and Kotlin. Fails when no emitted symbol matches a source token, and reports match rate, unparsed lines, and unemitted tokens on every run. | `tokens:validate-output` |
|
|
14
14
|
| `validate-adherence.mjs` | Assert the code *consuming* a design system still adheres to it: every referenced component exists, every literal variant value is one the system declares, and no colour, spacing, radius or type literal duplicates a token that already holds that value. `unnamed-control` fails a Button or IconButton (or a component whose doc record says `archetype: "button"`) with no accessible name: no text, and no `aria-label`, `aria-labelledby` or `title` on it or anything inside it. `colour-only-status` fails a Badge or Tag (or a record with `archetype: "badge"`) the same way, so its status isn't carried by colour alone. Both read script files only (`.js`, `.jsx`, `.ts`, `.tsx`, `.mjs`, `.cjs`), abstain rather than guess on anything they can't read, print how many elements they checked and abstained on, and can be switched off with `--skip`. Fails when an enabled rule had nothing to check, so a green run always means something was verified. | `adherence:check` |
|
|
15
15
|
| `lib/source-scan.mjs` | Shared source-tree walker (`walk`, `DEFAULT_EXCLUDES`, `SOURCE_EXT`) plus `normalizeName`, the display-name-to-code-identifier fold. Every gate that scans a consumer's repo reads it from here rather than carrying its own copy. | copied alongside `guard-token-removal.mjs`, and in the documentation set for `validate-adherence.mjs` and `verify-check.mjs` |
|
|
@@ -29,6 +29,7 @@ tested here; copied verbatim by `token-crosswalk-builder` into the user's
|
|
|
29
29
|
| `lib/component-states.mjs` | The archetype table and resolver behind `state-incomplete` — which baseline interaction states each kind of component owes, and when to abstain. `validate-adherence.mjs` also uses it to find Badges. | copied alongside `verify-check.mjs` and `validate-adherence.mjs` |
|
|
30
30
|
| `lib/contrast.mjs` | The WCAG contrast maths and the semantic colour pairs behind `color-contrast` — which text role must clear which surface, in every mode, and where the rule deliberately abstains. | copied alongside `verify-check.mjs` |
|
|
31
31
|
| `build-doc-card-builder.mjs` | Generate `references/doc-card-builder.md` from the planner + the Figma renderer template (`lib/doc-card-render.figma.js`). `--check` gates CI. | plugin-internal (not installed) |
|
|
32
|
+
| `build-state-baseline.mjs` | Generate `references/state-baseline.md`, the table of which components owe which interaction states, from `lib/component-states.mjs`. The Figma executor reads the table; `verify:check` reads the module. `--check` gates CI. | plugin-internal (not installed) |
|
|
32
33
|
| `build-native-adapter-config.mjs` | Generate `references/native-adapter-config.md` by slicing `lib/sd-native.mjs` on its `@doc-section` markers and interleaving each fragment under its prose. Fails when module code falls outside every section, so the doc cannot silently ship incomplete. `--check` gates CI. | plugin-internal (not installed) |
|
|
33
34
|
|
|
34
35
|
**Documentation scripts — install as a set.** Copying these files without
|
|
@@ -245,7 +246,7 @@ the committed `adapters/<target>/` tree plus the runtime payload
|
|
|
245
246
|
(`references/` + `scripts/`, minus `scripts/adapters/`) into `.throughline/`,
|
|
246
247
|
rewriting `${CLAUDE_PLUGIN_ROOT}` → `.throughline`:
|
|
247
248
|
|
|
248
|
-
npx @radicool/throughline@0.
|
|
249
|
+
npx @radicool/throughline@0.22.0 init --target=cursor|codex|generic
|
|
249
250
|
|
|
250
251
|
See `scripts/install.mjs` (pure core + CLI + `install.test.mjs`).
|
|
251
252
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Generates references/state-baseline.md from scripts/lib/component-states.mjs.
|
|
2
|
+
// The table is the one list of which components owe which interaction states:
|
|
3
|
+
// `verify:check` reads the module, and `figma-executor` (which cannot import
|
|
4
|
+
// JavaScript) reads this rendering. Mirrors build-native-adapter-config.mjs:
|
|
5
|
+
// run bare to write, --check to gate CI. Zero dependencies.
|
|
6
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { join, dirname } from 'node:path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
import { ARCHETYPES, BASELINE_STATES, NAME_SYNONYMS, STATE_SYNONYMS } from './lib/component-states.mjs';
|
|
10
|
+
|
|
11
|
+
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
12
|
+
export const OUT = join(REPO_ROOT, 'references', 'state-baseline.md');
|
|
13
|
+
|
|
14
|
+
export function render(states = BASELINE_STATES, synonyms = NAME_SYNONYMS, archetypes = ARCHETYPES, stateSynonyms = STATE_SYNONYMS) {
|
|
15
|
+
const aliases = Object.keys(stateSynonyms).map((s) => `\`${s}\` counts as \`${stateSynonyms[s]}\``).join(', and ');
|
|
16
|
+
const owing = archetypes.filter((a) => (states[a] ?? []).length > 0);
|
|
17
|
+
const rows = owing.map((a) => {
|
|
18
|
+
const names = Object.keys(synonyms).filter((n) => synonyms[n] === a);
|
|
19
|
+
return `| ${a} | ${names.map((n) => `\`${n}\``).join(', ')} | ${states[a].map((s) => `\`${s}\``).join(', ')} |`;
|
|
20
|
+
});
|
|
21
|
+
const exempt = archetypes.filter((a) => !owing.includes(a) && a !== 'other');
|
|
22
|
+
const exemptNames = exempt.flatMap((a) => Object.keys(synonyms).filter((n) => synonyms[n] === a));
|
|
23
|
+
return `<!-- GENERATED by scripts/build-state-baseline.mjs from scripts/lib/component-states.mjs. Do not edit; run: node scripts/build-state-baseline.mjs -->
|
|
24
|
+
# State baseline
|
|
25
|
+
|
|
26
|
+
Which interaction states a component owes. This is the one list both checks read:
|
|
27
|
+
\`verify:check\` (\`state-incomplete\`) reads it from \`scripts/lib/component-states.mjs\`,
|
|
28
|
+
and \`figma-executor\` (its state check and the \`focus-indicator\` check) reads
|
|
29
|
+
this table. CI fails when the table is stale.
|
|
30
|
+
|
|
31
|
+
A component name matches a row when it folds (lowercase, punctuation and spaces
|
|
32
|
+
stripped) to exactly one of that row's names. \`Text Field\` folds to \`textfield\`.
|
|
33
|
+
Matching is exact on purpose: \`IconButton\` matches nothing, and a component that
|
|
34
|
+
matches nothing owes no states.
|
|
35
|
+
|
|
36
|
+
| Archetype | Names | States owed |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
${rows.join('\n')}
|
|
39
|
+
|
|
40
|
+
Every state in the table must be a value on the component's \`state\` axis. On the
|
|
41
|
+
Figma side the matrix also carries \`default\` as the resting state; the CLI does not
|
|
42
|
+
count it. ${aliases}, since that is what shadcn and Radix call it. Conditional states (\`loading\`, \`error\`, \`success\`) are not in the table:
|
|
43
|
+
whether one applies is a design judgment.
|
|
44
|
+
|
|
45
|
+
Names that fold to ${exemptNames.map((n) => `\`${n}\``).join(', ')} owe none, so neither check asserts states on them.
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function main() {
|
|
50
|
+
const check = process.argv.includes('--check');
|
|
51
|
+
const rendered = render();
|
|
52
|
+
if (!check) {
|
|
53
|
+
writeFileSync(OUT, rendered);
|
|
54
|
+
console.log(`wrote ${OUT}`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const current = readFileSync(OUT, 'utf8');
|
|
58
|
+
if (current === rendered) {
|
|
59
|
+
console.log('references/state-baseline.md is up to date');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.error(
|
|
63
|
+
'references/state-baseline.md is stale.\n' +
|
|
64
|
+
'Run: node scripts/build-state-baseline.mjs',
|
|
65
|
+
);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) main();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Repo-wide token-removal guard: grep
|
|
1
|
+
// Repo-wide token-removal guard: grep source files (minus generated + tests) for
|
|
2
2
|
// references to about-to-be-deleted symbols. Zero dependencies.
|
|
3
3
|
//
|
|
4
4
|
// Cleanup must not proceed until this returns zero references — deleted Tailwind
|
|
@@ -33,9 +33,9 @@ export function scanFile(path, symbols) {
|
|
|
33
33
|
|
|
34
34
|
export function guard(root, symbols, excludes = DEFAULT_EXCLUDES) {
|
|
35
35
|
const findings = [];
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
for (const file of walk(root, { excludes
|
|
36
|
+
// A token can be referenced from a stylesheet or markup as easily as from
|
|
37
|
+
// TypeScript, so this reads every file type the shared walker defaults to.
|
|
38
|
+
for (const file of walk(root, { excludes })) {
|
|
39
39
|
for (const hit of scanFile(file, symbols)) {
|
|
40
40
|
findings.push({ file: relative(root, file), ...hit });
|
|
41
41
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
// The archetype baseline for `state-incomplete` (`docs/specs/2026-09-12-verification-proof-bundle.md`,
|
|
2
|
-
// step 3).
|
|
2
|
+
// step 3). The prose in `references/figma-component-standards.md` (State handling) points at
|
|
3
|
+
// `references/state-baseline.md`, which is generated from this file.
|
|
3
4
|
//
|
|
4
5
|
// `default` is deliberately excluded from every baseline below: documenting the
|
|
5
6
|
// resting state is not documentation, and the Figma matrix requirement for it is
|
|
6
7
|
// the executor's, not this table's. Conditional states (`loading`, `error`,
|
|
7
|
-
// `success
|
|
8
|
-
//
|
|
8
|
+
// `success`) are deliberately excluded too — whether one applies to a given
|
|
9
|
+
// component is a design judgment, not something a baseline can assert. `selected`
|
|
10
|
+
// is not conditional for a choice control: a checkbox, radio, toggle, switch or
|
|
11
|
+
// chip that cannot be selected is not one.
|
|
9
12
|
//
|
|
10
|
-
//
|
|
13
|
+
// This table is the single source for both checks. `scripts/build-state-baseline.mjs`
|
|
14
|
+
// renders it into `references/state-baseline.md`, which the Figma executor reads;
|
|
15
|
+
// CI fails when that file is stale. MAINTENANCE: change this table, then run it.
|
|
11
16
|
|
|
12
17
|
import { normalizeName } from './source-scan.mjs';
|
|
13
18
|
|
|
@@ -16,7 +21,7 @@ export const ARCHETYPES = ['button', 'input', 'choice', 'card', 'modal', 'badge'
|
|
|
16
21
|
export const BASELINE_STATES = {
|
|
17
22
|
button: ['hover', 'focus', 'active', 'disabled'],
|
|
18
23
|
input: ['hover', 'focus', 'disabled'],
|
|
19
|
-
choice: ['hover', 'focus', 'active', 'disabled'],
|
|
24
|
+
choice: ['hover', 'focus', 'active', 'selected', 'disabled'],
|
|
20
25
|
card: [],
|
|
21
26
|
modal: [],
|
|
22
27
|
badge: [],
|
|
@@ -40,6 +45,14 @@ export const NAME_SYNONYMS = {
|
|
|
40
45
|
tag: 'badge',
|
|
41
46
|
};
|
|
42
47
|
|
|
48
|
+
// Other names a recorded state goes by. shadcn and Radix call a checked checkbox
|
|
49
|
+
// `checked` and a pressed-in toggle `on`; both are `selected` here. `pressed` is
|
|
50
|
+
// not one: the standards use it for `active`.
|
|
51
|
+
export const STATE_SYNONYMS = {
|
|
52
|
+
checked: 'selected',
|
|
53
|
+
on: 'selected',
|
|
54
|
+
};
|
|
55
|
+
|
|
43
56
|
export function resolveArchetype(record) {
|
|
44
57
|
if (ARCHETYPES.includes(record.archetype)) return record.archetype;
|
|
45
58
|
const bySynonym = NAME_SYNONYMS[normalizeName(record.name)];
|
|
@@ -51,6 +64,11 @@ export function missingStates(record) {
|
|
|
51
64
|
if (archetype === null) return [];
|
|
52
65
|
const baseline = BASELINE_STATES[archetype];
|
|
53
66
|
if (baseline.length === 0) return [];
|
|
54
|
-
const recordedKeys = new Set(
|
|
67
|
+
const recordedKeys = new Set(
|
|
68
|
+
Object.keys(record.states ?? {}).map((key) => {
|
|
69
|
+
const name = normalizeName(key);
|
|
70
|
+
return STATE_SYNONYMS[name] ?? name;
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
55
73
|
return baseline.filter((state) => !recordedKeys.has(normalizeName(state)));
|
|
56
74
|
}
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
// extensions it yielded. The duplication was the shape #57 was filed for: two
|
|
6
6
|
// definitions of one rule, agreeing today, with nothing keeping them in step.
|
|
7
7
|
//
|
|
8
|
-
// The file filter is the caller's, not a built-in: a
|
|
9
|
-
// stylesheets and
|
|
10
|
-
//
|
|
8
|
+
// The file filter is the caller's, not a built-in: a caller that
|
|
9
|
+
// wants stylesheets and HTML takes them, and one that wants only .ts/.tsx can
|
|
10
|
+
// narrow it, without forking the walker.
|
|
11
11
|
import { readdirSync, statSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
13
|
|