Skip to content

Figma bridge

j3w1 edited this page Sep 9, 2026 · 2 revisions

A bounded, one-way route for getting a reviewed set of theme values into Figma as Variables. For designers, and for whoever operates the connector on their behalf.

Set expectations first: this is not a one-click Figma plugin, not a component library, and not a sync. Values travel from a pinned theme revision into a Figma file. Nothing travels back — editing a variable in Figma never changes the theme.

Contents: What it maps · Size limits · Generate a payload · Dry-run and apply · Receipts · Update and rollback · Conflicts · Evidence

What it maps, and what it refuses

Theme value Becomes
sRGB colour role a COLOR variable
Scalar px spacing or radius a FLOAT variable
An alias between roles a native Figma variable alias

Names are slash-separated inside the collection j3w1/theme/default/v1; the token path stays the stable identity. Semantic scopes are set specifically — text, fills, strokes, gaps, corner radius — so the variables offer themselves in the right places. Eligibility and pending decision IDs travel with each definition, so a designer can see that a value is pending.

Refused, and reported rather than flattened: typography, shadows, composite borders and any other unsupported type. Only the approved default profile is imported; heritage-ansi and extended are reported and never turned into extra modes. Alias primitives come across as dependencies with empty property scopes — they support the roles, they are not newly approved things to apply.

Fonts are not imported and no binaries are distributed. Install the families named in the pinned typography tokens yourself.

The size of one import

The reviewed route is bounded to eight variables including dependencies, in one collection with one default mode. That is a deliberate ceiling: it fits inside a single connector operation, so an import is atomic in practice. Larger payloads fail before any mutation.

The default sample is six variables: color.surface.default, color.text.default, space.4, radius.none, plus the two primitives the first two alias.

If you were hoping to import the whole palette, this is the honest answer: you cannot, through this route, today.

Step 1 Generate a pinned payload

From a checkout, at an exact revision:

node scripts/figma-bridge.mjs --ref FULL_COMMIT_OR_RELEASE_TAG > payload.json

--roles takes an explicit comma-separated role selection if you want something other than the default sample. The command reads Git objects at the resolved full revision — never a moving branch, never the working tree — and records the source and payload digests, the mapping version, the unsupported items and the excluded profiles.

The bridge and script are included in v1.1.0. Instructions and limits: spec/figma-bridge.md and the bridge page.

Step 2 Dry-run, then apply

The adapter is exports/figma/importer.mjs, driven through a Figma connector's Variables API route. Pin its download to the same reviewed revision as the instructions you are following, and read the connector's own current instructions first.

return await importFigmaVariables(figma, {
  payload, receipt: null, documentKey, action: "dry-run"
});

Dry-run reads only. Inspect the returned diff — the proposed additions and changes — before anything is written. Then invoke again with action: "apply" for exactly the change you reviewed.

If you are asking someone else to run it, this is a complete request:

Use the documented Figma Variables bridge in a new draft. Start from its default sample at an exact reviewed theme revision. Run the read-only dry-run first and show me the diff, including anything reported as unsupported. Apply only after I have confirmed the target file, and return the complete receipt.

Use a new draft for a first import. Confirm the target file key before applying — the document key is an explicit input, not something the adapter guesses.

Receipts are the whole mechanism

Every apply returns a receipt: the record of which variables the bridge owns, with their recorded values and metadata. Keep the entire receipt, outside the document and outside the repository.

Every later dry-run, apply and rollback must be supplied with the current receipt and the same document key. Without it the bridge will not adopt a variable just because the name matches — which is the property that keeps it from quietly taking over someone else's work.

A receipt is a private ownership record. Do not paste one into a public issue or wiki.

Updating and rolling back

An exact re-import is a no-op: same source, same target, same receipt, no changes. That is how you check that a design file is still in sync without touching it.

For a real update, review the new diff and keep the new receipt. Owned variables that an update omits are retained and reported, never silently removed.

To undo the most recent apply, use action: "rollback" with the current receipt. Existing variables regain their exact recorded values and metadata; newly created variables are removed in reverse dependency order; a collection the bridge created is removed only if it holds nothing unrelated. The call returns the previous receipt — keep that too.

Rollback also refuses to remove a variable that a retained variable still references. It does not audit every canvas node, so inspect any bindings you made from layers to these variables before asking for a rollback.

When the bridge refuses

Before any mutation, the adapter compares every owned ID, value, description, scope, collection identity and mode against the receipt. A variable that is missing, detached or manually edited stops both apply and rollback.

That is the correct behaviour, not a bug. Resolve it deliberately: decide in Figma what the manual edit was for, or start a separate draft. Never discard the receipt to force an overwrite — that trades a visible conflict for an invisible one.

Permission errors, mode errors and API errors stay failures. No team-library publishing permission and no paid-plan capability is assumed by this route.

What has actually been verified

The following records the historical bridge exercise described in the repository. It is not a claim that this wiki update re-imported v1.1.0 into Figma; a new target or payload needs its own protocol and receipt.

A real six-variable sample was imported through the connector into a new design draft (Starter/Full access, Plugin API 1.0.0). Actual alias IDs matched. Spacing and radius matched exactly. Colour channels matched the exact float32 representation of the source channels, with no tolerance. An exact re-import changed no IDs. A separately previewed spacing addition was applied and then rolled back. A deliberate manual edit was rejected without overwriting the edit.

That is the claim, and it is the whole claim. It demonstrates a bounded bridge. It does not promise team-library publishing, every account capability, or a Figma component kit.


Next: Tokens · Components · Verification

Clone this wiki locally