Skip to content
Merged
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/base-styles/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- Add `outset-ring__focus` mixin for outline-based focus rings using `--wpds-*` design tokens ([#78698](https://github.com/WordPress/gutenberg/pull/78698)).

## 10.0.1 (2026-06-16)

## 10.0.0 (2026-06-10)
Expand Down
14 changes: 14 additions & 0 deletions packages/base-styles/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

@use "sass:color";
@use "sass:math";
@use "@wordpress/theme/src/prebuilt/scss/design-token-fallbacks" as wpds;
@use "./variables";
@use "./colors";
@use "./breakpoints";
Expand Down Expand Up @@ -180,6 +181,19 @@
}


/**
* Standard focus rings for the WordPress Design System.

* Apply `outset-ring__focus` inside the relevant pseudo-class at the call site,
* e.g. `&:focus { @include outset-ring__focus(); }`.
*/

@mixin outset-ring__focus() {
Comment thread
ciampo marked this conversation as resolved.
outline: wpds.wpds-var("--wpds-border-width-focus") solid wpds.wpds-var("--wpds-color-stroke-focus");
outline-offset: wpds.wpds-var("--wpds-border-width-focus");
}


/**
* Applies editor left position to the selector passed as argument
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/base-styles/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
"./z-index": "./_z-index.scss"
},
"wpScript": true,
"dependencies": {
"@wordpress/theme": "file:../theme"
},
"publishConfig": {
"access": "public"
}
Expand Down
1 change: 1 addition & 0 deletions packages/theme/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Add `--wpds-border-radius-xl` for page and app shell surfaces so nested cards and notices can stay on `--wpds-border-radius-lg` without matching the parent radius ([#78913](https://github.com/WordPress/gutenberg/pull/78913)).
- Add `cornerRadius` prop to `ThemeProvider` for configuring the border-radius preset (`none`, `subtle`, `moderate`, `pronounced`) via prebuilt design token modes. [#78816](https://github.com/WordPress/gutenberg/pull/78816).
- Add disabled variants for the `brand` and `error` tones of the interactive `background`, `foreground`, and `stroke` color tokens (e.g. `--wpds-color-stroke-interactive-brand-disabled`, `--wpds-color-background-interactive-brand-strong-disabled`), for parity with the existing `neutral` disabled tokens ([#79124](https://github.com/WordPress/gutenberg/pull/79124)).
- Export `@wordpress/theme/design-token-fallbacks` with a generated `wpds-var()` Sass function for compile-time design token fallback injection ([#78698](https://github.com/WordPress/gutenberg/pull/78698)).

### Enhancements

Expand Down
38 changes: 35 additions & 3 deletions packages/theme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ If your application renders React content into additional documents (an iframe,

### Developer Tools

For the best development experience, we recommend configuring the [build plugins](#build-plugins) and [Stylelint rules](#stylelint-plugins) provided by this package. The build plugins automatically inject fallback values into `var(--wpds-*)` references so components render correctly even when the tokens stylesheet is not yet loaded, and will raise an error if a reference does not match a known token. The Stylelint rules catch typos, unknown tokens, and other discouraged patterns during development.
For the best development experience, we recommend configuring the [Stylelint rules](#stylelint-plugins) provided by this package. The Stylelint rules catch typos, unknown tokens, and other discouraged patterns during development.

If you use `@wordpress/build` to build your scripts, the build plugins are already enabled by default.
If you reference `--wpds-*` tokens in CSS or JS/TS source, use the [build plugins](#build-plugins) to inject fallback values at build time so components render correctly even when the tokens stylesheet is not loaded. If you use `@wordpress/build`, these plugins are already enabled by default when `@wordpress/theme` is installed.

If you write Sass/SCSS but cannot load the design tokens stylesheet, add build plugins to your pipeline, or use `@wordpress/build`, use the [Sass helper](#sass-helper) instead.

## Theme Provider

Expand Down Expand Up @@ -228,7 +230,9 @@ This rule reports an error when a CSS declaration sets (defines) a custom proper

### `plugin-wpds/no-token-fallback-values`

This rule reports an error when a `var()` call for a `--wpds-*` token includes a manual fallback value. Fallback values for design tokens are injected automatically at build time by the [build plugins](#build-plugins), so manual fallbacks in source are redundant and can drift out of sync with the token definitions.
This rule reports an error when a `var()` call for a `--wpds-*` token includes a manual fallback value. In CSS processed by the [build plugins](#build-plugins), fallback values are injected automatically, so manual fallbacks in `var(--wpds-*)` references are redundant and can drift out of sync with the token definitions.

For Sass sources that cannot use the build plugins, use the [Sass helper](#sass-helper) instead of writing manual fallback values.

```css
/* ✗ Error: Do not add a fallback value for Design System token '--wpds-color-foreground-content-neutral' */
Expand Down Expand Up @@ -304,6 +308,34 @@ export default defineConfig( {
} );
```

## Sass helper

An alternative to loading the [design tokens stylesheet](#outside-wordpress), using the [build plugins](#build-plugins), or `@wordpress/build`. Use it when you write Sass/SCSS and cannot change your build setup to support any of those options.

You do not need this helper if you already load the design tokens stylesheet or use fallback injection via the build plugins or `@wordpress/build`. In those cases, write bare `var(--wpds-*)` references in your source.

The `@wordpress/theme/design-token-fallbacks` export provides a generated `wpds-var()` function that wraps token references with the same fallback map used by the build plugins — for example, `wpds.wpds-var('--wpds-color-fg-content-neutral')` compiles to `var(--wpds-color-fg-content-neutral, #1e1e1e)`.

```scss
@use '@wordpress/theme/design-token-fallbacks' as wpds;

.example {
color: wpds.wpds-var( '--wpds-color-fg-content-neutral' );
}
```

When using Sass with [`NodePackageImporter`](https://sass-lang.com/documentation/js-api/classes/nodepackageimporter/), you can import via the `pkg:` scheme:

```scss
@use 'pkg:@wordpress/theme/design-token-fallbacks' as wpds;
```

For Sass setups that resolve packages from `node_modules` with a load path, use:

```scss
@use '@wordpress/theme/src/prebuilt/scss/design-token-fallbacks' as wpds;
```

## Contributing to this package

This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects.
Expand Down
67 changes: 58 additions & 9 deletions packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,50 @@ export function computeBrandFallback( stepHex: string ): string {
}%, ${ mixWith })`;
}

function escapeScssString( value: string ): string {
return value.replace( /\\/g, '\\\\' ).replace( /'/g, "\\'" );
}

export function formatDesignTokenFallbacksScss(
fallbacks: Record< string, string >
): string {
const mapEntries = Object.entries( fallbacks )
.map( ( [ token, fallback ] ) => {
if ( fallback.includes( '#{' ) ) {
throw new Error(
`Sass interpolation syntax is not supported in design token fallbacks: ${ token }`
);
}

return `\t'${ token }': '${ escapeScssString( fallback ) }'`;
} )
.join( ',\n' );
Comment thread
mirka marked this conversation as resolved.

return [
'// This file is generated by the @wordpress/terrazzo-plugin-ds-token-fallbacks plugin.',
'// Do not edit this file directly.',
'',
'@use "sass:map";',
'@use "sass:string";',
'',
'$fallbacks: (',
mapEntries,
');',
'',
'@function wpds-var($token) {',
'\t@if not map.has-key($fallbacks, $token) {',
'\t\t@error "Unknown design token: #{$token}";',
'\t}',
'',
'\t@return string.unquote("var(#{$token}, #{map.get($fallbacks, $token)})");',
'}',
'',
].join( '\n' );
}

export default function pluginDsTokenFallbacks( {
filename = 'js/design-token-fallbacks.mjs',
scssFilename = 'scss/design-token-fallbacks.scss',
} = {} ): Plugin {
return {
name: '@wordpress/terrazzo-plugin-ds-token-fallbacks',
Expand Down Expand Up @@ -280,17 +322,24 @@ export default function pluginDsTokenFallbacks( {
)
);

const generatedHeader = [
'/*',
' * This file is generated by the @wordpress/terrazzo-plugin-ds-token-fallbacks plugin.',
' * Do not edit this file directly.',
' */',
'',
].join( '\n' );

outputFile(
filename,
[
'/*',
' * This file is generated by the @wordpress/terrazzo-plugin-ds-token-fallbacks plugin.',
' * Do not edit this file directly.',
' */',
'',
`export default ${ JSON.stringify( sorted, null, '\t' ) }`,
'',
].join( '\n' )
generatedHeader +
`export default ${ JSON.stringify( sorted, null, '\t' ) }` +
'\n'
);

outputFile(
scssFilename,
formatDesignTokenFallbacksScss( sorted )
);
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computeBrandFallback } from '../index';
import { computeBrandFallback, formatDesignTokenFallbacksScss } from '../index';

jest.mock( '@terrazzo/plugin-css', () => ( { FORMAT_ID: 'css/value' } ) );

Expand All @@ -15,3 +15,47 @@ describe( 'computeBrandFallback', () => {
);
} );
} );

describe( 'formatDesignTokenFallbacksScss', () => {
it( 'generates a Sass map and wpds-var function', () => {
const scss = formatDesignTokenFallbacksScss( {
'--wpds-border-radius-sm': '2px',
'--wpds-border-width-focus':
'var(--wp-admin-border-width-focus, 2px)',
'--wpds-typography-font-family-mono':
'"Menlo", "Consolas", monaco, monospace',
} );

expect( scss ).toContain( '@use "sass:map";' );
expect( scss ).toContain( '@function wpds-var($token)' );
expect( scss ).toContain( "'--wpds-border-radius-sm': '2px'" );
expect( scss ).toContain(
"'--wpds-border-width-focus': 'var(--wp-admin-border-width-focus, 2px)'"
);
expect( scss ).toContain(
'\'--wpds-typography-font-family-mono\': \'"Menlo", "Consolas", monaco, monospace\''
Comment thread
mirka marked this conversation as resolved.
);
} );

it( 'escapes backslashes and single quotes', () => {
const scss = formatDesignTokenFallbacksScss( {
'--wpds-x-single-quote': "a'b",
'--wpds-x-backslash': 'a\\b',
'--wpds-x-both': "a'\\b",
} );

expect( scss ).toContain( "'--wpds-x-single-quote': 'a\\'b'" );
expect( scss ).toContain( "'--wpds-x-backslash': 'a\\\\b'" );
expect( scss ).toContain( "'--wpds-x-both': 'a\\'\\\\b'" );
} );

it( 'throws on Sass interpolation syntax', () => {
expect( () =>
formatDesignTokenFallbacksScss( {
'--wpds-x-interpolation': 'calc(#{some-value} * 1px)',
} )
).toThrow(
/Sass interpolation syntax is not supported in design token fallbacks: --wpds-x-interpolation/
);
} );
} );
1 change: 1 addition & 0 deletions packages/theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"default": "./build/index.cjs"
},
"./design-tokens.css": "./src/prebuilt/css/design-tokens.css",
"./design-token-fallbacks": "./src/prebuilt/scss/design-token-fallbacks.scss",
"./design-tokens.js": {
"types": "./build-types/prebuilt/js/design-tokens.d.ts",
"import": "./build-module/prebuilt/js/design-tokens.mjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* This file is generated by the @wordpress/terrazzo-plugin-ds-token-fallbacks plugin.
* Do not edit this file directly.
*/

export default {
'--wpds-border-radius-lg': '8px',
'--wpds-border-radius-md': '4px',
Expand Down
Loading
Loading