From dd4826071769b5ad8f119937e2e9c6ba0afbbc48 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Fri, 17 Jul 2026 11:13:32 +0300 Subject: [PATCH 1/4] Charts: inject WPDS fallbacks during build --- projects/js-packages/charts/AGENTS.md | 4 +- .../update-charts-wpds-token-fallbacks | 4 ++ projects/js-packages/charts/package.json | 2 + projects/js-packages/charts/postcss.config.js | 6 ++ .../charts/area-chart/area-chart.module.scss | 4 +- .../charts/bar-chart/bar-chart.module.scss | 8 +-- .../conversion-funnel-chart.module.scss | 70 +++++++++---------- .../heatmap-chart/heatmap-chart.module.scss | 42 +++++------ .../leaderboard-chart.module.scss | 46 ++++++------ .../charts/line-chart/line-chart.module.scss | 30 ++++---- .../pie-chart/stories/donut.stories.tsx | 2 +- .../pie-semi-circle-chart.module.scss | 6 +- .../grid-control/grid-control.module.scss | 2 +- .../svg-empty-state.module.scss | 4 +- .../src/charts/private/x-zoom.module.scss | 16 ++--- .../legend/private/base-legend.module.scss | 10 +-- .../tooltip/base-tooltip.module.scss | 8 +-- .../trend-indicator.module.scss | 10 +-- .../src/providers/chart-context/themes.ts | 34 ++++----- .../charts/src/stories/chart-decorator.tsx | 2 +- .../charts/tools/postcss-css-bundle.ts | 43 ++++++++++++ projects/js-packages/charts/tsdown.config.ts | 7 +- 22 files changed, 210 insertions(+), 150 deletions(-) create mode 100644 projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks create mode 100644 projects/js-packages/charts/postcss.config.js create mode 100644 projects/js-packages/charts/tools/postcss-css-bundle.ts diff --git a/projects/js-packages/charts/AGENTS.md b/projects/js-packages/charts/AGENTS.md index 700b44384d61..24872d63079c 100644 --- a/projects/js-packages/charts/AGENTS.md +++ b/projects/js-packages/charts/AGENTS.md @@ -24,7 +24,7 @@ jp changelog add js-packages/charts -s patch -t changed -e "Charts: )`, `var(--wpds-border-*, )`, and `var(--wpds-typography-*, )` instead of hardcoded px values for spacing, padding, margins, border radius, border width, font size, and font weight. Fallbacks must match the WPDS spec value for that token — do not invent fallback values. +- **Design tokens (WPDS).** In SCSS and theme JS, use bare `var(--wpds-*)` tokens (no hardcoded fallbacks). Official fallbacks are injected at build time via `@wordpress/theme`'s PostCSS plugin (`postcss.config.js` + `postcssCssBundle` in `tsdown.config.ts`) and the Vite `vite-ds-token-fallbacks` plugin for JS/TS theme strings. Do not invent fallback values. - **UI primitives.** Prefer `Stack` and the stable `Text` from `@wordpress/ui` over ad-hoc flexbox or raw ``/`
` for layout and text. Do not use `__experimental*` exports from `@wordpress/components` (e.g. `__experimentalText`, `__experimentalHStack`) — use the stable `@wordpress/ui` equivalents. Exception: `__experimentalGrid` has no stable alternative yet and is acceptable to use for now. - **Theming.** Theming flows through `@wordpress/theme`'s `ThemeProvider` (unlocked via private APIs in Storybook; see `src/stories/chart-decorator.tsx`). Do not manually override DS tokens in stories or components to achieve theming — pass a color through `ThemeProvider` instead. - **Chart element styles.** Read chart element styles via `getElementStyles` from `GlobalChartsProvider`, not directly from `theme`. This is the supported path for color/style resolution across themes. @@ -55,7 +55,7 @@ The package is migrating to WordPress UI and Theme as its defaults. When adding - Using ad-hoc flexbox layouts where established layout primitives (e.g. `Stack` from `@wordpress/ui`) should be preferred. - Accessing colors/styles directly from `theme` rather than using `getElementStyles` from `GlobalChartsProvider`. - Hardcoding px values in SCSS for spacing, borders, or typography where a WPDS token (`--wpds-dimension-*`, `--wpds-border-*`, `--wpds-typography-*`) exists. -- CSS variable fallback values that diverge from the WPDS spec for that token. +- Hardcoding CSS variable fallback values for `--wpds-*` tokens (prefer bare `var(--wpds-*)`; build injects official fallbacks). - Using `__experimental*` exports from `@wordpress/components` (e.g. `__experimentalText`, `__experimentalHStack`) instead of the stable `@wordpress/ui` equivalents. (`__experimentalGrid` is excepted — no stable alternative exists yet.) - Manually overriding DS tokens in stories or components to achieve theming instead of passing a color through `@wordpress/theme`'s `ThemeProvider`. - Responsive wrappers that conflict with component sizing semantics (fixed-height charts, resize behavior, aspect-ratio assumptions). diff --git a/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks b/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks new file mode 100644 index 000000000000..a6aa5afa224c --- /dev/null +++ b/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks @@ -0,0 +1,4 @@ +Significance: patch +Type: changed +Comment: Emit DS token fallbacks via PostCSS/tsdown and drop hardcoded fallbacks from Charts styles and theme JS. + diff --git a/projects/js-packages/charts/package.json b/projects/js-packages/charts/package.json index dc7ca32f567b..8ab49751a169 100644 --- a/projects/js-packages/charts/package.json +++ b/projects/js-packages/charts/package.json @@ -119,11 +119,13 @@ "@wordpress/components": "37.0.0", "@wordpress/element": "8.3.0", "@wordpress/private-apis": "1.51.0", + "autoprefixer": "10.4.20", "babel-jest": "30.4.1", "identity-obj-proxy": "^3.0.0", "jest": "30.4.2", "jest-extended": "7.0.0", "magic-string": "0.30.21", + "postcss": "8.5.14", "react": "18.3.1", "react-dom": "18.3.1", "sass-embedded": "1.97.3", diff --git a/projects/js-packages/charts/postcss.config.js b/projects/js-packages/charts/postcss.config.js new file mode 100644 index 000000000000..0a4a36cc0cb5 --- /dev/null +++ b/projects/js-packages/charts/postcss.config.js @@ -0,0 +1,6 @@ +import postcssDsTokenFallbacks from '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks'; +import autoprefixer from 'autoprefixer'; + +export default () => ( { + plugins: [ postcssDsTokenFallbacks, autoprefixer ], +} ); diff --git a/projects/js-packages/charts/src/charts/area-chart/area-chart.module.scss b/projects/js-packages/charts/src/charts/area-chart/area-chart.module.scss index 7dbc4338fcce..51db992cce8a 100644 --- a/projects/js-packages/charts/src/charts/area-chart/area-chart.module.scss +++ b/projects/js-packages/charts/src/charts/area-chart/area-chart.module.scss @@ -7,8 +7,8 @@ transform-origin: 0 95%; transform: scaleY(0); animation: - rise var(--wpds-motion-duration-xl, 400ms) - var(--wpds-motion-easing-expressive, cubic-bezier(0.25, 0, 0, 1)) forwards; + rise var(--wpds-motion-duration-xl) + var(--wpds-motion-easing-expressive) forwards; } } diff --git a/projects/js-packages/charts/src/charts/bar-chart/bar-chart.module.scss b/projects/js-packages/charts/src/charts/bar-chart/bar-chart.module.scss index 4d02b09aee29..ee828ef66def 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/bar-chart.module.scss +++ b/projects/js-packages/charts/src/charts/bar-chart/bar-chart.module.scss @@ -9,8 +9,8 @@ transform-box: fill-box; transform: scaleY(0); animation: - rise var(--wpds-motion-duration-xl, 400ms) - var(--wpds-motion-easing-expressive, cubic-bezier(0.25, 0, 0, 1)) forwards; + rise var(--wpds-motion-duration-xl) + var(--wpds-motion-easing-expressive) forwards; } @keyframes rise { @@ -25,8 +25,8 @@ transform-box: fill-box; transform: scaleX(0); animation: - stretch var(--wpds-motion-duration-xl, 400ms) - var(--wpds-motion-easing-expressive, cubic-bezier(0.25, 0, 0, 1)) forwards; + stretch var(--wpds-motion-duration-xl) + var(--wpds-motion-easing-expressive) forwards; } @keyframes stretch { diff --git a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss index 1072b6c0db22..f33e2280ed04 100644 --- a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +++ b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss @@ -12,22 +12,22 @@ .main-rate { overflow: hidden; - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); + color: var(--wpds-color-foreground-content-neutral); text-overflow: ellipsis; - font-size: var(--wpds-typography-font-size-xl, 18px); + font-size: var(--wpds-typography-font-size-xl); font-style: normal; - font-weight: var(--wpds-typography-font-weight-medium, 499); - line-height: var(--wpds-typography-line-height-sm, 20px); + font-weight: var(--wpds-typography-font-weight-medium); + line-height: var(--wpds-typography-line-height-sm); margin: 0; } .change-indicator { overflow: hidden; text-overflow: ellipsis; - font-size: var(--wpds-typography-font-size-md, 13px); + font-size: var(--wpds-typography-font-size-md); font-style: normal; - font-weight: var(--wpds-typography-font-weight-medium, 499); - line-height: var(--wpds-typography-line-height-sm, 20px); + font-weight: var(--wpds-typography-font-weight-medium); + line-height: var(--wpds-typography-line-height-sm); margin: 0; } @@ -44,8 +44,8 @@ &--animated { transition: - opacity var(--wpds-motion-duration-lg, 300ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)); + opacity var(--wpds-motion-duration-lg) + var(--wpds-motion-easing-subtle); } &--blurred { @@ -54,25 +54,25 @@ } .step-label { - color: var(--wpds-color-foreground-content-neutral-weak, #707070); - 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); + color: var(--wpds-color-foreground-content-neutral-weak); + font-size: var(--wpds-typography-font-size-sm); + font-weight: var(--wpds-typography-font-weight-regular); + line-height: var(--wpds-typography-line-height-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .step-rate { - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); - font-size: var(--wpds-typography-font-size-md, 13px); - font-weight: var(--wpds-typography-font-weight-medium, 499); - line-height: var(--wpds-typography-line-height-xs, 16px); + color: var(--wpds-color-foreground-content-neutral); + font-size: var(--wpds-typography-font-size-md); + font-weight: var(--wpds-typography-font-weight-medium); + line-height: var(--wpds-typography-line-height-xs); } .bar-container { flex: 1; - border-radius: var(--wpds-border-radius-md, 4px); + border-radius: var(--wpds-border-radius-md); position: relative; cursor: pointer; } @@ -80,15 +80,15 @@ .funnel-bar { width: 100%; min-height: 4px; - border-radius: var(--wpds-border-radius-md, 4px) var(--wpds-border-radius-md, 4px) 0 0; + border-radius: var(--wpds-border-radius-md) var(--wpds-border-radius-md) 0 0; &--animated { transform-origin: bottom; transform-box: fill-box; transform: scaleY(0); animation: - stretch var(--wpds-motion-duration-xl, 400ms) - var(--wpds-motion-easing-expressive, cubic-bezier(0.25, 0, 0, 1)) forwards; + stretch var(--wpds-motion-duration-xl) + var(--wpds-motion-easing-expressive) forwards; } @keyframes stretch { @@ -100,33 +100,33 @@ } .tooltip-wrapper { - background: var(--wpds-color-background-surface-neutral-strong, #fff); + background: var(--wpds-color-background-surface-neutral-strong); // Override .visx-tooltip inline styles. - border-radius: var(--wpds-border-radius-md, 4px) !important; - padding: var(--wpds-dimension-padding-md, 12px) !important; - box-shadow: var(--wpds-elevation-sm, 0 1px 2px 0 #0000000d, 0 2px 3px 0 #0000000a, 0 6px 6px 0 #00000008, 0 8px 8px 0 #00000005) !important; + border-radius: var(--wpds-border-radius-md) !important; + padding: var(--wpds-dimension-padding-md) !important; + box-shadow: var(--wpds-elevation-sm) !important; } .tooltip-title { - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); - font-size: var(--wpds-typography-font-size-sm, 12px); + color: var(--wpds-color-foreground-content-neutral); + font-size: var(--wpds-typography-font-size-sm); font-style: normal; - font-weight: var(--wpds-typography-font-weight-regular, 400); - line-height: var(--wpds-typography-line-height-xs, 16px); + font-weight: var(--wpds-typography-font-weight-regular); + line-height: var(--wpds-typography-line-height-xs); } .tooltip-content { - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); - font-size: var(--wpds-typography-font-size-md, 13px); + color: var(--wpds-color-foreground-content-neutral); + font-size: var(--wpds-typography-font-size-md); font-style: normal; - font-weight: var(--wpds-typography-font-weight-medium, 499); - line-height: var(--wpds-typography-line-height-sm, 20px); + font-weight: var(--wpds-typography-font-weight-medium); + line-height: var(--wpds-typography-line-height-sm); } .empty-state { text-align: center; - color: var(--wpds-color-foreground-content-neutral-weak, #707070); - font-size: var(--wpds-typography-font-size-lg, 16px); + color: var(--wpds-color-foreground-content-neutral-weak); + font-size: var(--wpds-typography-font-size-lg); } diff --git a/projects/js-packages/charts/src/charts/heatmap-chart/heatmap-chart.module.scss b/projects/js-packages/charts/src/charts/heatmap-chart/heatmap-chart.module.scss index 646ed10575c4..cd1b0fd59163 100644 --- a/projects/js-packages/charts/src/charts/heatmap-chart/heatmap-chart.module.scss +++ b/projects/js-packages/charts/src/charts/heatmap-chart/heatmap-chart.module.scss @@ -2,7 +2,7 @@ width: 100%; height: 100%; min-height: 0; - font-size: var(--wpds-typography-font-size-sm, 12px); + font-size: var(--wpds-typography-font-size-sm); } // Height-capped grids are content-sized vertically, so the wrapper hugs them @@ -13,15 +13,15 @@ } .heatmap-chart__empty { - padding: var(--wpds-dimension-padding-lg, 16px); - color: var(--wpds-color-foreground-content-neutral-weak, #707070); + padding: var(--wpds-dimension-padding-lg); + color: var(--wpds-color-foreground-content-neutral-weak); } // Track template is set inline because CSS `repeat()` won't take a `var()` // count. ARIA rows are `display: contents` so their cells join this grid. .heatmap-chart__grid { display: grid; - gap: var(--heatmap-cell-gap, var(--wpds-dimension-gap-xs, 4px)); + gap: var(--heatmap-cell-gap, var(--wpds-dimension-gap-xs)); flex: 1 1 0; width: 100%; // Override flex's auto minimum so short widgets do not scroll. @@ -29,10 +29,10 @@ &:focus-visible { outline: - var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px)) solid - var(--wpds-color-stroke-focus, var(--wp-admin-theme-color, #3858e9)); + var(--wpds-border-width-focus) solid + var(--wpds-color-stroke-focus); outline-offset: 2px; - border-radius: var(--wpds-border-radius-sm, 2px); + border-radius: var(--wpds-border-radius-sm); } } @@ -58,8 +58,8 @@ .heatmap-chart__col-label, .heatmap-chart__row-label { - color: var(--wpds-color-foreground-content-neutral-weak, #707070); - font-size: var(--wpds-typography-font-size-xs, 11px); + color: var(--wpds-color-foreground-content-neutral-weak); + font-size: var(--wpds-typography-font-size-xs); white-space: nowrap; overflow: visible; } @@ -73,7 +73,7 @@ align-self: center; justify-self: end; text-align: right; - padding-inline-end: var(--wpds-dimension-padding-xs, 4px); + padding-inline-end: var(--wpds-dimension-padding-xs); } .heatmap-chart__cell { @@ -82,9 +82,9 @@ justify-content: center; min-width: 0; min-height: 0; - border-radius: var(--wpds-border-radius-sm, 2px); + border-radius: var(--wpds-border-radius-sm); // Empty (no-data) default; cells with a value override it below. - background: var(--wpds-color-background-track-neutral-weak, #f0f0f0); + background: var(--wpds-color-background-track-neutral-weak); } // Floor the mix at 15% so the lowest value stays visibly tinted, distinct from @@ -95,25 +95,25 @@ .heatmap-chart__cell--selected { outline: - var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px)) solid - var(--wpds-color-stroke-focus, var(--wp-admin-theme-color, #3858e9)); - outline-offset: calc(-1 * var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px))); + var(--wpds-border-width-focus) solid + var(--wpds-color-stroke-focus); + outline-offset: calc(-1 * var(--wpds-border-width-focus)); } .heatmap-chart__cell-value { - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); - font-size: var(--wpds-typography-font-size-md, 13px); + color: var(--wpds-color-foreground-content-neutral); + font-size: var(--wpds-typography-font-size-md); } // Light text on fills dark enough to out-contrast dark text (decided in JS from // the blended fill luminance). .heatmap-chart__cell--strong .heatmap-chart__cell-value { - color: var(--wpds-color-foreground-interactive-neutral-strong, #f0f0f0); + color: var(--wpds-color-foreground-interactive-neutral-strong); } .heatmap-chart__legend-swatch { - width: var(--wpds-dimension-size-3xs, 12px); - height: var(--wpds-dimension-size-3xs, 12px); - border-radius: var(--wpds-border-radius-sm, 2px); + width: var(--wpds-dimension-size-3xs); + height: var(--wpds-dimension-size-3xs); + border-radius: var(--wpds-border-radius-sm); background: color-mix(in sRGB, var(--heatmap-primary) calc((0.15 + 0.85 * var(--intensity)) * 100%), var(--heatmap-bg, #fff)); } diff --git a/projects/js-packages/charts/src/charts/leaderboard-chart/leaderboard-chart.module.scss b/projects/js-packages/charts/src/charts/leaderboard-chart/leaderboard-chart.module.scss index a23b7bfe398e..f45ffda527a6 100644 --- a/projects/js-packages/charts/src/charts/leaderboard-chart/leaderboard-chart.module.scss +++ b/projects/js-packages/charts/src/charts/leaderboard-chart/leaderboard-chart.module.scss @@ -1,7 +1,7 @@ .leaderboardChart { transition: - opacity var(--wpds-motion-duration-lg, 300ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)); + opacity var(--wpds-motion-duration-lg) + var(--wpds-motion-easing-subtle); &--responsive { height: 100%; @@ -40,7 +40,7 @@ } .label { - padding-left: var(--wpds-dimension-padding-sm, 8px); + padding-left: var(--wpds-dimension-padding-sm); } } @@ -49,7 +49,7 @@ min-height: 6px; // Keeps a sliver of bar visible for tiny shares, and stops the interactive // row's fixed-pixel hover inset from collapsing a narrow bar to nothing. - min-width: var(--wpds-dimension-size-5xs, 4px); + min-width: var(--wpds-dimension-size-5xs); border-radius: var(--a8c--charts--leaderboard--bar--border-radius, 9999px); z-index: -1; /* places it behind the label */ @@ -59,8 +59,8 @@ transform-box: fill-box; transform: scaleX(0); animation: - stretch var(--wpds-motion-duration-xl, 400ms) - var(--wpds-motion-easing-expressive, cubic-bezier(0.25, 0, 0, 1)) forwards; + stretch var(--wpds-motion-duration-xl) + var(--wpds-motion-easing-expressive) forwards; } @keyframes stretch { @@ -93,15 +93,15 @@ } .emptyState { - padding: var(--wpds-dimension-padding-3xl, 32px) var(--wpds-dimension-padding-lg, 16px); + padding: var(--wpds-dimension-padding-3xl) var(--wpds-dimension-padding-lg); text-align: center; - color: var(--wpds-color-foreground-content-neutral-weak, #707070); - font-size: var(--wpds-typography-font-size-md, 13px); + color: var(--wpds-color-foreground-content-neutral-weak); + font-size: var(--wpds-typography-font-size-md); font-style: italic; } .interactiveRow { - --focus-ring-width: var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px)); + --focus-ring-width: var(--wpds-border-width-focus); display: grid; grid-column: 1 / -1; @@ -135,16 +135,16 @@ // value slide is given an RTL override below. No row background. .bar { transition: - opacity var(--wpds-motion-duration-sm, 100ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)), - width var(--wpds-motion-duration-sm, 100ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)); + opacity var(--wpds-motion-duration-sm) + var(--wpds-motion-easing-subtle), + width var(--wpds-motion-duration-sm) + var(--wpds-motion-easing-subtle); } .valueContainer { transition: - transform var(--wpds-motion-duration-sm, 100ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)); + transform var(--wpds-motion-duration-sm) + var(--wpds-motion-easing-subtle); } &:hover, @@ -152,7 +152,7 @@ // Fixed reserve for the chevron — the value slides by it in full, and the // bar width inset (see leaderboard-chart.tsx) scales it by the bar's share. - --a8c--charts--leaderboard--bar--hover-inset: var(--wpds-dimension-gap-2xl, 32px); + --a8c--charts--leaderboard--bar--hover-inset: var(--wpds-dimension-gap-2xl); .bar { opacity: 0.5; @@ -174,10 +174,10 @@ } &:focus-visible { - border-radius: var(--wpds-border-radius-sm, 2px); + border-radius: var(--wpds-border-radius-sm); outline: var(--focus-ring-width) solid - var(--wpds-color-stroke-focus, var(--wp-admin-theme-color, #3858e9)); + var(--wpds-color-stroke-focus); // Inset by its own width so the outer edge hugs the row boundary: the // outline then sits in the row's padding (also a ring width), clear of the // bar, and stays inside the overflow:auto container that would clip an @@ -188,15 +188,15 @@ .chevron { position: absolute; - inset-inline-end: var(--wpds-dimension-padding-xs, 4px); + inset-inline-end: var(--wpds-dimension-padding-xs); top: 0; bottom: 0; margin-block: auto; opacity: 0; - color: var(--wpds-color-foreground-content-neutral-weak, #707070); + color: var(--wpds-color-foreground-content-neutral-weak); transition: - opacity var(--wpds-motion-duration-sm, 100ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)); + opacity var(--wpds-motion-duration-sm) + var(--wpds-motion-easing-subtle); pointer-events: none; // The glyph points toward the inline end, so flip it to point left in RTL. diff --git a/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss b/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss index 4160e6719ca1..d75bcedf08c5 100644 --- a/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss +++ b/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss @@ -7,8 +7,8 @@ transform-origin: 0 95%; transform: scaleY(0); animation: - rise var(--wpds-motion-duration-xl, 400ms) - var(--wpds-motion-easing-expressive, cubic-bezier(0.25, 0, 0, 1)) forwards; + rise var(--wpds-motion-duration-xl) + var(--wpds-motion-easing-expressive) forwards; } } @@ -18,23 +18,23 @@ &__tooltip, &__annotation-label-popover { - background: var(--wpds-color-background-surface-neutral-strong, #fff); - color: var(--wpds-color-foreground-content-neutral, #1e1e1e); - padding: var(--wpds-dimension-padding-sm, 8px); + background: var(--wpds-color-background-surface-neutral-strong); + color: var(--wpds-color-foreground-content-neutral); + padding: var(--wpds-dimension-padding-sm); } &__tooltip-date { - font-weight: var(--wpds-typography-font-weight-medium, 499); - padding-bottom: var(--wpds-dimension-padding-md, 12px); + font-weight: var(--wpds-typography-font-weight-medium); + padding-bottom: var(--wpds-dimension-padding-md); } &__tooltip-row { - padding: var(--wpds-dimension-padding-xs, 4px) 0; + padding: var(--wpds-dimension-padding-xs) 0; } &__tooltip-label { - font-weight: var(--wpds-typography-font-weight-medium, 499); - padding-right: var(--wpds-dimension-padding-lg, 16px); + font-weight: var(--wpds-typography-font-weight-medium); + padding-right: var(--wpds-dimension-padding-lg); } &__annotations-overlay { @@ -63,14 +63,14 @@ &__annotation-label-popover { min-width: 125px; border: none; - border-radius: var(--wpds-border-radius-md, 4px); - font-size: var(--wpds-typography-font-size-md, 13px); - box-shadow: var(--wpds-elevation-sm, 0 1px 2px 0 #0000000d, 0 2px 3px 0 #0000000a, 0 6px 6px 0 #00000008, 0 8px 8px 0 #00000005); + border-radius: var(--wpds-border-radius-md); + font-size: var(--wpds-typography-font-size-md); + box-shadow: var(--wpds-elevation-sm); position: fixed; // Without margin set, the popover has margin:auto which upsets the // positioning relative to the trigger button. A gap token is appropriate // because it creates a gap to the trigger button. - margin: var(--wpds-dimension-gap-sm, 8px); + margin: var(--wpds-dimension-gap-sm); visibility: hidden; &--visible { @@ -84,7 +84,7 @@ } &__annotation-label-popover-content { - padding: var(--wpds-dimension-padding-sm, 8px); + padding: var(--wpds-dimension-padding-sm); } &__annotation-label-popover-close-button { diff --git a/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx b/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx index 892eddd93806..09fd9163c9cd 100644 --- a/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx +++ b/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx @@ -263,7 +263,7 @@ const CustomPieLegend = ( { style={ { display: 'inline-grid', gridTemplateColumns: '1fr auto auto', - gap: 'var(--wpds-dimension-gap-xs, 4px) var(--wpds-dimension-gap-sm, 8px)', + gap: 'var(--wpds-dimension-gap-xs) var(--wpds-dimension-gap-sm)', } } > { items.map( ( item, index ) => { diff --git a/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss b/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss index 28919bfa9c43..6648bd3f4258 100644 --- a/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +++ b/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss @@ -6,11 +6,11 @@ } .label { - font-weight: var(--wpds-typography-font-weight-medium, 499); - font-size: var(--wpds-typography-font-size-lg, 16px); + font-weight: var(--wpds-typography-font-weight-medium); + font-size: var(--wpds-typography-font-size-lg); } .note { - font-size: var(--wpds-typography-font-size-md, 13px); + font-size: var(--wpds-typography-font-size-md); } } diff --git a/projects/js-packages/charts/src/charts/private/grid-control/grid-control.module.scss b/projects/js-packages/charts/src/charts/private/grid-control/grid-control.module.scss index cababf969113..6b0e986aea81 100644 --- a/projects/js-packages/charts/src/charts/private/grid-control/grid-control.module.scss +++ b/projects/js-packages/charts/src/charts/private/grid-control/grid-control.module.scss @@ -1,7 +1,7 @@ .grid-control { :global(.visx-line) { - stroke: var(--wpds-color-stroke-surface-neutral, #dbdbdb); + stroke: var(--wpds-color-stroke-surface-neutral); stroke-width: 1px; shape-rendering: crispEdges; } diff --git a/projects/js-packages/charts/src/charts/private/svg-empty-state/svg-empty-state.module.scss b/projects/js-packages/charts/src/charts/private/svg-empty-state/svg-empty-state.module.scss index fc8334565931..770beeb3ff3e 100644 --- a/projects/js-packages/charts/src/charts/private/svg-empty-state/svg-empty-state.module.scss +++ b/projects/js-packages/charts/src/charts/private/svg-empty-state/svg-empty-state.module.scss @@ -1,5 +1,5 @@ .svg-empty-state { text-align: center; - font-size: var(--wpds-typography-font-size-md, 13px); - color: var(--wpds-color-foreground-content-neutral-weak, #707070); + font-size: var(--wpds-typography-font-size-md); + color: var(--wpds-color-foreground-content-neutral-weak); } diff --git a/projects/js-packages/charts/src/charts/private/x-zoom.module.scss b/projects/js-packages/charts/src/charts/private/x-zoom.module.scss index e4792834299c..c71e09bb8ce9 100644 --- a/projects/js-packages/charts/src/charts/private/x-zoom.module.scss +++ b/projects/js-packages/charts/src/charts/private/x-zoom.module.scss @@ -6,14 +6,14 @@ &__selection { fill: var(--charts-zoom-selection-fill, rgba(56, 88, 233, 0.16)); stroke: var(--charts-zoom-selection-stroke, rgba(56, 88, 233, 0.65)); - stroke-width: var(--wpds-border-width-xs, 1px); + stroke-width: var(--wpds-border-width-xs); pointer-events: none; } &__reset { position: absolute; - top: var(--wpds-dimension-gap-sm, 8px); - right: var(--wpds-dimension-gap-sm, 8px); + top: var(--wpds-dimension-gap-sm); + right: var(--wpds-dimension-gap-sm); z-index: 2; display: inline-flex; align-items: center; @@ -22,20 +22,20 @@ height: 28px; padding: 0; background: var(--charts-zoom-reset-bg, rgba(255, 255, 255, 0.92)); - color: var(--charts-zoom-reset-fg, var(--wpds-color-foreground-content-neutral, #1e1e1e)); + color: var(--charts-zoom-reset-fg, var(--wpds-color-foreground-content-neutral)); border: - var(--wpds-border-width-xs, 1px) solid + var(--wpds-border-width-xs) solid var(--charts-zoom-reset-border, rgba(0, 0, 0, 0.16)); - border-radius: var(--wpds-border-radius-md, 4px); + border-radius: var(--wpds-border-radius-md); cursor: pointer; - box-shadow: var(--wpds-elevation-xs, 0 1px 1px 0 #00000008, 0 1px 2px 0 #00000005, 0 3px 3px 0 #00000005, 0 4px 4px 0 #00000003); + box-shadow: var(--wpds-elevation-xs); &:hover { background: var(--charts-zoom-reset-bg-hover, rgba(255, 255, 255, 1)); } &:focus-visible { - outline: 2px solid var(--charts-zoom-reset-focus, var(--wpds-color-stroke-focus, #3858e9)); + outline: 2px solid var(--charts-zoom-reset-focus, var(--wpds-color-stroke-focus)); outline-offset: 1px; } } diff --git a/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss b/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss index 2c41fc2f403e..beed9f8f21da 100644 --- a/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss +++ b/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss @@ -3,14 +3,14 @@ } .legend-item { - font-size: var(--wpds-typography-font-size-md, 13px); + font-size: var(--wpds-typography-font-size-md); &--interactive { cursor: pointer; user-select: none; transition: - opacity var(--wpds-motion-duration-md, 200ms) - var(--wpds-motion-easing-subtle, cubic-bezier(0.15, 0, 0.15, 1)); + opacity var(--wpds-motion-duration-md) + var(--wpds-motion-easing-subtle); &:hover { opacity: 0.8; @@ -19,7 +19,7 @@ &:focus { outline: 2px solid currentColor; outline-offset: 2px; - border-radius: var(--wpds-border-radius-md, 4px); + border-radius: var(--wpds-border-radius-md); } &:focus:not(:focus-visible) { @@ -54,6 +54,6 @@ } .legend-item-value { - font-weight: var(--wpds-typography-font-weight-medium, 499); + font-weight: var(--wpds-typography-font-weight-medium); flex-shrink: 0; // Prevent value from shrinking when text is ellipsized } diff --git a/projects/js-packages/charts/src/components/tooltip/base-tooltip.module.scss b/projects/js-packages/charts/src/components/tooltip/base-tooltip.module.scss index 961953182716..a75b186952ab 100644 --- a/projects/js-packages/charts/src/components/tooltip/base-tooltip.module.scss +++ b/projects/js-packages/charts/src/components/tooltip/base-tooltip.module.scss @@ -1,13 +1,13 @@ .tooltip { - padding: var(--wpds-dimension-padding-sm, 8px); + padding: var(--wpds-dimension-padding-sm); // No WPDS token fits a translucent dark tooltip surface: // bg-interactive-neutral-strong is opaque, and there is no // white-on-dark content foreground token. background-color: rgba(0, 0, 0, 0.85); color: #fff; - border-radius: var(--wpds-border-radius-md, 4px); - font-size: var(--wpds-typography-font-size-md, 13px); - box-shadow: var(--wpds-elevation-sm, 0 1px 2px 0 #0000000d, 0 2px 3px 0 #0000000a, 0 6px 6px 0 #00000008, 0 8px 8px 0 #00000005); + border-radius: var(--wpds-border-radius-md); + font-size: var(--wpds-typography-font-size-md); + box-shadow: var(--wpds-elevation-sm); position: absolute; pointer-events: none; transform: translate(-50%, -100%); diff --git a/projects/js-packages/charts/src/components/trend-indicator/trend-indicator.module.scss b/projects/js-packages/charts/src/components/trend-indicator/trend-indicator.module.scss index 53e5f6a8de47..6ec9e9bd99b2 100644 --- a/projects/js-packages/charts/src/components/trend-indicator/trend-indicator.module.scss +++ b/projects/js-packages/charts/src/components/trend-indicator/trend-indicator.module.scss @@ -2,22 +2,22 @@ display: inline-flex; align-items: center; gap: 0.125em; - font-size: var(--wpds-typography-font-size-md, 13px); - font-weight: var(--wpds-typography-font-weight-medium, 499); + font-size: var(--wpds-typography-font-size-md); + font-weight: var(--wpds-typography-font-weight-medium); line-height: 1; // Trend colors use the documented --charts-trend-* override API, falling // back to the accessible WPDS semantic tokens. &--up { - color: var(--charts-trend-up-color, var(--wpds-color-foreground-content-success-weak, #008030)); + color: var(--charts-trend-up-color, var(--wpds-color-foreground-content-success-weak)); } &--down { - color: var(--charts-trend-down-color, var(--wpds-color-foreground-content-error-weak, #cc1818)); + color: var(--charts-trend-down-color, var(--wpds-color-foreground-content-error-weak)); } &--neutral { - color: var(--charts-trend-neutral-color, var(--wpds-color-foreground-content-neutral-weak, #707070)); + color: var(--charts-trend-neutral-color, var(--wpds-color-foreground-content-neutral-weak)); } &__icon { diff --git a/projects/js-packages/charts/src/providers/chart-context/themes.ts b/projects/js-packages/charts/src/providers/chart-context/themes.ts index 8767f5e44b5b..d31f654d4e0b 100644 --- a/projects/js-packages/charts/src/providers/chart-context/themes.ts +++ b/projects/js-packages/charts/src/providers/chart-context/themes.ts @@ -4,24 +4,24 @@ import type { CompleteChartTheme } from '../../types'; * Default theme configuration */ const defaultTheme: CompleteChartTheme = { - backgroundColor: 'var(--wpds-color-background-surface-neutral-strong, #fff)', + backgroundColor: 'var(--wpds-color-background-surface-neutral-strong)', labelBackgroundColor: 'transparent', // label background color (transparent by default) // White label text sits on top of arbitrary series colors, so it has no WPDS // content-foreground equivalent and stays hardcoded (tokenization outlier). labelTextColor: '#FFFFFF', colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ], gridStyles: { - stroke: 'var(--wpds-color-stroke-surface-neutral, #dbdbdb)', + stroke: 'var(--wpds-color-stroke-surface-neutral)', strokeWidth: 1, }, tickLength: 4, gridColor: '', gridColorDark: '', - xTickLineStyles: { stroke: 'var(--wpds-color-stroke-surface-neutral, #dbdbdb)', strokeWidth: 1 }, - xAxisLineStyles: { stroke: 'var(--wpds-color-stroke-surface-neutral, #dbdbdb)', strokeWidth: 1 }, + xTickLineStyles: { stroke: 'var(--wpds-color-stroke-surface-neutral)', strokeWidth: 1 }, + xAxisLineStyles: { stroke: 'var(--wpds-color-stroke-surface-neutral)', strokeWidth: 1 }, legend: { labelStyles: { - color: 'var(--wpds-color-foreground-content-neutral, #1e1e1e)', + color: 'var(--wpds-color-foreground-content-neutral)', }, containerStyles: {}, shapeStyles: [], @@ -34,26 +34,26 @@ const defaultTheme: CompleteChartTheme = { // elements for axis labels and ticks. Setting `inherit` lets SVG text // pick up the host application's font-family via normal CSS inheritance. svgLabelSmall: { - fill: 'var(--wpds-color-foreground-content-neutral, #1e1e1e)', + fill: 'var(--wpds-color-foreground-content-neutral)', fontFamily: 'inherit', }, svgLabelBig: { fontFamily: 'inherit' }, annotationStyles: { label: { - anchorLineStroke: 'var(--wpds-color-foreground-content-neutral, #1e1e1e)', - backgroundFill: 'var(--wpds-color-background-surface-neutral-strong, #fff)', + anchorLineStroke: 'var(--wpds-color-foreground-content-neutral)', + backgroundFill: 'var(--wpds-color-background-surface-neutral-strong)', }, connector: { - stroke: 'var(--wpds-color-foreground-content-neutral, #1e1e1e)', + stroke: 'var(--wpds-color-foreground-content-neutral)', }, circleSubject: { stroke: 'transparent', - fill: 'var(--wpds-color-foreground-content-neutral, #1e1e1e)', + fill: 'var(--wpds-color-foreground-content-neutral)', radius: 5, }, }, geoChart: { - featureFillColor: 'var(--wpds-color-background-surface-neutral-weak, #f4f4f4)', + featureFillColor: 'var(--wpds-color-background-surface-neutral-weak)', }, leaderboardChart: { rowGap: 12, @@ -61,15 +61,15 @@ const defaultTheme: CompleteChartTheme = { labelSpacing: 'xs', // [negative, neutral, positive] deltaColors: [ - 'var(--wpds-color-foreground-content-error-weak, #cc1818)', - 'var(--wpds-color-foreground-content-neutral-weak, #707070)', - 'var(--wpds-color-foreground-content-success-weak, #008030)', + 'var(--wpds-color-foreground-content-error-weak)', + 'var(--wpds-color-foreground-content-neutral-weak)', + 'var(--wpds-color-foreground-content-success-weak)', ], }, conversionFunnelChart: { - backgroundColor: 'var(--wpds-color-background-surface-neutral-weak, #f4f4f4)', - positiveChangeColor: 'var(--wpds-color-foreground-content-success-weak, #008030)', - negativeChangeColor: 'var(--wpds-color-foreground-content-error-weak, #cc1818)', + backgroundColor: 'var(--wpds-color-background-surface-neutral-weak)', + positiveChangeColor: 'var(--wpds-color-foreground-content-success-weak)', + negativeChangeColor: 'var(--wpds-color-foreground-content-error-weak)', }, lineChart: { lineStyles: { diff --git a/projects/js-packages/charts/src/stories/chart-decorator.tsx b/projects/js-packages/charts/src/stories/chart-decorator.tsx index 661835fb27de..49e4289a892b 100644 --- a/projects/js-packages/charts/src/stories/chart-decorator.tsx +++ b/projects/js-packages/charts/src/stories/chart-decorator.tsx @@ -163,7 +163,7 @@ const StoryChartProvider = ( { */ }
diff --git a/projects/js-packages/charts/tools/postcss-css-bundle.ts b/projects/js-packages/charts/tools/postcss-css-bundle.ts new file mode 100644 index 000000000000..939fec792af4 --- /dev/null +++ b/projects/js-packages/charts/tools/postcss-css-bundle.ts @@ -0,0 +1,43 @@ +import postcss from 'postcss'; +import { type TsdownPlugin } from 'tsdown'; +import postcssConfig from '../postcss.config.js'; + +/** + * Run PostCSS (DS token fallbacks + autoprefixer) on emitted CSS assets. + * + * tsdown's `css.transformer: 'postcss'` cannot resolve `postcss-modules` under + * pnpm's isolation (tsdown dynamically imports it from its own package root). + * Keeping the default lightningcss transformer preserves CSS modules; this + * plugin injects official `--wpds-*` fallbacks into the final CSS bundle. + * + * Uses `order: 'post'` so it runs after `@tsdown/css` emits the CSS asset. + * + * @return {TsdownPlugin} The tsdown plugin. + */ +export function postcssCssBundle(): TsdownPlugin { + return { + name: 'postcss-css-bundle', + generateBundle: { + order: 'post', + async handler( _options, bundle ) { + const { plugins } = postcssConfig(); + await Promise.all( + Object.values( bundle ).map( async file => { + if ( file.type !== 'asset' || ! file.fileName.endsWith( '.css' ) ) { + return; + } + const source = + typeof file.source === 'string' + ? file.source + : Buffer.from( file.source ).toString( 'utf8' ); + if ( ! source.includes( '--wpds-' ) ) { + return; + } + const result = await postcss( plugins ).process( source, { from: undefined } ); + file.source = result.css; + } ) + ); + }, + }, + }; +} diff --git a/projects/js-packages/charts/tsdown.config.ts b/projects/js-packages/charts/tsdown.config.ts index 1cd3269e46c7..aff147369527 100644 --- a/projects/js-packages/charts/tsdown.config.ts +++ b/projects/js-packages/charts/tsdown.config.ts @@ -1,5 +1,7 @@ import { readFileSync } from 'node:fs'; +import viteDsTokenFallbacks from '@wordpress/theme/vite-plugins/vite-ds-token-fallbacks'; import { defineConfig } from 'tsdown'; +import { postcssCssBundle } from './tools/postcss-css-bundle.ts'; import { removeDataTestId } from './tools/remove-data-testid.ts'; const pkg = JSON.parse( readFileSync( new URL( './package.json', import.meta.url ), 'utf8' ) ) as { @@ -51,7 +53,10 @@ export default defineConfig( { generateScopedName: 'a8ccharts-[hash]-[local]', }, }, - plugins: [ removeDataTestId() ], + // PostCSS injects official `--wpds-*` fallbacks into the CSS bundle; + // the Vite plugin does the same for JS/TS theme strings (e.g. `themes.ts`). + // See tools/postcss-css-bundle.ts for why we don't use css.transformer: 'postcss'. + plugins: [ removeDataTestId(), viteDsTokenFallbacks(), postcssCssBundle() ], // Fail loudly once @tsdown/css stops emitting these, so the suppression can be dropped. onSuccess() { if ( ! suppressedCssSourcemapWarning ) { From 7f31d58c2ee38ddd5b4a8346eb0bc2b33546fedb Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Fri, 17 Jul 2026 11:43:37 +0300 Subject: [PATCH 2/4] Updated tokens to latest wp-theme --- .../conversion-funnel-chart.module.scss | 19 ++++++++++++------- .../charts/line-chart/line-chart.module.scss | 11 ++++++++--- .../pie-semi-circle-chart.module.scss | 2 +- .../stories/index.docs.mdx | 2 +- .../src/charts/private/x-zoom.module.scss | 7 ++++++- .../legend/private/base-legend.module.scss | 2 +- .../tooltip/base-tooltip.module.scss | 7 ++++++- .../trend-indicator.module.scss | 2 +- 8 files changed, 36 insertions(+), 16 deletions(-) diff --git a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss index f33e2280ed04..8b8922dfeb0b 100644 --- a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +++ b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss @@ -16,7 +16,7 @@ text-overflow: ellipsis; font-size: var(--wpds-typography-font-size-xl); font-style: normal; - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); line-height: var(--wpds-typography-line-height-sm); margin: 0; } @@ -26,7 +26,7 @@ text-overflow: ellipsis; font-size: var(--wpds-typography-font-size-md); font-style: normal; - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); line-height: var(--wpds-typography-line-height-sm); margin: 0; } @@ -56,7 +56,7 @@ .step-label { color: var(--wpds-color-foreground-content-neutral-weak); font-size: var(--wpds-typography-font-size-sm); - font-weight: var(--wpds-typography-font-weight-regular); + font-weight: var(--wpds-typography-font-weight-default); line-height: var(--wpds-typography-line-height-xs); white-space: nowrap; overflow: hidden; @@ -66,7 +66,7 @@ .step-rate { color: var(--wpds-color-foreground-content-neutral); font-size: var(--wpds-typography-font-size-md); - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); line-height: var(--wpds-typography-line-height-xs); } @@ -105,7 +105,12 @@ // Override .visx-tooltip inline styles. border-radius: var(--wpds-border-radius-md) !important; padding: var(--wpds-dimension-padding-md) !important; - box-shadow: var(--wpds-elevation-sm) !important; + // No WPDS elevation token (ex `--wpds-elevation-sm`). + box-shadow: + 0 1px 2px 0 #0000000d, + 0 2px 3px 0 #0000000a, + 0 6px 6px 0 #00000008, + 0 8px 8px 0 #00000005 !important; } @@ -113,7 +118,7 @@ color: var(--wpds-color-foreground-content-neutral); font-size: var(--wpds-typography-font-size-sm); font-style: normal; - font-weight: var(--wpds-typography-font-weight-regular); + font-weight: var(--wpds-typography-font-weight-default); line-height: var(--wpds-typography-line-height-xs); } @@ -121,7 +126,7 @@ color: var(--wpds-color-foreground-content-neutral); font-size: var(--wpds-typography-font-size-md); font-style: normal; - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); line-height: var(--wpds-typography-line-height-sm); } diff --git a/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss b/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss index d75bcedf08c5..325ee88f713f 100644 --- a/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss +++ b/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss @@ -24,7 +24,7 @@ } &__tooltip-date { - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); padding-bottom: var(--wpds-dimension-padding-md); } @@ -33,7 +33,7 @@ } &__tooltip-label { - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); padding-right: var(--wpds-dimension-padding-lg); } @@ -65,7 +65,12 @@ border: none; border-radius: var(--wpds-border-radius-md); font-size: var(--wpds-typography-font-size-md); - box-shadow: var(--wpds-elevation-sm); + // No WPDS elevation token (ex `--wpds-elevation-sm`). + box-shadow: + 0 1px 2px 0 #0000000d, + 0 2px 3px 0 #0000000a, + 0 6px 6px 0 #00000008, + 0 8px 8px 0 #00000005; position: fixed; // Without margin set, the popover has margin:auto which upsets the // positioning relative to the trigger button. A gap token is appropriate diff --git a/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss b/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss index 6648bd3f4258..b3e0302e93c2 100644 --- a/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +++ b/projects/js-packages/charts/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss @@ -6,7 +6,7 @@ } .label { - font-weight: var(--wpds-typography-font-weight-medium); + font-weight: var(--wpds-typography-font-weight-emphasis); font-size: var(--wpds-typography-font-size-lg); } diff --git a/projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.docs.mdx b/projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.docs.mdx index a4942f4141d6..4811efb2509b 100644 --- a/projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.docs.mdx +++ b/projects/js-packages/charts/src/charts/pie-semi-circle-chart/stories/index.docs.mdx @@ -222,7 +222,7 @@ Segments automatically use theme colors, but you can override individual segment The chart includes built-in styling for labels and notes with appropriate typography hierarchy: -- **Label**: Uses `--wpds-typography-font-size-lg` (default 16px), `--wpds-typography-font-weight-medium` (default 499), positioned above the chart +- **Label**: Uses `--wpds-typography-font-size-lg` (default 15px), `--wpds-typography-font-weight-emphasis` (default 600), positioned above the chart - **Note**: Uses `--wpds-typography-font-size-md` (default 13px), positioned below the label Date: Fri, 17 Jul 2026 12:21:03 +0300 Subject: [PATCH 3/4] Use the upcoming LightningCSS visitor --- projects/js-packages/charts/AGENTS.md | 2 +- .../update-charts-wpds-token-fallbacks | 2 +- projects/js-packages/charts/package.json | 2 - projects/js-packages/charts/postcss.config.js | 6 --- .../charts/tools/postcss-css-bundle.ts | 43 ------------------- projects/js-packages/charts/tsdown.config.ts | 12 +++--- tools/js-tools/stylelint.config.base.mjs | 1 - 7 files changed, 9 insertions(+), 59 deletions(-) delete mode 100644 projects/js-packages/charts/postcss.config.js delete mode 100644 projects/js-packages/charts/tools/postcss-css-bundle.ts diff --git a/projects/js-packages/charts/AGENTS.md b/projects/js-packages/charts/AGENTS.md index 24872d63079c..b91e7e2e65ce 100644 --- a/projects/js-packages/charts/AGENTS.md +++ b/projects/js-packages/charts/AGENTS.md @@ -24,7 +24,7 @@ jp changelog add js-packages/charts -s patch -t changed -e "Charts: `/`
` for layout and text. Do not use `__experimental*` exports from `@wordpress/components` (e.g. `__experimentalText`, `__experimentalHStack`) — use the stable `@wordpress/ui` equivalents. Exception: `__experimentalGrid` has no stable alternative yet and is acceptable to use for now. - **Theming.** Theming flows through `@wordpress/theme`'s `ThemeProvider` (unlocked via private APIs in Storybook; see `src/stories/chart-decorator.tsx`). Do not manually override DS tokens in stories or components to achieve theming — pass a color through `ThemeProvider` instead. - **Chart element styles.** Read chart element styles via `getElementStyles` from `GlobalChartsProvider`, not directly from `theme`. This is the supported path for color/style resolution across themes. diff --git a/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks b/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks index a6aa5afa224c..e8eb54941b4f 100644 --- a/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks +++ b/projects/js-packages/charts/changelog/update-charts-wpds-token-fallbacks @@ -1,4 +1,4 @@ Significance: patch Type: changed -Comment: Emit DS token fallbacks via PostCSS/tsdown and drop hardcoded fallbacks from Charts styles and theme JS. +Comment: Emit DS token fallbacks via Lightning CSS/tsdown and drop hardcoded fallbacks from Charts styles and theme JS. diff --git a/projects/js-packages/charts/package.json b/projects/js-packages/charts/package.json index 8ab49751a169..dc7ca32f567b 100644 --- a/projects/js-packages/charts/package.json +++ b/projects/js-packages/charts/package.json @@ -119,13 +119,11 @@ "@wordpress/components": "37.0.0", "@wordpress/element": "8.3.0", "@wordpress/private-apis": "1.51.0", - "autoprefixer": "10.4.20", "babel-jest": "30.4.1", "identity-obj-proxy": "^3.0.0", "jest": "30.4.2", "jest-extended": "7.0.0", "magic-string": "0.30.21", - "postcss": "8.5.14", "react": "18.3.1", "react-dom": "18.3.1", "sass-embedded": "1.97.3", diff --git a/projects/js-packages/charts/postcss.config.js b/projects/js-packages/charts/postcss.config.js deleted file mode 100644 index 0a4a36cc0cb5..000000000000 --- a/projects/js-packages/charts/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -import postcssDsTokenFallbacks from '@wordpress/theme/postcss-plugins/postcss-ds-token-fallbacks'; -import autoprefixer from 'autoprefixer'; - -export default () => ( { - plugins: [ postcssDsTokenFallbacks, autoprefixer ], -} ); diff --git a/projects/js-packages/charts/tools/postcss-css-bundle.ts b/projects/js-packages/charts/tools/postcss-css-bundle.ts deleted file mode 100644 index 939fec792af4..000000000000 --- a/projects/js-packages/charts/tools/postcss-css-bundle.ts +++ /dev/null @@ -1,43 +0,0 @@ -import postcss from 'postcss'; -import { type TsdownPlugin } from 'tsdown'; -import postcssConfig from '../postcss.config.js'; - -/** - * Run PostCSS (DS token fallbacks + autoprefixer) on emitted CSS assets. - * - * tsdown's `css.transformer: 'postcss'` cannot resolve `postcss-modules` under - * pnpm's isolation (tsdown dynamically imports it from its own package root). - * Keeping the default lightningcss transformer preserves CSS modules; this - * plugin injects official `--wpds-*` fallbacks into the final CSS bundle. - * - * Uses `order: 'post'` so it runs after `@tsdown/css` emits the CSS asset. - * - * @return {TsdownPlugin} The tsdown plugin. - */ -export function postcssCssBundle(): TsdownPlugin { - return { - name: 'postcss-css-bundle', - generateBundle: { - order: 'post', - async handler( _options, bundle ) { - const { plugins } = postcssConfig(); - await Promise.all( - Object.values( bundle ).map( async file => { - if ( file.type !== 'asset' || ! file.fileName.endsWith( '.css' ) ) { - return; - } - const source = - typeof file.source === 'string' - ? file.source - : Buffer.from( file.source ).toString( 'utf8' ); - if ( ! source.includes( '--wpds-' ) ) { - return; - } - const result = await postcss( plugins ).process( source, { from: undefined } ); - file.source = result.css; - } ) - ); - }, - }, - }; -} diff --git a/projects/js-packages/charts/tsdown.config.ts b/projects/js-packages/charts/tsdown.config.ts index aff147369527..cd2bd5b43f69 100644 --- a/projects/js-packages/charts/tsdown.config.ts +++ b/projects/js-packages/charts/tsdown.config.ts @@ -1,7 +1,7 @@ import { readFileSync } from 'node:fs'; +import lightningcssDsTokenFallbacks from '@wordpress/theme/lightningcss-plugins/lightningcss-ds-token-fallbacks'; import viteDsTokenFallbacks from '@wordpress/theme/vite-plugins/vite-ds-token-fallbacks'; import { defineConfig } from 'tsdown'; -import { postcssCssBundle } from './tools/postcss-css-bundle.ts'; import { removeDataTestId } from './tools/remove-data-testid.ts'; const pkg = JSON.parse( readFileSync( new URL( './package.json', import.meta.url ), 'utf8' ) ) as { @@ -52,11 +52,13 @@ export default defineConfig( { modules: { generateScopedName: 'a8ccharts-[hash]-[local]', }, + lightningcss: { + visitor: lightningcssDsTokenFallbacks, + }, }, - // PostCSS injects official `--wpds-*` fallbacks into the CSS bundle; - // the Vite plugin does the same for JS/TS theme strings (e.g. `themes.ts`). - // See tools/postcss-css-bundle.ts for why we don't use css.transformer: 'postcss'. - plugins: [ removeDataTestId(), viteDsTokenFallbacks(), postcssCssBundle() ], + // Lightning CSS injects official `--wpds-*` fallbacks into SCSS/CSS; the Vite + // plugin does the same for JS/TS theme strings (e.g. `themes.ts`). + plugins: [ removeDataTestId(), viteDsTokenFallbacks() ], // Fail loudly once @tsdown/css stops emitting these, so the suppression can be dropped. onSuccess() { if ( ! suppressedCssSourcemapWarning ) { diff --git a/tools/js-tools/stylelint.config.base.mjs b/tools/js-tools/stylelint.config.base.mjs index 56ea35e33598..5f91bbf69ad5 100644 --- a/tools/js-tools/stylelint.config.base.mjs +++ b/tools/js-tools/stylelint.config.base.mjs @@ -89,7 +89,6 @@ const baseConfig = { // Packages that still ship hardcoded WPDS fallbacks (no build-time inject yet). files: [ 'projects/js-packages/base-styles/**/*.{css,scss,sass}', - 'projects/js-packages/charts/**/*.{css,scss,sass}', 'projects/js-packages/components/**/*.{css,scss,sass}', 'projects/js-packages/social-previews/**/*.{css,scss,sass}', 'projects/plugins/jetpack/**/*.{css,scss,sass}', From b13d5d2d767e0b3d11713fc67029691744a864a8 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Fri, 17 Jul 2026 12:34:57 +0300 Subject: [PATCH 4/4] Update pnpm-lock.yaml --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f3794a05ac84..c8131955e86d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36442,7 +36442,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass-embedded: 1.97.3 - webpack: 5.107.2(postcss@8.5.14)(webpack-cli@6.0.1) + webpack: 5.107.2(webpack-cli@6.0.1) sax@1.6.0: {}