Theme: Enrich design-tokens.js export with more token data#76604
Theme: Enrich design-tokens.js export with more token data#76604
design-tokens.js export with more token data#76604Conversation
dac0b1b to
67dcbaf
Compare
|
Size Change: 0 B Total Size: 7.66 MB ℹ️ View Unchanged
|
Replace the flat array default export with two named exports: `tokens` (keyed by variable name, with description and group) and `groups` (keyed by source file, listing member tokens). Update ESLint and Stylelint consumers accordingly.
The Terrazzo iteration order is already deterministic.
67dcbaf to
51a0f68
Compare
|
Flaky tests detected in 2202947. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/23445569685
|
Instead of transforming the value at the CSS plugin layer, apply the Chrome font-weight workaround directly in typography.json. This keeps $value and css aligned in the design-tokens.js export.
Enrich the token metadata with $type, $description (renamed from description), and modes.default containing both the DTCG $value and the css string. Brand tokens omit modes since their runtime values depend on --wp-admin-theme-color.
packages/theme/CHANGELOG.md
Outdated
| ### Breaking Changes | ||
|
|
||
| - The `design-tokens.js` export now provides `tokens` (keyed by variable name, with `$description`, `$type`, `group`, and `modes.default` containing `$value` and `css`) and `groups` (keyed by source file) named exports, replacing the previous flat array default export ([#76604](https://github.com/WordPress/gutenberg/pull/76604)). | ||
| - The `medium` font weight token value changed from `500` to `499` in the source definition. The CSS output is unchanged since the same workaround was previously applied at the CSS transform layer ([#76604](https://github.com/WordPress/gutenberg/pull/76604)). |
There was a problem hiding this comment.
Can you explain why we need to make this change?
There was a problem hiding this comment.
Yes, I wanted to discuss the trade-offs here again, since you originally wanted to keep the "499 workaround" separate from the canonical value.
So here is what a design-tokens.js data object looks like right now:
"--wpds-dimension-surface-width-lg": {
"$description": "Large surface width",
"$type": "dimension",
"group": "dimension",
"modes": {
"default": {
"$value": {
"value": 560,
"unit": "px"
},
"css": "560px"
}
}There's a $value field for a programmatic breakdown of the value, and a css field for the CSS string representation of the value.
But what happens when there's a token that has both a canonical value and a transformed value? We need to double the amount of fields, and it's not even clear which value you should use for whatever context:
"--wpds-font-weight-medium": {
"$description": "Medium font weight for emphasis and headings. Uses 499 instead of 500 to work around a Chrome bug where 500 renders as 600 when the exact weight is unavailable. See: https://issues.chromium.org/issues/40552893",
"$type": "fontWeight",
"group": "typography",
"modes": {
"default": {
"$value": 500,
"transformed$Value": 499,
"css": "500"
"transformedCss": "499"
}
}
}
};So at a certain point, maintaining a separation between a canonical value and a transformed value isn't really worth it, unless we have an actual case where we must use one over the other. At the moment there's no use case, so it's just unnecessary overhead. And even in the general case, I think we should try to avoid having transformed values at all, since it adds complications like this where consumers need to choose which value (raw/transformed) to use when. What do you think?
What would this look like in practice? |
This was motivated by seeing Storybook files like this, which was manually duplicating the token descriptions. Another use case is token values in Framer Motion setups, where we cannot use CSS variables. gutenberg/packages/components/src/utils/dropdown-motion.ts Lines 4 to 15 in 4c2851a It's also useful in Emotion files, which currently cannot be processed by the fallback injector (#75872). |
design-tokens.js export with token metadatadesign-tokens.js export with more token data
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
| tokenEntry.modes = { | ||
| default: { | ||
| $value: token.token.$value, | ||
| css: token.value as string, |
There was a problem hiding this comment.
mode: '.' (on L32 above) always returns string here.
There was a problem hiding this comment.
Might be worth an inline comment.
|
I'll give this a more detailed look tomorrow, but thinking out loud a bit on initial thoughts: I'm a bit confused about what we want this file to be, and what should be the sources of truth. It looks like it duplicates a slice of what we could expect to point people to the original token values for, but then also mixes in some CSS-specific concerns. Could we, for example, structure this more as a pointer or mapping of the tokens and equivalent CSS properties and expect people to use the original token data instead. Curious what you think, since I haven't fully made up my mind either. Everything in "prebuilt" is ultimately a derivation of the original tokens, so maybe this is fine as just one more that's optimized for common usage patterns in JavaScript. |
Would love to hear any alternative setups here. I believe the current gap is that we don't expose the "original token data" in any way. One thought I had was whether we should just expose the original json files. Those are in a standardized format, and so consumers could then manipulate them however they wish. The current approach in this PR is kind of like a convenience layer for that. |
Yeah, this is exactly what I was thinking, or... more accurately, what I thought we already did 😅 They're sort-of consumable as-is in JavaScript if we expose them (better with bleeding-edge JSON modules supports). After letting this sit in my brain overnight, I think this can work. As you say, it's just a convenience to that data. And a derivation of the source tokens in the same way the existing CSS or other prebuild artifacts are. It could be helpful to have a clearer articulation of all that we expect it contain, because right now it cherry-picks a few details from tokens and combines with extra things like CSS, group. |
Having |
What?
Replaces the flat
string[]default export of@wordpress/theme/design-tokens.jswith two named exports that carry full token metadata:tokens— an object keyed by CSS variable name, with$description,$type,group, and default values for each token.groups— an object keyed by group name (derived from the source token file), with an array of member token names as values.Why?
The previous export was a flat list of CSS variable names used only for lint rule validation. Storybook stories and documentation that need to display token metadata (descriptions, types, values) currently hardcode this information, which drifts from the source of truth in
tokens/*.json.By exposing the data that the Terrazzo build already has access to, consumers like stories and docs can stay in sync without duplication. Having both the DTCG
$value(structured, for programmatic use) andcss(string, for documentation contexts) covers the common access patterns.How?
Updated the
terrazzo-plugin-known-wpds-css-variablesTerrazzo plugin to read$description,$type, and$valuefrom the token data and output the two named exports. Default values are nested undermodes.defaultto allow future mode expansion (e.g. compact, high-contrast). Brand-dependent color tokens (those whose runtime value depends on--wp-admin-theme-color) omit themodesproperty since they have no stable default.Updated the two consumers of the previous default export:
packages/eslint-plugin/rules/no-unknown-ds-tokens.js— switched fromnew Set(tokenList)tonew Set(Object.keys(tokenMap)).packages/theme/src/stylelint-plugins/no-unknown-ds-tokens.mjs— same.Also moved the Chrome font-weight 500-to-499 workaround (chromium#40552893) from the CSS plugin transform layer into
typography.jsondirectly, so that$valueandcssare consistent in the output.Design decisions
$description,$type, and$valueuse the DTCG$-prefix convention.cssandmodesare not DTCG terms and use plain names.modesnamespace: Values are nested undermodes.defaultrather than flat on the token entry, anticipating future mode support.--wp-admin-theme-colorhave no stable default. Rather than exposing a misleading fallback, these entries omitmodesentirely.Known limitation: type narrowing
The generated file is
.mjs, so TypeScript infers widened types ($typeisstring, not the literal"dimension"). Consumers can't use discriminated union narrowing based on$typeto infer the shape of$value. Full literal type inference would requireas const, which is TypeScript-only syntax. Once the project's minimum Node version is raised to 24+ (which supports native TypeScript execution via type stripping), we can convert the generated file to.tsand addas const.Testing Instructions
Run the ESLint and Stylelint rule tests:
Use of AI Tools
Authored with Cursor (Claude).