diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index d5df4223ed002e..31b65b81d9f68c 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -8,6 +8,7 @@ ### Enhancements +- The `no-unknown-ds-tokens` rule now reports bare `--wpds-*` tokens not wrapped in `var()`, which would silently miss build-time fallback injection. - The `no-setting-ds-tokens` rule now checks all object property keys, not just those inside JSX `style` attributes ([#76212](https://github.com/WordPress/gutenberg/pull/76212)). ## 24.3.0 (2026-03-04) @@ -44,7 +45,7 @@ ### Enhancements -- The `dependency-group` rule is not recommended anymore. ([#73616](https://github.com/WordPress/gutenberg/pull/73616)) +- The `dependency-group` rule is not recommended anymore. ([#73616](https://github.com/WordPress/gutenberg/pull/73616)) ## 22.22.0 (2025-11-26) diff --git a/packages/eslint-plugin/docs/rules/no-unknown-ds-tokens.md b/packages/eslint-plugin/docs/rules/no-unknown-ds-tokens.md index 8293473cbd3a2c..8b6b53a5d60f4e 100644 --- a/packages/eslint-plugin/docs/rules/no-unknown-ds-tokens.md +++ b/packages/eslint-plugin/docs/rules/no-unknown-ds-tokens.md @@ -4,6 +4,8 @@ When using Design System tokens (CSS custom properties beginning with `--wpds-`) Additionally, token names must not be dynamically constructed (e.g. via template literal expressions), as they cannot be statically verified for correctness or processed automatically to inject fallbacks. +Tokens must also be wrapped in `var()` syntax (e.g. `var(--wpds-color-fg-content-neutral)`). The build tooling relies on this pattern to inject fallback values so that components render correctly without a ThemeProvider. Bare token references like `'--wpds-color-fg-content-neutral'` will not receive fallbacks. + This rule lints all string literals and template literals in JavaScript/TypeScript files. For CSS files, use the [corresponding Stylelint rule](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-theme/#stylelint-plugins) from the `@wordpress/theme` package. ## Rule details @@ -27,6 +29,11 @@ const token = 'var(--wpds-nonexistent-token)'; const token = `var(--wpds-dimension-gap-${ size })`; ``` +```js +// Bare tokens without var() won't receive build-time fallbacks. +const token = '--wpds-color-fg-content-neutral'; +``` + Examples of **correct** code for this rule: ```jsx diff --git a/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js b/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js index 0b345744d3dc41..64d094a579487b 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js +++ b/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js @@ -39,6 +39,9 @@ ruleTester.run( 'no-unknown-ds-tokens', rule, { { code: '`var(--wpds-color-fg-content-neutral) ${ suffix }`', }, + { + code: `const style = { '--wpds-color-fg-content-neutral': 'red' };`, + }, ], invalid: [ { @@ -142,5 +145,60 @@ ruleTester.run( 'no-unknown-ds-tokens', rule, { }, ], }, + { + code: `const token = '--wpds-color-fg-content-neutral';`, + errors: [ + { + messageId: 'bareToken', + data: { + tokenNames: "'--wpds-color-fg-content-neutral'", + }, + }, + ], + }, + { + code: 'const token = `--wpds-color-fg-content-neutral`;', + errors: [ + { + messageId: 'bareToken', + data: { + tokenNames: "'--wpds-color-fg-content-neutral'", + }, + }, + ], + }, + { + code: '
', + errors: [ + { + messageId: 'bareToken', + data: { + tokenNames: "'--wpds-color-fg-content-neutral'", + }, + }, + ], + }, + { + code: '`${ prefix }: --wpds-color-fg-content-neutral`', + errors: [ + { + messageId: 'bareToken', + data: { + tokenNames: "'--wpds-color-fg-content-neutral'", + }, + }, + ], + }, + { + code: '`var(--wpds-color-fg-content-neutral) --wpds-color-fg-content-neutral ${ x }`', + errors: [ + { + messageId: 'bareToken', + data: { + tokenNames: "'--wpds-color-fg-content-neutral'", + }, + }, + ], + }, ], } ); diff --git a/packages/eslint-plugin/rules/no-unknown-ds-tokens.js b/packages/eslint-plugin/rules/no-unknown-ds-tokens.js index aa7bb9d280ede3..b374ebdcfd745d 100644 --- a/packages/eslint-plugin/rules/no-unknown-ds-tokens.js +++ b/packages/eslint-plugin/rules/no-unknown-ds-tokens.js @@ -4,35 +4,41 @@ const tokenList = tokenListModule.default || tokenListModule; const DS_TOKEN_PREFIX = 'wpds-'; /** - * Extracts all unique CSS custom properties (variables) from a given CSS value string, - * including those in fallback positions, optionally filtering by a specific prefix. + * Single-pass extraction that finds all `--prefix-*` tokens in a CSS value + * string and classifies each occurrence as `var()`-wrapped or bare. * - * @param {string} value - The CSS value string to search for variables. + * @param {string} value - The CSS value string to search. * @param {string} [prefix=''] - Optional prefix to filter variables (e.g., 'wpds-'). - * @return {Set