Commit f369eeb2a78 for woocommerce

commit f369eeb2a7818cd43771fd18ce09c65b8aad7854
Author: Mikael Korpela <mikael@ihminen.org>
Date:   Tue Oct 6 23:09:45 2026 +0300

    Enable WPDS stylelint rules and build-time token fallbacks (#69250)

diff --git a/package.json b/package.json
index ccaa9a81793..3490566cfe2 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
 		"@woocommerce/monorepo-utils": "workspace:*",
 		"@wordpress/prettier-config": "4.49.0",
 		"@wordpress/stylelint-config": "catalog:tooling",
+		"@wordpress/theme": "catalog:wp-bundled",
 		"babel-loader": "9.2.x",
 		"chalk": "^4.1.2",
 		"copy-webpack-plugin": "13.0.x",
diff --git a/packages/js/internal-build/changelog/add-stylelint-wpds-plugins b/packages/js/internal-build/changelog/add-stylelint-wpds-plugins
new file mode 100644
index 00000000000..217b1529473
--- /dev/null
+++ b/packages/js/internal-build/changelog/add-stylelint-wpds-plugins
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add WPDS design token fallbacks to built CSS with the `@wordpress/theme` PostCSS plugin.
diff --git a/packages/js/internal-build/package.json b/packages/js/internal-build/package.json
index 1bd456cadb4..0ccff5203c6 100644
--- a/packages/js/internal-build/package.json
+++ b/packages/js/internal-build/package.json
@@ -59,6 +59,7 @@
 		"@automattic/color-studio": "^4.0.0",
 		"@wordpress/base-styles": "catalog:wp-bundled",
 		"@wordpress/postcss-plugins-preset": "catalog:wp-min",
+		"@wordpress/theme": "catalog:wp-bundled",
 		"chokidar": "^3.6.0",
 		"css-loader": "6.11.x",
 		"esbuild": "^0.24.0",
diff --git a/packages/js/internal-build/src/style-build/index.cjs b/packages/js/internal-build/src/style-build/index.cjs
index e44643a20e8..dc0f28ef6f7 100644
--- a/packages/js/internal-build/src/style-build/index.cjs
+++ b/packages/js/internal-build/src/style-build/index.cjs
@@ -5,6 +5,10 @@ const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );
 const path = require( 'path' );
 const RemoveEmptyScriptsPlugin = require( 'webpack-remove-empty-scripts' );
 const postcssPlugins = require( '@wordpress/postcss-plugins-preset' );
+// ESM-only; Node's require( esm ) returns the module namespace.
+const {
+	default: dsTokenFallbacks,
+} = require( '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks' );
 const StyleAssetPlugin = require( './style-asset-plugin.cjs' );
 const WebpackRTLPlugin = require( './webpack-rtl-plugin.cjs' );

@@ -31,7 +35,10 @@ module.exports = {
 						loader: 'postcss-loader',
 						options: {
 							postcssOptions: {
-								plugins: postcssPlugins,
+								plugins: [
+									...postcssPlugins,
+									dsTokenFallbacks,
+								],
 							},
 						},
 					},
diff --git a/plugins/woocommerce/changelog/add-stylelint-wpds-plugins b/plugins/woocommerce/changelog/add-stylelint-wpds-plugins
new file mode 100644
index 00000000000..7265be54962
--- /dev/null
+++ b/plugins/woocommerce/changelog/add-stylelint-wpds-plugins
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Lint WPDS design token usage and add token fallbacks at build time; no merchant-facing change.
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/update-banner.scss b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/update-banner.scss
index ea82bd90f90..cd35fdeaa39 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/update-banner.scss
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/email-editor-integration/update-banner.scss
@@ -2,8 +2,8 @@
  * <UpdateBanner> — RSM-141.
  *
  * Floating card pinned 16px from the canvas top, centered horizontally.
