Lay out and document a component's page in the Figma design source — title and written description, the component set with its labelled cell grid, and extension tables of locked instances for states and properties that are not variant axes. Use when asked to "document the Button page", "add the description and grid", "show the states", "reorganise the component pages", or when a component set exists in Figma and needs the surrounding explanation. Works on ANY component set, including ones thi...
Scanned 9/3/2026
Install to Claude Code
npx -y skills add cris-achiardi/weave-ds-template --skill ds-figma-document --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ds Figma Document?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/cris-achiardi-ds-figma-document)More formats (shields.io, HTML) on the badges page.
---
name: ds-figma-document
description: Lay out and document a component's page in the Figma design source — title and written description, the component set with its labelled cell grid, and extension tables of locked instances for states and properties that are not variant axes. Use when asked to "document the Button page", "add the description and grid", "show the states", "reorganise the component pages", or when a component set exists in Figma and needs the surrounding explanation. Works on ANY component set, including ones this repo did not generate.
---
# ds-figma-document
Builds the page around a component set: the title, the written description, the labelled cell grid,
and the extension tables that show what the set deliberately does not contain.
**This does not create component sets.** Use `ds-figma-component` for that. This skill takes a set
that already exists — generated by us or hand-built by a designer — and makes the page around it
readable.
## When this, and when `ds-figma-component`
| You want | Skill |
| -------------------------------------------------- | ---------------------------------------------------- |
| A component set that does not exist yet | `ds-figma-component` |
| The page around a set: description, labels, states | **this one** |
| To reorganise several component pages | **this one** |
| To add a variant axis to a set | `ds-figma-component` |
| To show a state WITHOUT adding an axis | **this one** — that is what extension tables are for |
This skill needs a set, and — for the description and the states — the component's source. Where
the set was hand-built and no code component exists, say so in the description rather than inventing
the code side; a page that implies a code counterpart which does not exist is worse than one that
admits the gap.
## What is not ready here yet
This skill was ported from a mature design system into a **starter template**. Two things it leans
on do not exist yet:
- **No components.** `packages/contracts/components/` is empty by design, and
`.figma/maps/components.json` has no entries. There is also no working skill that builds one —
`ds-component` is superseded — so this skill can only document sets that already exist in the
Figma file on their own terms.
- **No theme axis.** `.figma/manifest.json` → `identity.themes` records `modes: ["dark"]` with
`decided: false` — dark-only, and not yet decided to be dark-only. **The mode-flip verification in
step 5 is the strongest check on this page and it cannot run.** Report that it did not run. Do not
let a page imply it follows a light/dark axis that the file does not have.
## Prerequisites
- Figma **Desktop** with the **Desktop Bridge** plugin open in the target file.
- `figma_get_status({ probe: true })`. `currentFileKey` must equal `.figma/manifest.json` →
`sources.weave.key` — **read it from the manifest; never hard-code a key.**
- **`figma_navigate({ url, lock: true })` before any write.** The target drifts between open files.
- `figma_capture_screenshot` for validation — it exports through the plugin and reflects live state.
`figma_take_screenshot` takes the REST path and needs `FIGMA_ACCESS_TOKEN` in the MCP config.
## The anatomy of a documented page
```
Frame "<Component>" VERTICAL auto-layout, 64 gap, 64 padding
├── Frame title + description
├── Frame "component" the public component — what a designer actually drags in
├── GROUP "<set name>" ← one per component set
│ ├── Labels axis brackets + one label per column and row value
│ ├── cell grid dashed cells, one per variant
│ └── COMPONENT_SET overlaid on the cell grid at the SAME position
└── ext: <Property> extension table — locked instances, tinted
```
The group and the auto-layout frame are doing opposite jobs on purpose. See the layout laws in
`references/layout-mechanics.md`.
**A component built on the wrapper pattern has more than one set,** and its sets are private —
hidden from the Assets panel by a leading dot. The page is then the _only_ place they can be seen,
so lay out a group per set, in the order a designer would reach for them. Put the public component
above them in its own block, so the thing to use is not buried under the things that explain it.
## The "How to use" companion frame
The anatomy above answers _what the component contains_. It does not answer _how to drive it_, and
for anything beyond a flat set that is the question a designer actually arrives with. Give those a
second frame beside the doc frame, titled `How to use: <Component>`.
One section per mechanism, each **prose plus a worked example**, in the same dashed-cell language as
the main page so it reads as one board:
| Section | Prose answers | The example shows |
| ------------- | ------------------------------------------------------ | --------------------------------------------------------------------------- |
| Instance swap | where the nested instance lives, how to reach it | each target set side by side, and a second row proving what survives a swap |
| Properties | what each control does, and which are design-only | one row per property, one cell per value |
| Appearance | that values bind, so modes cascade from a parent frame | one row per axis, **each cell carrying an explicit variable mode** |
The appearance row is the one worth the effort — **once this file has more than one mode.**
`cell.setExplicitVariableModeForCollection(coll, modeId)` makes a flip visible _in place_ instead of
describing it. With a single dark mode there is nothing to flip, so build this section when a mode
axis is decided, and until then leave it out rather than shipping a row that demonstrates nothing.
**Say which axes do nothing.** Sweep every mode and every axis before writing the section (the
recipe is in `../ds-figma-component/references/wrapper-pattern.md`). An axis that quietly does nothing
is the same failure as a baked literal: the board implies a capability that is not there.
Align every table's left edge by giving all row labels one width, or the page reads as ragged even
though each block is internally correct.
## Read these when you're in their territory
| When | Read |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| Placing anything | `references/layout-mechanics.md` — the three layout laws, the grid API, and the measured property-table values |
| Building any extension table | `references/extension-tables.md` — what belongs in one, the locked/tinted conventions, and how to measure states honestly |
| Writing the description | The section below |
| You need the component's real states | `pnpm contract <Name>` and its `<Name>.module.css` — never a guess about what a control "should" have |
| Naming an axis or a value | `.ai/maps/prop-map.md` §1–2 |
## Workflow
### 1. Survey before touching anything
Screenshot the page. Read the existing structure. If there is an older property table or a
hand-built specimen, decide explicitly whether it stays — and say which you chose. Do not delete
someone's work as a side effect of adding yours.
### 2. Title and description
The description is the part a reader cannot reconstruct from looking at the artefact. Three
paragraphs, in this order:
1. **What it is** — the variant/size surface in one sentence.
2. **What it inherits** — the underlying element or primitive, and what that gives you for free
(focus order, keyboard activation, form submission). Then the binding story: values bind to the
token tier, which is why a token change repaints it.
3. **What is deliberately absent, and why** — or, where a `State` axis exists, **what it means.**
This is the highest-value paragraph and the one people skip. If the set carries states, say that
`State` is a design-only axis with no prop behind it (except `disabled`), name the tokens each
state is built from, and name the variant that breaks the pattern. If a state is genuinely absent,
say why.
That third paragraph has a repo rule behind it: states the browser owns are not props here, so a
`State` axis in Figma has **no code counterpart by design**. Saying that plainly is the difference
between a designer asking for `state="hover"` and understanding why it will never exist.
`.ai/maps/proposals/README.md` has the reasoning.
Bind the description to a text style — match what it already is if you can, so nothing moves
visually. **Do not force a binding on the title** if no text style matches its size; inventing one
changes the design. Note the file's recorded conflict while you are here: line height disagrees
between the observed text styles (`UI/Button` is 1.2487, the others are 100). Do not fix it inside a
page; record it.
### 3. The component group
The set, the dashed cell grid overlaying it, and the labels — all in one GROUP.
`references/layout-mechanics.md` has the mechanics; the two that decide whether it looks right are:
**overlay needs a group**, and **cells must be whole pixels**.
### 4. Extension tables
For every property that is NOT a variant axis. `references/extension-tables.md`.
The test: _would modelling it as an axis multiply the whole set?_ If yes, it is a candidate for an
extension table. Slot booleans are the clear case.
**States are not automatically extensions.** A component built on the wrapper pattern carries
`State` as a real axis, and then a states table would only repeat what the set already shows —
retire it rather than keeping both. Check what the set holds before building a table for something
it already models.
### 5. Verify
- Screenshot the whole frame; check nothing overlaps and the frame hugs its content.
- **Flip the colour-scheme collection to its other mode.** Everything bound repaints. Anything that
does not is either a baked literal you must annotate, or a binding you got wrong. Clear the mode
after. **In this file there is no second mode yet** — record that this check did not run.
- Confirm every extension instance is `locked`.
### 6. Record
Update `.figma/maps/components.json` against `.figma/schema/components.schema.json` — the set id, the
group id, the extension tables and what each shows, and any measured literal with the method used to
get it. An entry per component that is actually done, never one in advance. `componentKey` stays
`null` until the source file is published as a library; `pageId` and `componentSetId` are EPHEMERAL
by the schema's own wording, so record them as refreshable rather than as a durable join.
## Caveats belong in Figma annotations
**Every issue, workaround and translation goes on the node as a Figma annotation.** Not only in the
commit, not only in the map, not only in the page description — on the component itself, where
someone inspecting it will hit it.
```js
node.annotations = [{ labelMarkdown: '**Fill does not inherit.** …', categoryId: '1:0' }];
```
Four preset categories, and the choice carries meaning:
| id | Category | Use for |
| ----- | ------------- | ------------------------------------------------------------------------------------------------- |
| `1:0` | Development | translations and Figma limits — what could not bind, what is literal, where the two models differ |
| `1:1` | Interaction | which states are real, which are design-only, what a prop actually means |
| `1:2` | Accessibility | contrast and perceivability concerns worth checking before shipping |
| `1:3` | Content | placeholders and stand-ins someone is expected to replace |
Annotations take markdown, so bold the claim and keep the explanation to a sentence or two. Put them
on the **component set** (and on the public wrapper too, where there is one) — that is what a
designer clicks. For a glyph set, annotate every glyph: the caveat is where you hit it, not on a
gallery frame.
A page description explains the component to someone reading the board. An annotation warns someone
_using_ it. Write both; they are not substitutes.
## The honesty rule
**A baked literal must be annotated on the artefact, not only in the commit message.**
Some values genuinely cannot bind. Baking those is correct and often the only option. Hiding that
they are baked is not: a designer reading the board in six months will not read your commit, will
assume the cells follow the tokens, and will be wrong.
Put a note row on the table saying which cells are literals, how they were measured, and that they
will not follow a token change. `references/extension-tables.md` has the wording.
**But exhaust the bindings first — the honesty rule is not a licence to bake.** A `color-mix()`
result has no token holding it, which reads like proof that it must be baked. It is not: the mix's
_inputs_ are both tokens, and Figma composites a bound layer over a bound fill to reach the same
place. A bake made under that reasoning is wrong twice over — it freezes the value against a token
change, and a measured literal can simply be incorrect in a direction the note can never reveal.
Before annotating anything as unbindable, ask what the CSS is _composing_, not what it _computes_.
`references/extension-tables.md` and `../ds-figma-component/references/wrapper-pattern.md` carry the
state-layer recipe.
## Housekeeping
Place everything inside the page's frame, never on blank canvas. Delete partial artefacts from a
failed attempt before retrying — a half-built table is indistinguishable from a finished one. Never
delete or restyle a node you did not create unless that is explicitly the task; if reflowing pushes
someone's node out of place, say so.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!