[RFC] Publish the Figma-variable to CSS-custom-property mapping, and a checker for it
- Dominant language
- TypeScript
- Stars
- 13.1k
- Forks
- 1.1k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
## The gap
The Night Watch wiki documents how TypeScript constructs map to Figma — union props
to variant axes, booleans to component properties, ReactNode to slots — but not how
**tokens** map. It says the token workflow lives in private automation.
That mapping is not derivable by rule. We measured it across all 166 variables in the
published library and it takes **five different name shapes** to match them. Anyone
building on Astryx who wants to drive tokens from Figma — which is the common case
for an agency applying it to a client brand — has to rediscover this by trial.
## What the mapping actually is
Figma and the CSS agree on the token *set* far more than on the token *names*. Four
systematic divergences:
| divergence | Figma | CSS custom property |
|---|---|---|
| role/hue order inverted | `Gray/Background` | `--color-background-gray` |
| group segment dropped | `Status/Success` | `--color-success` |
| synonym | `Surface/Card` | `--color-background-card` |
| abbreviation | `Element/Small` | `--size-element-sm` |
| word vs decimal | `Spacing/half` | `--spacing-0-5` |
Straight-through cases exist too — `Radius/Element` is `--radius-element`, and the
whole Radius and Spacing collections map one-to-one once the two fractional spacing
steps are aliased.
Measured coverage, Figma variable → a custom property `astryx.css` actually reads:
```
166 Figma variables
135 reach a code token (81%)
110 themeable — a theme or a consumer can override them
25 global constants declared in astryx.css
31 reach nothing
```
The 31 fall into three explainable groups, none of them a naming problem:
- **14 `Line Height/*`** — the CSS has no `--line-height-*` tokens; line-height is
compiled into the atomic classes. In Figma these are bound to text styles (14 of 14
local text styles bind `lineHeight` to a variable), so each side has a mechanism and
they simply differ. Worth documenting so it does not read as a gap.
- **14 `Component/*`** — Badge, Input Ring, ProgressBar internals, compiled into the
components rather than exposed as variables.
- **3 semantic misses** — `Status/Error Inverted`, `Effects/Elevation Shadow`,
`Effects/Elevation Shadow Strong`. The code has `--shadow-low/medium/high`, related
in meaning but not in name.
The reverse gap is larger and worth stating: **118 of the 228 tokens a theme declares
have no Figma name at all**, dominated by the 56 `--color-data-*` chart colours, plus
the motion durations, the shadow scale, and `--astryx-card-padding` /
`--astryx-section-padding`. A designer working only in the library cannot reach half
of what a theme can set.
## Proposal
1. **Publish the mapping** as a generated artifact — `tokens.map.json` or similar,
emitted alongside `theme.css` by `astryx theme build`, keyed Figma name → custom
property. It is derivable from the same resolved token set the theme build already
walks, so it need not be hand-maintained.
2. **Ship a checker.** A consumer overriding a custom property that no component
reads gets no error and no styling — the same silent class as #5920 and #5921. A validator that takes a set of overrides and reports the ones
`astryx.css` never reads catches it. Ours found two wrong guesses in our own brand
config within minutes: `--radius-md` (the real names are role-based:
`--radius-element`, `--radius-container`) and `--color-border-focus` (it is
`--focus-outline-color`).
3. **Document the two deliberate asymmetries** — Line Height and `Component/*` — so
they are not repeatedly rediscovered as bugs.
## What we can contribute
A working implementation of both, extracted from our spike:
- the candidate matcher and mapping table over all 166 variables, with per-name
reporting of which shape matched;
- the override checker described in (2);
- both as gated claims that fail if the correspondence changes.
These live in a client project's repository at the moment, so say the word and we will
extract them into a standalone repo or open a PR here directly. Happy to reshape into
whatever form suits — a script
under `scripts/`, a `@astryxdesign/cli` subcommand (`astryx theme check`), or just the
documented table.
## Why we care
We are evaluating Astryx as the design system for a Norwegian housing developer with
multiple building launches, each needing its own brand on one shared component set.
Our approach delivers a brand as a scoped block of custom properties generated from
CMS content, which works well precisely *because* the components read tokens through
`var()`. That makes the Figma ↔ CSS token correspondence the load-bearing contract of
the whole workflow, and it is currently undocumented.
## Environment
`@astryxdesign/core@0.5.2`, all seven `@astryxdesign/theme-*@0.5.2`,
`Astryx Library (Community)` Figma file as of 2026-09-02.
Contributor guide
Research direction
Start by tracing the resolved token set used by `astryx theme build`, alongside the generated `theme.css` and reads in `astryx.css`. Review the proposed `tokens.map.json` artifact, override checker, and the possible `scripts/` or `@astryxdesign/cli` entry points. Done means the mapping, checker, gated correspondence claims, and documentation of the two asymmetries are published.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- css, figma, typescript
- Domain
- design, documentation, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100