diff --git a/.stylelintrc b/.stylelintrc index 785b95b9e0a3..5d17fb02d11a 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -3,15 +3,17 @@ "plugins": [ "@signal-noise/stylelint-scales", "@wordpress/theme/stylelint-plugins/no-unknown-ds-tokens", - "@wordpress/theme/stylelint-plugins/no-setting-wpds-custom-properties" + "@wordpress/theme/stylelint-plugins/no-setting-wpds-custom-properties", + "@wordpress/theme/stylelint-plugins/no-token-fallback-values" ], "reportInvalidScopeDisables": true, "reportNeedlessDisables": true, "rules": { "at-rule-empty-line-before": null, "at-rule-no-unknown": null, - "plugin-wpds/no-unknown-ds-tokens": null, - "plugin-wpds/no-setting-wpds-custom-properties": null, + "plugin-wpds/no-unknown-ds-tokens": true, + "plugin-wpds/no-setting-wpds-custom-properties": true, + "plugin-wpds/no-token-fallback-values": true, "comment-empty-line-before": null, "font-weight-notation": null, "no-descending-specificity": null, diff --git a/apps/blaze-dashboard/webpack.config.js b/apps/blaze-dashboard/webpack.config.js index 55707702e927..008108b58675 100644 --- a/apps/blaze-dashboard/webpack.config.js +++ b/apps/blaze-dashboard/webpack.config.js @@ -1,5 +1,6 @@ const path = require( 'path' ); const process = require( 'process' ); // eslint-disable-line +const { getPostCssPlugins } = require( '@automattic/calypso-build/postcss-plugins' ); const FileConfig = require( '@automattic/calypso-build/webpack/file-loader' ); const Minify = require( '@automattic/calypso-build/webpack/minify' ); const SassConfig = require( '@automattic/calypso-build/webpack/sass' ); @@ -12,7 +13,6 @@ const { defaultRequestToExternal, defaultRequestToHandle, } = require( '@wordpress/dependency-extraction-webpack-plugin/lib/util' ); -const autoprefixerPlugin = require( 'autoprefixer' ); const webpack = require( 'webpack' ); const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); const cacheIdentifier = require( '../../build-tools/babel/babel-loader-cache-identifier' ); @@ -95,7 +95,7 @@ module.exports = { // This is required because Calypso imports `@automattic/notifications` and that package defines its // own `postcss.config.js` that they use for their webpack bundling process. config: false, - plugins: [ autoprefixerPlugin() ], + plugins: getPostCssPlugins(), }, prelude: `@use '${ require.resolve( 'calypso/assets/stylesheets/shared/_utils.scss' diff --git a/apps/odyssey-stats/webpack.config.js b/apps/odyssey-stats/webpack.config.js index 473931d9fcb3..609af8aabb02 100644 --- a/apps/odyssey-stats/webpack.config.js +++ b/apps/odyssey-stats/webpack.config.js @@ -1,5 +1,6 @@ const path = require( 'path' ); const process = require( 'process' ); // eslint-disable-line +const { getPostCssPlugins } = require( '@automattic/calypso-build/postcss-plugins' ); const FileConfig = require( '@automattic/calypso-build/webpack/file-loader' ); const Minify = require( '@automattic/calypso-build/webpack/minify' ); const SassConfig = require( '@automattic/calypso-build/webpack/sass' ); @@ -12,7 +13,6 @@ const { defaultRequestToExternal, defaultRequestToHandle, } = require( '@wordpress/dependency-extraction-webpack-plugin/lib/util' ); -const autoprefixerPlugin = require( 'autoprefixer' ); const webpack = require( 'webpack' ); const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); const cacheIdentifier = require( '../../build-tools/babel/babel-loader-cache-identifier' ); @@ -99,7 +99,7 @@ module.exports = { // This is required because Calypso imports `@automattic/notifications` and that package defines its // own `postcss.config.js` that they use for their webpack bundling process. config: false, - plugins: [ autoprefixerPlugin() ], + plugins: getPostCssPlugins(), }, prelude: `@use '${ require.resolve( 'calypso/assets/stylesheets/shared/_utils.scss' diff --git a/client/components/jetpack/jetpack-footer/style.scss b/client/components/jetpack/jetpack-footer/style.scss index 03fee967aaaf..4f95f137a12a 100644 --- a/client/components/jetpack/jetpack-footer/style.scss +++ b/client/components/jetpack/jetpack-footer/style.scss @@ -2,18 +2,18 @@ .jetpack-footer { display: flex; - border-top: var(--wpds-border-width-xs, 1px) solid var(--wpds-color-stroke-surface-neutral-weak, #e0e0e0); + border-top: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak); box-sizing: border-box; - color: var(--wpds-color-fg-content-neutral, #1e1e1e); - font-size: var(--wpds-font-size-md, 13px); + color: var(--wpds-color-fg-content-neutral); + font-size: var(--wpds-typography-font-size-md); padding: var(--wpds-dimension-padding-xl) var(--wpds-dimension-padding-2xl); width: 100%; .jetpack-footer__menu-item { - font-family: var(--wpds-font-family-body); - font-size: var(--wpds-font-size-sm, 12px); - font-weight: var(--wpds-font-weight-regular, 400); - line-height: var(--wpds-font-line-height-xs, 16px); + font-family: var(--wpds-typography-font-family-body); + font-size: var(--wpds-typography-font-size-sm); + font-weight: var(--wpds-typography-font-weight-regular); + line-height: var(--wpds-typography-line-height-xs); text-underline-offset: 0.2em; &:any-link, @@ -44,10 +44,10 @@ } .jetpack-footer__logo-text { - font-family: var(--wpds-font-family-body); - font-size: var(--wpds-font-size-md); - font-weight: var(--wpds-font-weight-regular); - line-height: var(--wpds-font-line-height-sm); + font-family: var(--wpds-typography-font-family-body); + font-size: var(--wpds-typography-font-size-md); + font-weight: var(--wpds-typography-font-weight-regular); + line-height: var(--wpds-typography-line-height-sm); } a.jetpack-footer__a8c { diff --git a/client/lib/color-scheme/dark-theme.scss b/client/lib/color-scheme/dark-theme.scss index 5c2a438e7bd0..ce8f89c9a836 100644 --- a/client/lib/color-scheme/dark-theme.scss +++ b/client/lib/color-scheme/dark-theme.scss @@ -79,9 +79,11 @@ This mixin generates alias variables for a color palette. For example, the --wp-components-color-accent-darker-10: var( --studio-blue-90 ); --wp-components-color-accent-darker-20: var( --studio-blue-100 ); --wp-components-color-accent-inverted: #fff; + /* stylelint-disable plugin-wpds/no-setting-wpds-custom-properties -- Dark theme maps WPDS brand tokens to the Calypso accent palette. */ --wpds-color-fg-interactive-brand: var( --wp-components-color-accent ); --wpds-color-fg-interactive-brand-active: var( --wp-components-color-accent-darker-20 ); --wpds-color-stroke-focus-brand: var( --wp-components-color-accent ); + /* stylelint-enable plugin-wpds/no-setting-wpds-custom-properties */ --wp-components-color-gray-800: #e0e0e0; --wp-components-color-gray-700: #bdbdbd; --wp-components-color-gray-600: #8a8a8a; diff --git a/client/my-sites/earn/style.scss b/client/my-sites/earn/style.scss index 24e930117cbb..41a7f9bc87be 100644 --- a/client/my-sites/earn/style.scss +++ b/client/my-sites/earn/style.scss @@ -19,8 +19,8 @@ body.is-section-jetpack-monetize { .section-nav { background: none; box-shadow: none; - padding: 0 var(--wpds-dimension-padding-2xl, 24px); - border-bottom: var(--wpds-border-width-xs, 1px) solid var(--wpds-color-stroke-surface-neutral-weak, #e0e0e0); + padding: 0 var(--wpds-dimension-padding-2xl); + border-bottom: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak); } } diff --git a/client/my-sites/podcast/style.scss b/client/my-sites/podcast/style.scss index 96e21c556fb8..b8e1770b4ef9 100644 --- a/client/my-sites/podcast/style.scss +++ b/client/my-sites/podcast/style.scss @@ -9,12 +9,12 @@ body.is-section-podcasting { // which Calypso's unlayered button reset overrides. Restore the package // defaults (16px horizontal padding) and add a separator under the tab bar. .podcast__tabs-bar { - border-block-end: 1px solid var( --wpds-color-stroke-base-tertiary, #dcdcde ); + border-block-end: 1px solid var( --wpds-color-stroke-surface-neutral ); margin-block-end: 24px; } .podcast__tabs [role='tab'] { - padding-inline: var( --wpds-dimension-padding-lg, 16px ); + padding-inline: var( --wpds-dimension-padding-lg ); } .podcast__section-header { @@ -29,14 +29,14 @@ body.is-section-podcasting { font-size: 28px; font-weight: 400; line-height: 1.2; - color: var( --wpds-color-fg-content-neutral, #1e1e1e ); + color: var( --wpds-color-fg-content-neutral ); } .podcast__section-description { margin: 0; font-size: 13px; line-height: 1.4; - color: var( --wpds-color-fg-content-neutral-weak, #757575 ); + color: var( --wpds-color-fg-content-neutral-weak ); } .podcast__directory-list { @@ -47,7 +47,7 @@ body.is-section-podcasting { .podcast__directory-row { padding-block: 12px; - border-block-end: 1px solid var( --wpds-color-stroke-base-tertiary, #dcdcde ); + border-block-end: 1px solid var( --wpds-color-stroke-surface-neutral ); &:last-child { border-block-end: none; @@ -109,8 +109,8 @@ body.is-section-podcasting { justify-content: center; flex-direction: column; gap: 4px; - border: 2px dashed var( --wpds-color-stroke-base-tertiary, #c3c4c7 ); - background: var( --wpds-color-bg-surface-base-secondary, #f6f7f7 ); + border: 2px dashed var( --wpds-color-stroke-surface-neutral ); + background: var( --wpds-color-bg-surface-neutral-weak ); transition: border-color 0.15s ease, background-color 0.15s ease; &::before { @@ -118,13 +118,13 @@ body.is-section-podcasting { font-size: 28px; font-weight: 400; line-height: 1; - color: var( --wpds-color-fg-content-neutral-weak, #757575 ); + color: var( --wpds-color-fg-content-neutral-weak ); } &:hover:not( :disabled ), &:focus-visible { - border-color: var( --wpds-color-stroke-status-info-strong, #007cba ); - background: var( --wpds-color-bg-surface-base, #fff ); + border-color: var( --wpds-color-stroke-surface-info-strong ); + background: var( --wpds-color-bg-surface-neutral-strong ); } } @@ -132,7 +132,7 @@ body.is-section-podcasting { .podcast-cover-image-setting__placeholder { font-style: normal; font-size: 12px; - color: var( --wpds-color-fg-content-neutral-weak, #757575 ); + color: var( --wpds-color-fg-content-neutral-weak ); } .podcast__settings-cover @@ -205,7 +205,7 @@ body.is-section-podcasting { inset-block-start: 0; inset-inline-end: 16px; padding: 4px 10px; - background: var( --wpds-color-bg-content-neutral-medium, #50575e ); + background: var( --wpds-color-bg-interactive-neutral-strong ); color: #fff; font-size: 10px; font-weight: 600; @@ -216,7 +216,7 @@ body.is-section-podcasting { z-index: 1; &.is-recommended { - background: var( --wpds-color-bg-status-success-strong, #1a8d36 ); + background: var( --wpds-color-stroke-surface-success-strong ); } } @@ -249,7 +249,7 @@ body.is-section-podcasting { background: radial-gradient( circle at 0% 0%, var( --studio-blue-0, #f0f6fc ) 0%, transparent 50% ), radial-gradient( circle at 100% 100%, var( --studio-purple-0, #fdf3fc ) 0%, transparent 50% ), - var( --wpds-color-bg-surface-base, #fff ); + var( --wpds-color-bg-surface-neutral-strong ); @media ( max-width: 900px ) { grid-template-columns: 1fr; @@ -298,7 +298,7 @@ body.is-section-podcasting { .podcast__welcome-benefit-icon { display: inline-flex; align-items: center; - color: var( --wpds-color-fg-content-neutral, #1e1e1e ); + color: var( --wpds-color-fg-content-neutral ); svg { inline-size: 24px; @@ -331,7 +331,7 @@ body.is-section-podcasting { inline-size: 32px; block-size: 32px; border-radius: 50%; - background: var( --wpds-color-bg-content-neutral-strong, #2c3338 ); + background: var( --wpds-color-bg-interactive-neutral-strong ); color: #fff; font-size: 14px; font-weight: 600; @@ -349,7 +349,7 @@ body.is-section-podcasting { .podcast__submit-step { padding: 16px; - background: var( --wpds-color-bg-surface-tertiary, #f6f7f7 ); + background: var( --wpds-color-bg-surface-neutral-weak ); border-radius: 8px; } @@ -357,7 +357,7 @@ body.is-section-podcasting { margin: 0; font-size: 14px; font-weight: 600; - color: var( --wpds-color-fg-content-neutral, #1e1e1e ); + color: var( --wpds-color-fg-content-neutral ); } .podcast__submit-step-row { @@ -387,14 +387,14 @@ body.is-section-podcasting { flex: 1; min-width: 0; padding: 8px 12px; - background: var( --wpds-color-bg-surface, #fff ); - border: 1px solid var( --wpds-color-border-neutral, #ddd ); + background: var( --wpds-color-bg-surface-neutral-strong ); + border: 1px solid var( --wpds-color-stroke-surface-neutral ); border-radius: 4px; } .podcast__submit-step-saved-icon { flex-shrink: 0; - color: var( --wpds-color-fg-content-success, #007a3d ); + color: var( --wpds-color-fg-content-success ); // Gutenberg's Icon paths use fill="currentColor"; force any stray fill="none" // or hard-coded colors to inherit so the success token actually wins. diff --git a/client/my-sites/stats/style.scss b/client/my-sites/stats/style.scss index 43fdc082259a..588603393c6e 100644 --- a/client/my-sites/stats/style.scss +++ b/client/my-sites/stats/style.scss @@ -154,7 +154,7 @@ $font-sf-pro-display: "SF Pro Display", $sans; // `@media (max-width: $break-small)` rule. @media (min-width: ($break-small + 1px)) { background: var(--wpds-color-bg-surface-neutral-strong); - padding-inline: var(--wpds-dimension-padding-2xl, 24px); + padding-inline: var(--wpds-dimension-padding-2xl); } } } diff --git a/client/webpack.config.js b/client/webpack.config.js index 41751784e4cb..ab264a50d57f 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -3,6 +3,7 @@ */ const path = require( 'path' ); +const { getPostCssPlugins } = require( '@automattic/calypso-build/postcss-plugins' ); const FileConfig = require( '@automattic/calypso-build/webpack/file-loader' ); const Minify = require( '@automattic/calypso-build/webpack/minify' ); const SassConfig = require( '@automattic/calypso-build/webpack/sass' ); @@ -15,7 +16,6 @@ const ExtensiveLodashReplacementPlugin = require( '@automattic/webpack-extensive const InlineConstantExportsPlugin = require( '@automattic/webpack-inline-constant-exports-plugin' ); const ReactRefreshWebpackPlugin = require( '@pmmmwh/react-refresh-webpack-plugin' ); const SentryCliPlugin = require( '@sentry/webpack-plugin' ); -const autoprefixerPlugin = require( 'autoprefixer' ); const CircularDependencyPlugin = require( 'circular-dependency-plugin' ); const Dotenv = require( 'dotenv-webpack' ); const DuplicatePackageCheckerPlugin = require( 'duplicate-package-checker-webpack-plugin' ); @@ -296,7 +296,7 @@ const webpackConfig = { // This is required because Calypso imports `@automattic/notifications` and that package defines its // own `postcss.config.js` that they use for their webpack bundling process. config: false, - plugins: [ autoprefixerPlugin() ], + plugins: getPostCssPlugins(), }, prelude: `@use 'calypso/assets/stylesheets/shared/_utils.scss' as *;`, } ), diff --git a/packages/calypso-build/package.json b/packages/calypso-build/package.json index 07ddc1cf45d1..c5e6756c7d01 100644 --- a/packages/calypso-build/package.json +++ b/packages/calypso-build/package.json @@ -42,6 +42,7 @@ "@swc/core": "^1.11.24", "@types/webpack-env": "^1.18.8", "@wordpress/browserslist-config": "^6.46.0", + "@wordpress/theme": "^0.13.0", "@wordpress/dependency-extraction-webpack-plugin": "^6.24.0", "autoprefixer": "^10.4.21", "babel-loader": "^8.2.3", diff --git a/packages/calypso-build/postcss-plugins.js b/packages/calypso-build/postcss-plugins.js new file mode 100644 index 000000000000..1d0b32015c31 --- /dev/null +++ b/packages/calypso-build/postcss-plugins.js @@ -0,0 +1,25 @@ +const dsTokenFallbacks = + require( '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks' ).default; +const autoprefixer = require( 'autoprefixer' ); +const postcssCustomProperties = require( 'postcss-custom-properties' ); + +/** + * Returns the PostCSS plugins used across Calypso style builds. + * + * @param {Object} options + * @param {boolean} options.customProperties Whether to include `postcss-custom-properties`. + * @returns {import('postcss').AcceptedPlugin[]} + */ +function getPostCssPlugins( { customProperties = false } = {} ) { + const plugins = []; + + if ( customProperties ) { + plugins.push( postcssCustomProperties() ); + } + + plugins.push( dsTokenFallbacks(), autoprefixer() ); + + return plugins; +} + +module.exports = { getPostCssPlugins }; diff --git a/packages/calypso-build/postcss.config.js b/packages/calypso-build/postcss.config.js index 56b3b2d73505..f59b2e62d15d 100644 --- a/packages/calypso-build/postcss.config.js +++ b/packages/calypso-build/postcss.config.js @@ -1,6 +1,5 @@ +const { getPostCssPlugins } = require( './postcss-plugins' ); + module.exports = () => ( { - plugins: { - 'postcss-custom-properties': {}, - autoprefixer: {}, - }, + plugins: getPostCssPlugins( { customProperties: true } ), } );