pathway-dsPersistent rules for any AI agent working on this repository. These apply across sessions — don’t wait to be told them again. If a rule here conflicts with something in a specific conversation, follow the rule here unless the user explicitly overrides it in the moment.
Different layers of this system have different sources of truth. Keep them straight.
| What | Source of truth | Flow |
|---|---|---|
| Design tokens (primitives, semantics, modes) | Figma → exported to tokens/figma-export/pathwaytokens.json |
Automatic. CI runs sync-tokens.js → style-dictionary → Storybook. |
| Component implementations (HTML demos, specs, stories) | GitHub (the files in this repo) | Manual. Designer changes a component in Figma → user asks agent to pull → agent uses Figma MCP tools to fetch the updated node → agent edits the component’s files in this repo. |
| Component visual design (variants, frames, anatomy, variables-bound properties) | Figma | Same manual flow as above. Figma is the design artifact; this repo carries the implementation. |
Practical implications:
tokens/ conflicts with what’s in src/tokens/ or pathway-design-tokens.json — Figma wins. Regenerate the derived files; never hand-edit them.components/<name>/<name>-spec.md disagrees with what the component in Figma currently looks like — that means Figma was updated and this repo wasn’t yet. Ask the user whether to pull. Don’t assume either side is right.use_figma and write tools; do not call them unless the user explicitly asks you to change Figma. Read-only tools (get_design_context, get_metadata, get_screenshot, get_variable_defs) are fine for any diagnostic.The pipeline, in order:
Figma (source of truth for tokens)
│
│ Designer exports via "Variables Import Export" plugin
▼
tokens/figma-export/pathwaytokens.json ← committed as-is
│
│ scripts/sync-tokens.js (GitHub Action: sync-tokens.yml on push to figma-export/)
▼
tokens/pathway-design-tokens.json ← DTCG-format derived file
│
│ node style-dictionary.config.js (called by deploy-storybook.yml)
▼
src/tokens/tokens.css, src/tokens/tokens.js ← consumed by Storybook + components
Rules:
sync-tokens.js drops that token silently (with a warning in the CI log) and the build continues.Indigo → Brand rename), a one-off script or sed may be needed to rewrite stale references in already-imported data so the tree resolves. Those are ad-hoc jobs, requested explicitly by the user, run once, committed, and done. Never bake a one-time rewrite into the recurring sync, audit, or /update-tokens routines — a repeating rewrite masks real broken state once the migration is complete and makes future orphans invisible.pathway-design-tokens.json is derived. Do not hand-edit it. Changes made to it will be wiped by the next Figma sync.src/tokens/tokens.css is derived. Do not hand-edit it. Style Dictionary regenerates it on every build.Dark-mode tokens are now imported from the Figma export. The EXCLUDED_MODE_SLUGS set in sync-tokens.js is empty — all modes come through, including dark.
tokens/pathway-design-tokens.json and are emitted as CSS variables in src/tokens/tokens.css."dark-mode" and "dark" back to EXCLUDED_MODE_SLUGS in sync-tokens.js.When the token library changes — every time sync-tokens.js runs and modifies tokens/pathway-design-tokens.json or the CSS variable set — every component in components/** that references those tokens must be reconciled against Figma.
Principle: the source of truth for which tokens a component uses is Figma, not the component’s current GitHub files. If Figma says the SideNav uses fill/contextual/navitem/base and the repo says it uses some other token, Figma wins. If a token the repo mentions no longer exists, the fix comes from Figma.
sync-tokens.js that changes the derived token set (adds, removes, or renames tokens)."reconcile the components", "check components against Figma", etc.).Diff the token set. Compare tokens/pathway-design-tokens.json at HEAD against its state before the sync. Classify each change: added, removed, renamed (a disappeared name + a new name whose value matches are probably a rename — flag as a rename candidate for the user to confirm; don’t auto-rename aggressively).
components/**, src/stories/Library/**, and any src/tokens/tokens.* consumer, grep for:
--semantic-color-light-mode-icon-static-neutral-baseicon.static.neutral.base or Icon/Contextual/NavItem/Baseget_variable_defs(nodeId) — returns every variable currently bound to that node and its descendantsget_design_context(nodeId) — the current reference code with token bindings
Compare the set of tokens returned by Figma against the set the component’s GitHub files reference.Commit reconciled components. One commit per logical component update, with a message that says why the change was needed (e.g. “reconcile spinner: icon.static.brand-warm renamed to icon.static.brand”).
<component-name> · <file-path> · <stale-token-name> · <reason>
Where reason is removed-from-tokens, renamed-to-<new>, figma-also-stale, or figma-fetch-failed. The user fixes these in Figma, re-exports, and re-runs the sync.
For reconciliation to work without asking the user every time, every components/<name>/<name>-spec.md must contain a “Figma source” section with the file key and the root node ID in a parseable form. The existing sidenav and spinner specs follow this convention:
### Figma source
- **File:** [<display name>](https://www.figma.com/design/<fileKey>/...)
- **<Component> component:** [Open in Figma](https://www.figma.com/design/<fileKey>/...?node-id=<nodeId>)
Reconciliation agents extract <fileKey> and <nodeId> from the URLs with a regex. Do not remove those links or change their format. When adding a new component, copy the pattern exactly.
When a component can’t be reconciled cleanly, emit a short block at the end of the run. Example:
Reconciliation — 2 components need manual attention:
spinner
file: components/spinner/spinner-spec.md
stale: icon.static.accent-jade.base
reason: removed-from-tokens
next: delete the accent-jade branch from the Figma spinner node,
or restore the accent-jade tokens in Figma
sidenav
file: components/sidenav/sidenav-spec.md §3.3
stale: text.contextual.navitem.active
reason: figma-also-stale (Figma still aliases this to {Blue.180},
which no longer exists)
next: open sidenav in Figma, re-bind the Active text variable
to a real primitive, re-export, re-run sync-tokens
Keep entries short. The user decides which ones to act on; your job is to surface the list accurately, not to fix it silently.
When the user says “I changed the spinner in Figma, update GitHub” or “pull the new sidenav design” — i.e. the component itself changed, not just its tokens — follow this flow. It’s related to but distinct from §3.2 (which runs in response to token changes).
get_design_context — reference code + variable bindingsget_metadata — structural overview for large nodesget_screenshot — visual referenceget_variable_defs — resolved token values bound to the nodeget_design_context returns a figma.com/api/mcp/asset/<uuid> URL; curl it. If it 500s, retry a few times before giving up.components/<name>/. Update the HTML demo, the -spec.md, and any src/stories/Library/<Name>/ files together — they must stay consistent.npx storybook build) before committing. A broken Storybook build blocks CI.components/<name>/. <name> is lowercase kebab-case — sidenav, spinner, top-nav, date-picker.<name>.html — self-contained React+Babel demo, mirrors the conventions in components/sidenav/sidenav.html<name>-spec.md — authoritative specification, mirrors the structure of components/sidenav/sidenav-spec.md<name>-figmamake.html if the component ships an AI-codegen-friendly variant (SideNav does; Spinner doesn’t need one)docs/. See docs/README.md.src/stories/Library/<Name>/ (PascalCase inside Library/ because macOS APFS is case-insensitive and collides with src/stories/components/ — see the folder name there for why).components/ or docs/. The Storybook src/stories/ tree uses PascalCase folders to match the existing convention there — don’t change that.Every <name>-spec.md must follow the structure of components/sidenav/sidenav-spec.md. At minimum it has:
New components that don’t yet need every section can omit, but match the depth of sidenav-spec for the ones they do include.
Components resolve colour only through semantic tokens — never raw hex, never primitive tokens, never invented semantic names.
icon.static.<tone>.<emphasis> or icon.action.<role>.<state> — both exist as real token families in tokens/pathway-design-tokens.json. Any tone must match a real child of the family in that file. See components/spinner/spinner-spec.md §7.1 for the complete allowed list.text.static.* or text.action.* or text.contextual.* — same rule.Forbidden in any component CSS or spec:
color: #3555a0) — always wrong, always replace with a semantic varvar(--primitive-color-brand-300)) — always wrong, primitives are building blocks not contractsicon/semantic/success, color/brand/primary) — if the name isn’t in the JSON, it doesn’t existBefore writing any colour into a component, grep tokens/pathway-design-tokens.json to confirm the token name and family exist.
pathway-design-tokens.json are always lowercase with dots (semantic-color.light-mode.icon.static.neutral.base). sync-tokens.js slugifies the Figma export to this form automatically. Do not override.--semantic-color-light-mode-icon-static-neutral-base). Consume these exactly as emitted..pds-spinner__svg). Never PascalCase or camelCase in CSS selectors.If files accumulate at the repo root that don’t belong to the design system (one-off audits, scratch prototypes, unrelated assets), move them to the sibling repo:
https://github.com/helloimjolopez-collab/pathway-sandbox (private)Don’t let the design system repo become a junk drawer.
Three GitHub Actions live in .github/workflows/:
sync-tokens.yml — fires on push to tokens/figma-export/**. Runs sync-tokens.js, commits the result.sync-component.yml — fires on push to components/sidenav/sidenav.html. Regenerates sidenav-figmamake.html.deploy-storybook.yml — fires on push to tokens/, src/, .storybook/, components/, docs/, or config. Runs Style Dictionary + Storybook build, commits the output to /storybook/ on main, GitHub Pages serves it.The deployed Storybook lives at:
Data flows one way: GitHub → Storybook. Nothing ever flows from Storybook back to GitHub. If Storybook appears out of date, the fix is a push to main, not a Storybook rebuild.
Every new component follows the sequence documented in docs/component-pipeline.md. The key points for agents:
.jsx is the shared source of truth. components/<name>/<name>.jsx exports the React component. Storybook stories import from it; the standalone .html demo inlines the same logic. When the component changes, update both.<name>.jsx + <name>.html + <name>-spec.md in components/<name>/. Stories at src/stories/Library/<Name>/.docs/storybook-authoring.md — narrative prose, Playground + Controls at the top, state matrices as live components (not tables), token rows as name/swatch/hex, accessibility section that cites the overarching spec for system-wide rules. Never bury the Playground below reference tables.components/manifest.json is the machine-readable component registry. Update it when adding or changing a component.docs/figma-prep-checklist.md before handoff. If they haven’t, run the /pathway:component-readiness skill (or send them the checklist) — don’t guess.docs/design-system-spec.md) defines system-wide rules for motion, accessibility, colour, spacing, typography, naming. Every component spec inherits from it. Conflicts between a component spec and the overarching spec are resolved by the /pathway:spec-review skill, which requires explicit human sign-off on any deviation.The component pipeline is broken into four skills so different audiences can use what applies to them:
| Skill | Audience | What it does | Touches repo? |
|---|---|---|---|
/pathway:component-readiness |
Any designer | Runs Figma prep checklist against a component, reports findings | ❌ read-only |
/pathway:component-spec-maker |
Any designer | Drafts a -spec.md from a Figma component using the template; marks Status: PENDING HUMAN REVIEW |
❌ local only |
/pathway:spec-review |
Any designer | Reads a draft spec + the overarching spec; walks the user through every conflict; flips Status: to REVIEWED only when all resolved |
❌ local only |
/pathway:component-pipeline |
DS owner | --mode=create or --mode=update. Composes the three skills above → generates .jsx + .html + stories + MDX + manifest → commits + pushes |
✅ writes + pushes |
/pathway:tokens-sync |
DS owner | Syncs Figma token export, rebuilds Style Dictionary + Storybook, commits + pushes | ✅ writes + pushes |
All five skills ship in the JoLopez-Product-Plugins plugin (Claude Code). To install:
/plugin marketplace add helloimjolopez-collab/JoLopez-Product-Plugins
/plugin install pathway@jolopez-product-plugins
Token sync (update-tokens) is separate — it runs on token changes, independent of component work.
Non-negotiable principle: every Pathway skill requires explicit human approval at every gate. Claude drafts, recommends, flags — humans decide.
Status: PENDING HUMAN REVIEW to REVIEWED. Only the /pathway:spec-review skill, after walking the human through every conflict, flips the status — and only when the human has explicitly signed off on each decision.Status: REVIEWED.git pushes without confirming with the user first.use_figma or similar) without the user explicitly asking for a Figma write.Any agent working on a Pathway task must preserve these gates. If you’re composing a larger workflow (e.g. “create a dashboard with the design system”), you still pause at each gate. Skipping a gate to “save time” breaks the principle; a batched-questions implementation is a bug, not an optimisation.
If the user says “just do it, don’t ask me” for a specific action, proceed with that action — their explicit permission replaces the gate. But the next gate still applies unless they’ve scoped the permission broadly (“do the whole thing, don’t ask me”).
node_modules/, storybook-static/, .env, .claude/, or .DS_Store (all in .gitignore).pathway-design-tokens.json, src/tokens/tokens.css, src/tokens/tokens.js, components/sidenav/sidenav-figmamake.html).main.origin/main.rm -rf on anything not clearly scratch.