facebook / facebook/astryx

[RFC] Publish the Figma-variable to CSS-custom-property mapping, and a checker for it

Open
#5,922 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.