feat(theme): Export palette and theme colors as Figma variables - #1049
Draft
mdo wants to merge 1 commit into
Draft
feat(theme): Export palette and theme colors as Figma variables#1049mdo wants to merge 1 commit into
mdo wants to merge 1 commit into
Conversation
Designers can now import the Pierre colors into Figma instead of
copying hex values by hand. `moonx theme:build` writes `figma/`:
`primitives.json` carries every palette step, and each
`figma/semantic/*.json` is one theme variant that Figma turns into
a mode when the files are imported together.
The generator in `src/createFigmaTokens.ts` emits Design Tokens
Community Group JSON, the format Figma's variable import expects.
Semantic tokens are matched back to palette steps by hex and carry
a `com.figma.aliasData` extension so the two collections link, plus
a concrete color so a file still imports on its own. A role value
that is in no palette scale fails the build, which catches roles
drifting from the palette; `#ffffff` is the one intentional
literal.
Palette steps are sorted numerically and groups are serialized
from Maps because JavaScript hoists integer-like keys ("100")
ahead of leading-zero ones ("020"). Figma creates variables in
file order, so without that each scale would list its lightest
steps last.
The Display P3 vibrant variants are not exported; Figma's
importer supports only sRGB and HSL.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Member
Contributor
Author
Just trying to pad my stats. And no, not likely. I'm playing with this in Figma for now, still not 100% solid yet. |
Member
|
word word! |
Contributor
Author
Contributor
Author
|
hi mom |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
moonx theme:buildnow also writespackages/theme/figma/, a Figma-importable copy of the same colors in DTCG token format — so designers can import the Pierre palette into Figma instead of copying hex values by hand.Two collections, following Figma's variable modes import rules:
figma/primitives.json— all 249 palette steps across the 21 scales, one mode. Tokens land asblue/500,neutral/1040, and so on.figma/semantic/*.json— 8 files, one per theme variant (light, dark, both-soft, and the four CVD variants). Figma turns each file into a mode, and every file carries the same 58 role tokens (bg/editor,syntax/keyword,ansi/brightRed, …).Semantic values alias the primitives through the
com.figma.aliasDataextension, so editing a primitive in Figma updates every mode that uses it.Notes for reviewers
Aliases carry a concrete color too. Each semantic token has both a real hex
$valueand an alias, so a file still imports cleanly if the primitives collection is not present.Drift from the palette fails the build. Aliases are resolved by matching role hexes back to palette steps, so a role value that is in no palette scale throws. That is deliberate — it catches roles drifting away from the palette.
#ffffff(bg.editorandaccent.contrastOnAccentin the four light variants) is the one intentional literal, allowlisted inUNALIASED_ROLE_COLORS.Why palette steps get sorted and serialized by hand. JavaScript hoists integer-like object keys (
100) ahead of leading-zero ones (020), soObject.entries(neutral)returns the lightest shades last andJSON.stringifypreserves that. Since Figma creates variables in file order, the first draft listed every scale starting at100with020–080stranded at the bottom. Steps are now sorted numerically and groups serialized fromMaps to hold that order.Vibrant variants are excluded. Figma's importer supports only sRGB and HSL, and those themes use
color(display-p3 …).Packaging.
figma/**is added to.vscodeignoreso the tokens do not ship inside the VSIX; npm was already covered by thefilesallowlist. The output is committed likethemes/*.json, and.oxfmtrc.jsonignores it the same way.Test plan
moonx theme:build— writes all 9 filesmoonx theme:test— 376 pass, including 21 new intest/figma.test.tsmoonx theme:typecheckmoon run root:format root:lintfigma/and rebuilding reproduces byte-identical outputPierre Primitivesas their alias target, so cross-collection linking only resolves if the collection is named exactly that (step 1 of the README instructions). Worth confirming aliases resolve and that the 8 files land as 8 modes.