Theme: Add build plugins to inject design token fallbacks#75589
Conversation
8bed000 to
407f071
Compare
| "./postcss-plugins/postcss-ds-token-fallbacks": { | ||
| "types": "./src/postcss-plugins/postcss-ds-token-fallbacks.d.mts", | ||
| "import": "./src/postcss-plugins/postcss-ds-token-fallbacks.mjs", | ||
| "default": "./src/postcss-plugins/postcss-ds-token-fallbacks.mjs" | ||
| }, | ||
| "./esbuild-plugins/esbuild-ds-token-fallbacks": { | ||
| "types": "./src/esbuild-plugins/esbuild-ds-token-fallbacks.d.mts", | ||
| "import": "./src/esbuild-plugins/esbuild-ds-token-fallbacks.mjs", | ||
| "default": "./src/esbuild-plugins/esbuild-ds-token-fallbacks.mjs" | ||
| }, | ||
| "./vite-plugins/vite-ds-token-fallbacks": { | ||
| "types": "./src/vite-plugins/vite-ds-token-fallbacks.d.mts", | ||
| "import": "./src/vite-plugins/vite-ds-token-fallbacks.mjs", | ||
| "default": "./src/vite-plugins/vite-ds-token-fallbacks.mjs" | ||
| }, |
There was a problem hiding this comment.
These are official exports so anyone could theoretically integrate them into their build tooling. If necessary, we can even export the data source of the fallbacks so people can make their own injection plugins.
|
Size Change: +1.14 kB (+0.02%) Total Size: 6.84 MB
ℹ️ View Unchanged
|
| const ext = args.path.match( /(\.[^.]+)$/ )?.[ 1 ] || '.js'; | ||
|
|
||
| return { | ||
| contents: addFallbackToVar( source ), |
There was a problem hiding this comment.
In the both the esbuild and vite plugins, we're replacing all instances of the token strings, including in code comments. This should be safe and still understandable. Ideally we skip code comments, but it would increase the complexity of this plugin in a way that's probably not worth the trouble.
|
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. |
|
I wonder if the JavaScript part of this is overkill for what we could potentially manage ourselves. Specifically, I'm a little concerned of the performance impact of reading full contents and scanning for If we did decide to keep the JavaScript pieces, is it necessary to have a dedicated Vite plugin if Vite internally uses ESBuild (i.e. can we tell it to use the ESBuild plugin). |
|
Another option could be to generate these out-of-band to check into source control, similar to what we do with theme artifacts and checking their accuracy. Upsides:
Downsides:
One question regardless of approach is whether we should be more targeted to |
|
To first give an idea of the measured build performance impact:
Yes, apparently Vite cannot use esbuild plugins directly 🙃
My understanding was that at least If we did selective replacement (either of certain packages or of certain file types), we’d also need to add linters to warn on token usage that isn’t going to be replaced. So given that kind of added complexity (and missed spot risk) of selective replacement, the negligible overhead of the “replace everything” strategy doesn’t seem too bad. What do you think? If there are still some reservations about certain parts of the flow, I think it’s fine to start small (like just the stylesheets). But performance-wise, it seems pretty much free. |
|
If it's in-fact negligible, then I'm not as concerned. We could choose to scope it if it turns out to be an issue. Something we discussed while pairing on this that's worth considering: Do we even need the default stylesheet if we have this fallback behavior in place? It could be problematic for downstream consumers that want to use theme tokens, but then they could also just opt to use the same plugins we're developing here for our own build pipeline. |
|
cc @youknowriad since this impacts the build scripts, and following some previous discussions about integrating theme variables. For visibility, this builds on #75586 which adds approximate values for theme variables to be used as fallbacks. As described in #75586, this is largely meant to be used for backwards-compatibility, since plugins may support 2-3 or more versions of WordPress, the components still need to work. The approximates are very rough and ThemeProvider is still the preferred means of generating color values. This is just meant to ensure the components can be basically functional and themed in an environment where that's not available. We could probably even remove the fallbacks behavior after a few versions. |
|
Does the fallback honor the WordPress user profile? What's the story for that? |
| }, | ||
| }; | ||
| const plugins = [ | ||
| dsTokenFallbacksJs, |
There was a problem hiding this comment.
Is this a plugin that is needed just for Gutenberg or for third-party plugins too?
There was a problem hiding this comment.
Third-party plugins would benefit from it too if they want to start using tokens in plugins that still need to support versions of WordPress that don't have @wordpress/theme.
Yes 👍 We have a story to check the |
|
Ok sounds good to me. You folks know better than me here. |
6e4bcea to
e63e5f7
Compare
e63e5f7 to
b2083a8
Compare
|
@ciampo Thanks for catching. One was that it was only processing CSS modules, and the other was that I checked for any other possible gaps that we're missing, but couldn't find any. Would appreciate more eyes (tokens 😐) here though. |
|
#75879 is the other important half of the fix for https://core.trac.wordpress.org/ticket/64698, where we actually remove the design tokens stylesheet from being exposed through a style handle and loaded in the app. |
|
Flaky tests detected in 0e64b79. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/22365458408
|
ciampo
left a comment
There was a problem hiding this comment.
I can now see the fallbacks in the dataviews and the boot package.
I believe we're good to merge 🚀
|
There was a conflict while trying to cherry-pick the commit to the wp/7.0 branch. Please resolve the conflict manually and create a PR to the wp/7.0 branch. PRs to wp/7.0 are similar to PRs to trunk, but you should base your PR on the wp/7.0 branch instead of trunk. |
* Add build plugins to inject design token fallbacks * Add changelog * Harden esbuild and Vite token fallback plugins * Fix lint errors in .d.mts type declaration files * Add code comments addressing review feedback * Add token fallback plugin to compileStyles * Use static token map in Stack for build-time fallback injection Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: aduth <aduth@git.wordpress.org> # Conflicts: # packages/theme/CHANGELOG.md
|
Manually cherry picked at #75901 |
* Add build plugins to inject design token fallbacks * Add changelog * Harden esbuild and Vite token fallback plugins * Fix lint errors in .d.mts type declaration files * Add code comments addressing review feedback * Add token fallback plugin to compileStyles * Use static token map in Stack for build-time fallback injection Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: aduth <aduth@git.wordpress.org> # Conflicts: # packages/theme/CHANGELOG.md
* Add build plugins to inject design token fallbacks * Add changelog * Harden esbuild and Vite token fallback plugins * Fix lint errors in .d.mts type declaration files * Add code comments addressing review feedback * Add token fallback plugin to compileStyles * Use static token map in Stack for build-time fallback injection Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: aduth <aduth@git.wordpress.org> # Conflicts: # packages/theme/CHANGELOG.md
Improved the token validity lint rule in #75905 to prevent these cases. |
* Theme: Add design token fallback generation (#75586) * Add build-time fallback generation for design system tokens * Add calc() test and cross-reference comments for synced files * Add border-width-focus fallback to --wp-admin-border-width-focus * Hoist primary seed OKLCH/OKLab to module-level constants * Add unit tests for optimalMixPercentage and computeBrandFallback * Throw on brand tokens with alpha channel * Support alpha channel in brand token fallbacks * Revert "Support alpha channel in brand token fallbacks" This reverts commit eecfe36. * Trim tests to alpha guard only * Update fallbacks with new cursor token Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: ciampo <mciampini@git.wordpress.org> * Theme: Add build plugins to inject design token fallbacks (#75589) * Add build plugins to inject design token fallbacks * Add changelog * Harden esbuild and Vite token fallback plugins * Fix lint errors in .d.mts type declaration files * Add code comments addressing review feedback * Add token fallback plugin to compileStyles * Use static token map in Stack for build-time fallback injection Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: aduth <aduth@git.wordpress.org> # Conflicts: # packages/theme/CHANGELOG.md * Regenerate fallbacks to exclude newer token --------- Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: youknowriad <youknowriad@git.wordpress.org> Co-authored-by: aduth <aduth@git.wordpress.org>
|
When manually cherry-picked, the labels should be updated manually as well |


Fix (part 1/2) for https://core.trac.wordpress.org/ticket/64698
What?
Stacked on #75586
Adds PostCSS, esbuild, and Vite build plugins that inject fallback values into CSS and JS files using the token fallback map from #75586.
Why?
#75586 generates a map of
--wpds-*tokens to their fallback values, but nothing consumes it yet. This PR wires up the actual injection — transformingvar(--wpds-color-fg-default)intovar(--wpds-color-fg-default, #1e1e1e)at build time, both in stylesheets and in JS string literals.How?
Three thin plugin wrappers, all importing the same
addFallbackToVarfunction from #75586:postcss-ds-token-fallbacks.mjs) — walks CSS declarations, replaces barevar(--wpds-*)with fallback-injected versions. Used bywp-buildfor CSS and by Storybook via Vite's PostCSS integration.esbuild-ds-token-fallbacks.mjs) —onLoadhook that transforms JS/TS source files containing--wpds-references. Used bywp-buildfor JS transpilation.vite-ds-token-fallbacks.mjs) —transformhook, same idea as the esbuild plugin but for Vite's pipeline. Used by Storybook.Each plugin skips files that don't contain
--wpds-for zero overhead on unrelated modules.Integration points
wp-build(packages/wp-build/lib/build.mjs) — both the PostCSS and esbuild plugins are loaded as optional dependencies (@wordpress/themeis an optional peerDependency ofwp-build). Fails gracefully if not installed.storybook/main.ts) — the Vite plugin is added toviteFinalplugins, and the PostCSS plugin is added tocss.postcss.plugins.@wordpress/themeis added as a devDependency of the storybook package.Testing Instructions
Here is some test code to see the replacement in action:
The test code adds a DS token usage in
@wordpress/componentsprimaryButton, and disables all the design tokens stylesheet injection in Storybook. The modifiedButton, as well as all the@wordpress/uicomponents, should render with their fallback values even with the design tokens stylesheet unavailable in Storybook.You can also test in the Gutenberg app, for example by rendering a
@wordpress/uicomponent in the UI, or adding a design token to any component code.