- * Uses WPDS tokens via CSS custom properties with sensible WP-admin
- * fallbacks; no custom design tokens introduced.
+ * Uses WPDS tokens via CSS custom properties; fallbacks are added at
+ * build time. No custom design tokens introduced.
  */

 .wc-update-banner {
@@ -12,12 +12,12 @@
 	left: 50%;
 	transform: translateX(-50%);
 	width: min(720px, calc(100% - 32px));
-	background: var(--wpds-color-bg-surface, #fff);
-	border-radius: var(--wpds-border-radius-sm, 2px);
+	background: var(--wpds-color-background-surface-neutral-strong);
+	border-radius: var(--wpds-border-radius-sm);
 	box-shadow:
-		var(--wpds-elevation-md, 0 1px 2px rgba(0, 0, 0, 0.06)),
+		var(--wpds-elevation-md),
 		0 4px 12px rgba(0, 0, 0, 0.08);
-	border-left: 3px solid var(--wpds-color-bg-interactive-brand-strong, #3858e9);
+	border-left: 3px solid var(--wpds-color-stroke-surface-brand-strong);
 	padding: 12px 16px;
 	display: flex;
 	gap: 16px;
@@ -39,7 +39,7 @@
 	&__subtitle {
 		margin-top: 4px;
 		font-size: 12px;
-		color: var(--wpds-color-text-secondary, #757575);
+		color: var(--wpds-color-foreground-content-neutral-weak);
 	}

 	&__expand {
@@ -48,7 +48,7 @@
 		border: 0;
 		padding: 4px 0 0 0;
 		margin-top: 6px;
-		color: var(--wpds-color-bg-interactive-brand-strong, #3858e9);
+		color: var(--wpds-color-foreground-interactive-brand);
 		cursor: pointer;
 		font-size: 12px;
 		font-weight: 500;
@@ -76,7 +76,7 @@
 		font-size: 16px;
 		line-height: 1;
 		cursor: pointer;
-		color: var(--wpds-color-text-secondary, #757575);
+		color: var(--wpds-color-foreground-content-neutral-weak);

 		&:disabled {
 			opacity: 0.5;
@@ -86,13 +86,13 @@

 	/* Success morph */
 	&--success {
-		background: var(--wpds-color-bg-surface-success-weak, #ebffed);
-		border-left-color: var(--wpds-color-bg-interactive-success-strong, #00ba37);
+		background: var(--wpds-color-background-surface-success-weak);
+		border-left-color: var(--wpds-color-stroke-surface-success-strong);
 	}

 	/* Failure morph */
 	&--failure {
-		background: var(--wpds-color-bg-surface-warning, #fde6be);
+		background: var(--wpds-color-background-surface-warning);
 		border-left-color: #b26200;
 	}
 }
diff --git a/plugins/woocommerce/client/admin/client/wp-admin-scripts/settings-embed/settings-ui.scss b/plugins/woocommerce/client/admin/client/wp-admin-scripts/settings-embed/settings-ui.scss
index 566cefc56cc..c261e4fc7d1 100644
--- a/plugins/woocommerce/client/admin/client/wp-admin-scripts/settings-embed/settings-ui.scss
+++ b/plugins/woocommerce/client/admin/client/wp-admin-scripts/settings-embed/settings-ui.scss
@@ -50,7 +50,7 @@ body.woocommerce_page_wc-settings.woocommerce-settings-ui-page {
 			var(--wpds-color-stroke-surface-neutral-weak);
 		display: flex;
 		flex-direction: column;
-		gap: var(--wpds-dimension-gap-sm, 8px);
+		gap: var(--wpds-dimension-gap-sm);
 		padding: var(--wpds-dimension-padding-2xl) 48px;
 	}

@@ -73,7 +73,7 @@ body.woocommerce_page_wc-settings.woocommerce-settings-ui-page {
 	.wc-settings-ui-shell__header-actions {
 		display: flex;
 		flex: 0 0 auto;
-		gap: var(--wpds-dimension-gap-sm, 8px);
+		gap: var(--wpds-dimension-gap-sm);
 		margin-inline-start: auto;
 	}

@@ -84,13 +84,13 @@ body.woocommerce_page_wc-settings.woocommerce-settings-ui-page {

 	.wc-settings-ui-shell__badge {
 		align-items: center;
-		border-radius: var(--wpds-border-radius-lg, 8px);
+		border-radius: var(--wpds-border-radius-lg);
 		display: inline-flex;
-		font-size: var(--wpds-typography-font-size-sm, 12px);
-		font-weight: var(--wpds-typography-font-weight-regular, 400);
-		line-height: var(--wpds-typography-line-height-xs, 16px);
+		font-size: var(--wpds-typography-font-size-sm);
+		font-weight: var(--wpds-typography-font-weight-regular);
+		line-height: var(--wpds-typography-line-height-xs);
 		min-height: 24px;
-		padding: 0 var(--wpds-dimension-padding-sm, 8px);
+		padding: 0 var(--wpds-dimension-padding-sm);

 		&,
 		&--default {
@@ -208,7 +208,7 @@ body.woocommerce_page_wc-settings.woocommerce-settings-ui-page {
 		box-sizing: border-box;
 		display: flex;
 		flex-direction: column;
-		gap: var(--wpds-dimension-gap-xl, 24px);
+		gap: var(--wpds-dimension-gap-xl);
 		margin: var(--wpds-dimension-gap-2xl) auto 48px;
 		max-width: var(--wpds-dimension-surface-width-xl);
 	}
diff --git a/plugins/woocommerce/client/admin/stylelint.config.js b/plugins/woocommerce/client/admin/stylelint.config.js
index b511266cde6..ec78cd4fae9 100644
--- a/plugins/woocommerce/client/admin/stylelint.config.js
+++ b/plugins/woocommerce/client/admin/stylelint.config.js
@@ -3,8 +3,5 @@ module.exports = {
 	ignoreFiles: [ './vendor/**/*.scss' ],
 	rules: {
 		'scss/load-partial-extension': 'always',
-		// Enabled by the preset since @wordpress/stylelint-config 23.x.
-		// TODO: re-enable once update-banner.scss uses valid WPDS tokens.
-		'plugin-wpds/no-unknown-ds-tokens': null,
 	},
 };
diff --git a/plugins/woocommerce/client/blocks/.stylelintrc.json b/plugins/woocommerce/client/blocks/.stylelintrc.json
index 4bd2e23b83d..275eb527ebf 100644
--- a/plugins/woocommerce/client/blocks/.stylelintrc.json
+++ b/plugins/woocommerce/client/blocks/.stylelintrc.json
@@ -1,7 +1,11 @@
 {
 	"extends": "@wordpress/stylelint-config/scss-stylistic",
+	"plugins": [
+		"@wordpress/theme/stylelint-plugins/no-token-fallback-values"
+	],
 	"reportNeedlessDisables": true,
 	"rules": {
+		"plugin-wpds/no-token-fallback-values": true,
 		"no-descending-specificity": null,
 		"no-duplicate-selectors": null,
 		"selector-class-pattern": null,
diff --git a/plugins/woocommerce/client/blocks/package.json b/plugins/woocommerce/client/blocks/package.json
index 21784600bf3..830e008f167 100644
--- a/plugins/woocommerce/client/blocks/package.json
+++ b/plugins/woocommerce/client/blocks/package.json
@@ -155,6 +155,7 @@
 		"@wordpress/rich-text": "catalog:wp-min",
 		"@wordpress/scripts": "32.6.0",
 		"@wordpress/stylelint-config": "catalog:tooling",
+		"@wordpress/theme": "catalog:wp-bundled",
 		"ajv-cli": "3.3.x",
 		"allure-playwright": "^2.9.2",
 		"autoprefixer": "10.4.14",
diff --git a/plugins/woocommerce/client/blocks/postcss.config.js b/plugins/woocommerce/client/blocks/postcss.config.js
index 3c3296d5805..1c5dc0dd5bb 100644
--- a/plugins/woocommerce/client/blocks/postcss.config.js
+++ b/plugins/woocommerce/client/blocks/postcss.config.js
@@ -1,5 +1,6 @@
 module.exports = ( { env } ) => ( {
 	plugins: {
+		'@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks': {},
 		autoprefixer: { grid: true },
 		cssnano: env === 'production',
 	},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6d4b3a20712..470118a333c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -269,6 +269,9 @@ importers:
       '@wordpress/stylelint-config':
         specifier: catalog:tooling
         version: 23.42.0(@types/react@18.3.28)(esbuild@0.24.2)(postcss@8.5.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint-scss@6.14.0(stylelint@16.26.1(typescript@5.7.3)))(stylelint@16.26.1(typescript@5.7.3))
+      '@wordpress/theme':
+        specifier: catalog:wp-bundled
+        version: 0.17.0(@types/react@18.3.28)(esbuild@0.24.2)(postcss@8.5.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint@16.26.1(typescript@5.7.3))
       babel-loader:
         specifier: 9.2.x
         version: 9.2.1(@babel/core@7.25.7)(webpack@5.97.1(@swc/core@1.15.24))
@@ -1748,6 +1751,9 @@ importers:
       '@wordpress/postcss-plugins-preset':
         specifier: catalog:wp-min
         version: 5.33.1(postcss@8.5.26)
+      '@wordpress/theme':
+        specifier: catalog:wp-bundled
+        version: 0.17.0(@types/react@18.3.28)(esbuild@0.24.2)(postcss@8.5.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint@16.26.1(typescript@5.7.3))
       chokidar:
         specifier: ^3.6.0
         version: 3.6.0
@@ -3608,6 +3614,9 @@ importers:
       '@wordpress/stylelint-config':
         specifier: catalog:tooling
         version: 23.36.0(postcss@8.5.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint-scss@6.14.0(stylelint@16.26.1(typescript@5.7.3)))(stylelint@16.26.1(typescript@5.7.3))
+      '@wordpress/theme':
+        specifier: catalog:wp-bundled
+        version: 0.17.0(@types/react@18.3.28)(esbuild@0.24.2)(postcss@8.5.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(stylelint@16.26.1(typescript@5.7.3))
       ajv-cli:
         specifier: 3.3.x
         version: 3.3.0
diff --git a/stylelint.config.cjs b/stylelint.config.cjs
index 4fbcafcf779..1ec096d5b9a 100644
--- a/stylelint.config.cjs
+++ b/stylelint.config.cjs
@@ -4,6 +4,7 @@
  */
 module.exports = {
 	extends: '@wordpress/stylelint-config/scss-stylistic',
+	plugins: [ '@wordpress/theme/stylelint-plugins/no-token-fallback-values' ],
 	reportNeedlessDisables: true,
 	ignoreFiles: [
 		'docs/**',
@@ -32,6 +33,7 @@ module.exports = {
 		'scss/selector-no-redundant-nesting-selector': null,
 		'selector-id-pattern': null,
 		'length-zero-no-unit': [ true, { ignoreFunctions: [ 'calc', 'var' ] } ],
+		'plugin-wpds/no-token-fallback-values': true,
 	},
 	overrides: [
 		{