Skip to content

Charts: remove the theme color props and the override bridge - #51748

Merged
adamwoodnz merged 1 commit into
trunkfrom
charts-263-charts-remove-the-theme-color-props-and-the-override-bridge
Sep 4, 2026
Merged

adamwoodnz merged 1 commit into
trunkfrom
charts-263-charts-remove-the-theme-color-props-and-the-override-bridge

Conversation

@adamwoodnz

@adamwoodnz adamwoodnz commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Fixes CHARTS-263

Why

After CHARTS-262 (#51687) every chart color has a --a8c-charts-color-* role behind it, and the theme color props are a second way to say the same thing. Keeping both means keeping the layer that reconciles them — themeOverrideVars, withCatalogPointers, and an extra indirection on every role a field could reach. Removing the props removes the whole layer.

The fields could not simply be deleted. defaultTheme was the delivery mechanism, not a set of defaults: defaultTheme.gridStyles.stroke is literally var(--a8c-charts-color-grid, #dbdbdb), and that string is what lands in the gridline's inline style. So the chains move first, into private/catalog-pointers.ts.

Read this first — gridColor was not dead

The issue says to delete gridColor/gridColorDark because both are '' and read nowhere in src. True of our code, false of visx: buildChartTheme seeds the y axis line and y tick marks from config.gridColor, and the empty string was the only thing leaving the y axis unpainted — which is the design here.

But the y axis never needed gridColor. visx's ThemeConfig has first-class yAxisLineStyles/yTickLineStyles, symmetric with the x side. So this PR adds --a8c-charts-color-axis-y and --a8c-charts-color-tick-y, resolving to none: same default, but the suppression is now a documented role instead of a side effect. Once the y axis has roles, the old names were a lie — --a8c-charts-color-axis/-tick only ever painted x, so both are renamed to -axis-x/-tick-x. There is deliberately no broad --a8c-charts-color-axis above the pair, since the two sides do not share a default.

This is what keeps Calypso whole: chart-theme.ts sets gridColor, so its dashboard charts have a painted y axis today. It gets that back by declaring the two roles — see below.

Proposed changes

  • Take gridColor/gridColorDark off the public theme, moving the empty strings into the visx call.
  • Move the catalog pointers into private/catalog-pointers.ts, point every internal reader at the constant, then delete the fields and the bridge.
  • Give the y axis its own roles, rename the x pair to match, and add a story showing both states.
  • Let the stylesheet paint what it can reach — the funnel's change indicator, the leaderboard's deltas and the heatmap legend labels now take their color from their own .module.scss instead of routing a pointer through JS into an inline style.
  • Give LeaderboardChart, ConversionFunnelChart and BarListChart the stable root class the other charts have, and add the missing one to PieSemiCircleChart's error state. BarListChart and that error state also stop dropping a caller's own className.
  • Fold in the leaderboard gaps deprecated by Charts: move the leaderboard gaps and bar radius to CSS #51866, and collapse --a8c-charts-color-label-on-fill into --a8c-charts-color-label-inverse — see Removals folded in.

Why the new stable classes are needed

A role can be read by more than the chart you want to move. --a8c-charts-color-surface-secondary paints the funnel's track and GeoChart's dataless regions; the trend pair paints the funnel's change indicator and the leaderboard's deltas. Without a per-chart handle, a consumer setting one of those on the provider wrapper silently moves the other chart too — which is what happened to Premium Analytics' map in review (thanks @kangzj for catching it).

This only works for roles the stylesheet paints. A role resolved in JS at the provider wrapper — the palette, the background — still cannot be scoped per chart. That is the limit TOKENS.md records and CHARTS-255 tracks.

Two things deliberately kept

AnnotationStyles keeps its colors as the per-datum styles prop, and ChartTheme takes a colorless ThemeAnnotationStyles. --a8c-charts-color-annotation moves every annotation at once with no per-annotation selector to narrow it, so removing the datum colors would drop a capability with no replacement. The issue implied a blanket removal; this is narrower on purpose.

CompleteChartTheme stays Required< ChartTheme >. The acceptance criteria ask for a partial-overrides type, but the non-color defaults (tickLength, sparkline margin, heatmap sizing) are still guaranteed by defaultTheme and readers rely on it. What goes is the Omit/Pick juggling that existed only to keep optional color members optional.

Migration

Removed Replacement
backgroundColor --a8c-charts-color-background
labelBackgroundColor --a8c-charts-color-label-background
labelTextColor --a8c-charts-color-label-inverse
colors[ N - 1 ] --a8c-charts-color-series-N, or options.stroke for one series
gridStyles.stroke --a8c-charts-color-grid
xAxisLineStyles.stroke --a8c-charts-color-axis-x
xTickLineStyles.stroke --a8c-charts-color-tick-x
legend.labelStyles.color --a8c-charts-color-label
svgLabelSmall.fill --a8c-charts-color-label-axis
annotationStyles.* colors --a8c-charts-color-annotation, --a8c-charts-color-surface, or the annotation's own styles prop
geoChart.featureFillColor --a8c-charts-color-surface-secondary
leaderboardChart.primaryColor / .secondaryColor the primaryColor / secondaryColor props, or --a8c-charts-color-series-1 / -2
leaderboardChart.deltaColors --a8c-charts-color-trend-down / -trend-neutral / -trend-up, scoped to .leaderboard-chart if the funnel should not follow
leaderboardChart.rowGap / .columnGap --a8c-charts-dimension-leaderboard-row-gap / -column-gap
conversionFunnelChart.primaryColor --a8c-charts-color-series-1
conversionFunnelChart.backgroundColor --a8c-charts-color-surface-secondary, scoped to .conversion-funnel-chart if GeoChart should not follow
conversionFunnelChart.positiveChangeColor / .negativeChangeColor --a8c-charts-color-trend-up / -trend-down
heatmapChart.primaryColor the primaryColor prop, or --a8c-charts-color-series-1
gridColor --a8c-charts-color-axis-y and --a8c-charts-color-tick-y (new). Both default to none
gridColorDark none. Inert — xAxisLineStyles already overrode it
--a8c-charts-color-label-on-fill --a8c-charts-color-label-inverse

Also renamed: --a8c-charts-color-axis → -axis-x, --a8c-charts-color-tick → -tick-x.

An override must be set inside the provider tree, or on .a8c-charts-scope itself. A rule above GlobalChartsProvider does not apply: the catalog is declared on the provider's own wrapper, and an element's own declaration beats one it inherits.

Removals folded in

Not in the issue, but this is the release for it. The changelog entry is already major, and any PR merged before the next publish rides that same bump — so a separate PR only avoids a second major if nobody publishes in between, which is not something we control.

Removed Replacement
AreaChart's rescaleYOnLegendToggle rescaleYOnVisibilityChange — the same setting, under a name that matches when it applies
parseRgbString() normalizeColorToHex(), which handles rgb() alongside every other format
theme.leaderboardChart.rowGap / .columnGap the two dimension roles above
--a8c-charts-color-label-on-fill --a8c-charts-color-label-inverse

The leaderboard gaps were deprecated by #51866 and never reached a release — charts is at 3.2.0, that entry is still unreleased, so 4.0.0 absorbs both. The two changelog entries are merged into one rather than shipping "deprecated" and "removed" side by side in the same version.

label-on-fill and label-inverse were one concept under two names — label text on a filled surface. label-inverse survives because it is WPDS-mapped. This is the one visible change in the PR: pie labels follow --wpds-color-foreground-interactive-neutral-strong now, so they render #f0f0f0 rather than #fff until a consumer sets the role. Premium Analytics already overrode label-on-fill to exactly that token, so its dashboard is unchanged.

Also gone: the "Migration from Legacy API" section in annotation.docs.mdx, which told readers to migrate off an annotations prop that LineChart no longer has.

smoothing is kept. One docs line called it deprecated; the types never did, and "ignored when curveType is set" is an interaction rather than a replacement. That line now says what the types say.

Docs and tests corrected

The docs told readers to set colors on the theme in 21 places across 15 files — a root "Core Concepts" section, nine copies of "the default theme has neutral colors", and a GeoChart API table listing backgroundColor / datalessRegionColor / defaultColor as "Theme value". Twelve test names said the same. Both are now stated in terms of the roles and the palette.

One of those tests was worse than misnamed: use-leaderboard-legend-items built a theme with leaderboardChart.primaryColor/.secondaryColor, passed it to the provider, and asserted only that the colors were truthy. Assigning the literal to a const first suppressed excess-property checking, so nothing flagged it. The dead theme is gone and the test is named for what it checks.

Calypso

client/dashboard/app/chart-theme.ts sets eight fields, six of them color. Every one has a route:

.dashboard {
	--a8c-charts-color-background: var(--dashboard-surface__background-color);
	--a8c-charts-color-grid: var(--dashboard-surface__border-color);
	--a8c-charts-color-axis-x: var(--dashboard-surface__border-color);
	--a8c-charts-color-tick-x: var(--dashboard-surface__border-color);
	--a8c-charts-color-axis-y: var(--dashboard-surface__border-color);
	--a8c-charts-color-tick-y: var(--dashboard-surface__border-color);
	--a8c-charts-color-label: var(--dashboard__text-muted-color);
	--a8c-charts-color-label-axis: var(--dashboard__text-muted-color);
}

The last four lines are what gridColor/gridColorDark were doing by accident. The strokeWidth fields stay on the theme prop. Nothing is lost, but the Calypso change has to land before charts is bumped there.

Premium Analytics

Migrated here, as the one in-repo consumer that set theme colors. useChartTheme keeps only the non-color fields; the colors, the leaderboard row gap and the bar radius move to a chart-roles.scss declaring roles on .a8c-charts-scope, with the two chart-scoped exceptions above. No visible change.

Changelog

One charts entry and one Premium Analytics entry, each absorbing #51866's. No plugin entries — a departure from CHARTS-202 and CHARTS-254, which each landed ten. No plugin in the monorepo sets a charts theme color, so nothing a site owner can see changes in any of them. Say so if you disagree and I will add them.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No.

Testing instructions

Run the charts Storybook: pnpm -C projects/js-packages/storybook run storybook:dev.

  1. Open Bar / Line / Area → Fixed Dimensions, Pie, Leaderboard, Conversion Funnel, Heatmap, Geo and Line → Annotations. All match trunk except pie labels, which are now #f0f0f0 rather than #fff. The y axis has no axis line and no tick marks, only labels.
  2. Open Bar Chart → Painted Y Axis. Left is the default; right declares --a8c-charts-color-axis-y (blue) and --a8c-charts-color-tick-y (red). The x axis is identical in both.
  3. In DevTools, set --a8c-charts-color-grid: red on the chart's own root (the element carrying .bar-chart) — gridlines turn red. Set --a8c-charts-color-axis-x: lime on .a8c-charts-scope — the x axis turns green, the y axis stays unpainted.
  4. Set --a8c-charts-color-surface-secondary: red on .a8c-charts-scope with Geo and Conversion Funnel both open. Both move; scoping the same declaration to .conversion-funnel-chart moves only the funnel.
  5. Switch themeName to mixed-color-formats — the palette shows #e63946, #2a9d8f (from rgb()), #facc14 (from hsl()), so format normalization still runs through the roles. Switch to custom — gridlines pink (#ffe3e3, from the role) at stroke-width: 2 (from the theme prop).
  6. Confirm your editor no longer offers backgroundColor, colors, labelTextColor or leaderboardChart.rowGap on theme, and still offers tickLength, gridStyles.strokeWidth, svgLabelSmall.fontFamily and leaderboardChart.labelSpacing. Nor rescaleYOnLegendToggle on AreaChart, nor parseRgbString from the package root.
  7. Open Area Chart → Y-axis rescales when visible series change (default). Toggling a series in the legend rescales the y axis on the left chart and pins it on the right, unchanged from trunk — that story is what the removed rescaleYOnLegendToggle aliased.

Verified

  • pnpm -C projects/js-packages/charts run test — 85 suites, 1389 passed. pnpm -C projects/packages/premium-analytics run test — 156 suites, 3065 passed.
  • run typecheck on both packages, run build, eslint, stylelint — clean. run build matters: tools/assert-charts-scope-emitted.ts fails it if the catalog stops reaching dist/index.css, and this PR edits that stylesheet.
  • Exercised in Chrome at every surface above, reading back getComputedStyle. Grid, x axis line, x tick marks and tick labels each carry the var() chain on the element and resolve; the y pair computes none until declared, then paints, then returns to none on removal, with x untouched throughout. Pie labels #f0f0f0, moving to #00ff00 from .a8c-charts-scope and #ff00ff from .pie-chart. Annotations resolve with radius: 5 intact. Funnel background #f4f4f4, indicator #008030. Heatmap #007cba with the background chain resolving for the color-mix, and the cell fill tracking a background override at both levels. Geo #fff / #f4f4f4, both handed to Google Charts as hex. Leaderboard gaps 12px / 4px, following an override on .a8c-charts-scope.
  • The .conversion-funnel-chart and .leaderboard-chart scoping checked the same way — the scoped override moves only its own chart, and GeoChart no longer follows.

Rebase note

Rebased onto trunk after #51640 (tooltips render in-tree rather than in a document.body portal) and #51722 (WPDS fallbacks injected at build time). The 33 commits were squashed to one first, so each conflict was resolved against the final state of both sides rather than against intermediate ones. chart-scope.scss lost all 23 hand-written fallbacks to the build-time plugin, and chart-scope.test.ts now compares only the mapping, since the fallback is no longer in source.

🤖 Generated with Claude Code

https://claude.ai/code/session_0147vnw9VyKQkMsknWqyXNc8

@adamwoodnz adamwoodnz self-assigned this Aug 31, 2026
@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack or WordPress.com Site Helper), and enable the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack charts-263-charts-remove-the-theme-color-props-and-the-override-bridge
bin/jetpack-downloader test jetpack-mu-wpcom-plugin charts-263-charts-remove-the-theme-color-props-and-the-override-bridge

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: September 8, 2026

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Backup plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Boost plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Search plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Social plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Starter Plugin plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Protect plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Videopress plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Mu Wpcom plugin:

  • Next scheduled release: WordPress.com Simple releases happen semi-continuously (PCYsg-Jjm-p2)

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Wpcomsh plugin:

  • Next scheduled release: Atomic deploys happen twice daily on weekdays (p9o2xV-2EN-p2)

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Premium Analytics plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.


Stats Data plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

@github-actions github-actions Bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label Aug 31, 2026
@jp-launch-control

jp-launch-control Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 9 files. Only the first 5 are listed here.

File Coverage Δ% Δ Uncovered
projects/js-packages/charts/src/charts/area-chart/area-chart.tsx 112/112 (100.00%) 0.00% 0 💚
projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx 90/96 (93.75%) -0.19% 0 💚
projects/js-packages/charts/src/charts/leaderboard-chart/hooks/use-leaderboard-legend-items.ts 11/11 (100.00%) 0.00% 0 💚
projects/js-packages/charts/src/charts/line-chart/line-chart.tsx 135/137 (98.54%) -0.01% 0 💚
projects/js-packages/charts/src/charts/line-chart/private/line-chart-annotation.tsx 83/89 (93.26%) -0.15% 0 💚

1 file is newly checked for coverage.

File Coverage
projects/js-packages/charts/src/providers/chart-context/private/catalog-pointers.ts 2/2 (100.00%) 💚

Full summary · PHP report · JS report

@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Codex review

The removed API leaves an in-repository consumer unable to type-check, while active theme color override paths and public documentation remain inconsistent with the new contract.

Full review comments:

  • [P1] Migrate Premium Analytics before removing theme color fields — projects/js-packages/charts/src/types.ts:358-360
    Premium Analytics' use-chart-theme.ts still returns a WooChartTheme containing removed fields such as backgroundColor, gridStyles.stroke, deltaColors, and conversionFunnelChart; its widget and report roots pass that object to GlobalChartsProvider. Since that package type-checks against this re-exported ChartTheme, its build now fails, and a cast would instead silently discard its dashboard color customizations. Migrate the hook to scoped --a8c-charts-color-* CSS roles and retain only non-color theme fields before removing these properties.

  • [P2] Remove remaining color-bearing fields from ChartTheme — projects/js-packages/charts/src/types.ts:364-368
    ChartTheme still permits stroke through seriesLineStyles and lineChart.lineStyles, while its legend shapeStyles still permits fill and stroke. For example, theme={{ seriesLineStyles: [{ stroke: '#f00' }] }} reaches getElementStyles, and LineChart spreads those styles after its palette-derived stroke, so it continues to override --a8c-charts-color-series-* despite the new no-color theme contract. Restrict the theme-only style types to omit color properties and remove or replace the legend shape color path, while retaining per-series options as the intentional local override.

  • [P2] Update public docs for the removed theme color API — projects/js-packages/charts/src/charts/heatmap-chart/heatmap-chart.tsx:82-85
    The implementation now ignores heatmapChart.primaryColor, but the Heatmap props JSDoc and both Heatmap Storybook docs still advertise it as a fallback; the conversion-funnel docs still show conversionFunnelChart, and the provider docs still use theme.backgroundColor. Consumers following these examples now get excess-property errors in TypeScript or ineffective configuration in JavaScript. Replace those examples and descriptions with the relevant CSS roles and correct the documented fallback order.

@adamwoodnz

Copy link
Copy Markdown
Contributor Author

All three findings were valid. Addressed in f65b99d.

P1 — Premium Analytics. Confirmed: the Type checking job was already failing on use-chart-theme.ts. I had only run the charts package's own typecheck, which never sees its consumers.

Migrated it to catalog roles. Worth noting for review: most of what that hook was passing turned out to restate the charts defaults — background, grid, axis, label and label-axis all map to the same WPDS tokens the catalog already uses — so the new chart-roles.scss declares only the four that genuinely differ, rather than copying the lot into a second place to keep in step.

One deliberate behavior change: the leaderboard's delta values used --wpds-color-stroke-surface-{error,success}-strong while the funnel's change indicator used the foreground-content-*-weak pair. Both are --a8c-charts-color-trend-* now, and the weak pair is the catalog default, so the deltas move one step weaker. That is the role model working as intended — one name, one color — but it is a visible change and a Premium Analytics owner should confirm they are happy with it.

P2 — color still reachable through the line-style types. Correct, and it was the same hole xAxisLineStyles and xTickLineStyles already had closed. seriesLineStyles and lineChart.lineStyles now omit stroke too. Verified the in-repo consumer only ever sets strokeWidth / strokeDasharray / strokeLinecap / strokeDashoffset, so nothing breaks.

I did not narrow legend.shapeStyles, and want to flag the disagreement rather than leave it silent. It is typed Record< string, unknown > — an untyped SVG-props bag, not a style type with a color member. Narrowing it means either enumerating what is allowed, which breaks arbitrary passthrough consumers rely on, or converting it to CSSProperties and omitting the color keys, which is a wider type change than this PR's subject. The swatch color comes from the palette via item.color in the normal path. Happy to do it as a follow-up if a reviewer wants the contract airtight.

P2 — stale documentation. Fixed: the heatmap's primaryColor fallback order in its JSDoc and both docs pages, the funnel's theme example, and the two useGlobalChartsTheme snippets in the provider docs. That last one was the more useful catch — those snippets were showing theme.backgroundColor as the way to read a chart color, which is exactly the habit this PR is trying to end.

Not from the review, but landed alongside it: the --a8c-charts-color-axis / -tick roles are renamed to -axis-x / -tick-x, and the y axis gets its own -axis-y / -tick-y pair. The old names only ever painted the x axis without saying so.

@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Codex review

Heatmap legend text no longer honors the new CSS label role, leaving the color migration incomplete for that component. The patch also leaves several public documentation examples on removed theme APIs.

Full review comments:

  • [P2] Keep Heatmap legend labels on the catalog role — projects/js-packages/charts/src/charts/heatmap-chart/private/heatmap-legend.tsx:19-19
    When consumers set --a8c-charts-color-label, the Heatmap legend's “Less” and “More” labels no longer follow it: removing the default legend.labelStyles.color leaves these Text elements with no class or style that reads the role, unlike BaseLegend. Add a Heatmap legend-label CSS class (or equivalent inline default) with color: var(--a8c-charts-color-label) while preserving the remaining label styles.

  • [P2] Remove obsolete Heatmap theme-color guidance — projects/js-packages/charts/src/charts/heatmap-chart/stories/index.docs.mdx:78-78
    The later Theming section in this same document still says heatmapChart.primaryColor participates in resolution and presents it in a ChartTheme literal. That field has been removed and the renderer no longer reads it, so copying the example now fails type checking (or silently falls back to the palette in untyped code). Remove the theme-based branch and keep the per-instance primaryColor route plus compact sizing guidance.

  • [P2] Migrate the remaining grid-color examples — projects/js-packages/charts/src/types.ts:353-354
    Removing gridStyles.stroke leaves published examples in the LineChart, BarChart, and chart-context MDX guides using that removed property. Those snippets now fail when copied into TypeScript and describe an override that no longer paints; replace the color portions with --a8c-charts-color-grid CSS declarations, retaining only non-color settings such as strokeWidth in ChartTheme.

@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Response to round 2

All three fixed in 7995c7f.

P2 — Heatmap legend labels. Real bug, and the one thing in this round that a user would have seen. HeatmapLegend passed legend.labelStyles straight to <Text>, so removing the theme color left "Less"/"More" inheriting the host page instead of following --a8c-charts-color-label. BaseLegend was unaffected because it already had a class for this. Added .heatmap-chart__legend-label and applied it to both labels.

Verified in Storybook (HeatmapChart / WithCompositionLegend): both labels default to rgb(30, 30, 30), follow an override of the role at .a8c-charts-scope to rgb(1, 2, 3), and return to the default when it is removed.

P2 — Heatmap theme guidance. Correct, though the stale text is the Theming section (233–252), not line 78 — that bullet already describes the prop and the palette role accurately. Removed the heatmapChart: { primaryColor } branch and its resolution-order sentence; the section now shows the per-instance prop and, for moving every heatmap at once, a CSS declaration of --a8c-charts-color-series-1.

P2 — Grid-color examples. Fixed in the line-chart, bar-chart and chart-context guides: the colour moved into the CSS block as --a8c-charts-color-grid, and the theme example keeps strokeWidth.

Four more of the same class that the review did not reach, found while sweeping for it: backgroundColor: '#f5f5f5' as a ChartTheme field in the bar-list, leaderboard, pie and pie-semi-circle guides. Same treatment.

While there: six of these examples annotate a theme literal as ChartTheme while setting only optional fields, which fails on the required tickLength. That pre-dates this PR, but the examples were being edited anyway, so they now say Partial< ChartTheme > — what GlobalChartsProvider actually takes.

Checks: typecheck clean, 69 suites / 1271 tests, ESLint and Stylelint clean.

@kangzj kangzj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through this one properly and gave it a spin in Storybook - reading values back with getComputedStyle rather than eyeballing, since most of what moved here is a var() chain. The mechanism holds up really well: the y axis pair does exactly what you describe, the split between the role and the theme prop is clean, and every surface I could diff against charts-262 came out the same. Nice cleanup 👍

Not approving since it's still a draft, and there's one thing I'd want sorted before it goes green anyway.

Blocker

  1. Premium Analytics: --a8c-charts-color-surface-secondary on .a8c-charts-scope also repaints GeoChart's dataless countries, not just the funnel track it replaces. Reproduced live in the Visitors by Location story - dataless regions are #f4f4f4 on charts-262 and #ecf0fa on this branch, while the data ramp stays #007cba → #bedeed, so no-data now sits inside the ramp's own colour family. The PA changelog entry says "No visible change." too. Left it inline on the file.

Suggestions

Three, all inline - a docblock that got detached in leaderboard-chart.tsx, a comment that landed on the wrong role in chart-scope.scss, and the new Painted Y Axis story overflowing its container.

Testing performed

Charts Storybook on this branch, plus the same stories on charts-262 where I needed a before.

  • Painted Y Axis (new story) - left chart: y axis line and y ticks both compute none; right chart: axis rgb(56,88,233), ticks rgb(204,24,24). x axis is rgb(219,219,219) in both. ✅
  • DevTools overrides - --a8c-charts-color-grid: red on the chart's own .bar-chart turned the gridlines red straight away; --a8c-charts-color-axis-x: lime on .a8c-charts-scope moved the x axis line and left y at none. Both went back to #dbdbdb when I removed them. ✅
  • themeName mixed-color-formats - palette came out #e63946, rgb(42,157,143), rgb(250,204,20), so hex/rgb/hsl all normalise through the roles. Grid rgb(200,200,200). ✅
  • themeName custom - gridlines #ffe3e3 from the role at stroke-width: 2 from the theme prop. ✅
  • Pie - labels #fff, plate rgba(0,0,0,0), so no visible rect. ✅
  • Annotations - connector, circle subject, label anchor and label background all carry the chain and resolve (#1e1e1e / #fff), radius: 5 intact, and a per-datum styles colour still wins (var(--jp-red) → rgb(214,54,57)). ✅ The story renders nothing while the tab is backgrounded, same as you found - the elements are in the DOM either way.
  • Funnel - track #f4f4f4, bar #007cba, positive indicator #008030 off the new class. ✅
  • Heatmap - primary #007cba, background #fff, legend "Less"/"More" #1e1e1e off the new class. ✅
  • Leaderboard - up #008030, down #cc1818, and both zero-delta and the no-comparison placeholder land on neutral #707070, matching what deltaColors did. leaderboard-chart class is on the root. ✅
  • Geo (charts default) - background #fff, dataless #f4f4f4, unchanged. ✅
  • Legend labels - #1e1e1e via .legend-item-label, and PA's own legend keeps its font-size: 12px. ✅
  • PA parity - --a8c-charts-color-label-background, -label-on-fill and -tick-x all reach exactly what the old theme fields reached, and the .leaderboard-chart scoping keeps PA's trend colours off the funnel. The only role that widened is the one above. ⚠️
  • Bar Chart / Area Chart Fixed Dimensions - screenshots pixel-identical to charts-262. ✅ (The area chart's x labels sitting over the fill is on the base branch too, so not this PR.)
  • pnpm run test - 1271 passed, 69 suites. typecheck, build, eslint, stylelint all clean.

Nothing I could find that reads the removed props elsewhere in the monorepo - the newsletter widget's gridColorDark is on visx directly, not on charts.

Evidence

The new story, showing both the y axis roles working and the clipping I mention inline:

Painted Y Axis story

Geo dataless fill, same story, same data:

charts-262 this branch
dataless region #f4f4f4 #ecf0fa
data ramp #007cba … #bedeed #007cba … #bedeed

Thanks for the very thorough write-up on this one, it made the review a lot easier. Let me know what you think about the funnel class 🙂

.a8c-charts-scope {
--a8c-charts-color-label-background: var(--wpds-color-background-interactive-neutral-weak);
--a8c-charts-color-label-on-fill: var(--wpds-color-foreground-interactive-neutral-strong);
--a8c-charts-color-surface-secondary: var(--wpds-color-background-surface-brand);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one reaches further than the field it replaces. conversionFunnelChart.backgroundColor only ever painted the funnel track, but --a8c-charts-color-surface-secondary is also what GeoChart hands Google Charts as its dataless-region fill, so declaring it on the scope moves both.

Checked it in the Visitors by Location story: on charts-262 the dataless regions come out #f4f4f4, on this branch #ecf0fa, and the ramp is unchanged at #007cba → #bedeed. So a country with zero visitors now sits in the same pale blue family as a country with the fewest, which is a bit hard to read on the map. And the PA changelog says "No visible change.", which wouldn't be true.

I think the funnel wants the same treatment you gave the leaderboard in this PR - a stable conversion-funnel-chart class on its root, and then this line scoped to it:

.conversion-funnel-chart {
	--a8c-charts-color-surface-secondary: var(--wpds-color-background-surface-brand);
}

That keeps the funnel on brand and leaves the map on the neutral default. Does that make sense? Happy to be told there's a simpler way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You called it — our timing crossed. This landed in 5fb423c8 about two hours before your second pass, and you diffed against 660b20a, which is three commits back, so the blocker was already fixed when you re-read it.

Your suggestion is exactly what shipped, down to the SCSS:

.conversion-funnel-chart {
	--a8c-charts-color-surface-secondary: var(--wpds-color-background-surface-brand);
}

ConversionFunnelChart now carries a stable conversion-funnel-chart class on both root call sites (it has two — the loading branch and the normal one, and the class had to go on each). I checked the fix the way you found the bug, in Visitors by Location: geo dataless #f4f4f4 on the default → rgb(236,240,250) with the rule back on .a8c-charts-scope, reproducing your regression → #f4f4f4 again with it scoped to the funnel class. The funnel track still moves under the scoped rule, so it is doing the work and not just being inert.

Auditing for that turned up two more charts with no stable class — BarListChart had none at all (it renders through BarChartUnresponsive, so bar-chart was the only handle), and PieSemiCircleChart had one on its normal path but not its error path. Both fixed here, and both now pass a caller's own className through instead of dropping it.

Thanks for the diff — a role reaching further than the field it replaced is the failure mode this whole PR is most exposed to, and it was the one I had not checked.

* @return Formatted string representation of the value
*/
/** Indexed by `Math.sign( delta ) + 1`, so [negative, neutral, positive]. */
const DELTA_TREND_CLASS = [ 'deltaValue--down', 'deltaValue--neutral', 'deltaValue--up' ] as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small one - this landed between defaultValueFormatter's docblock and the function, so the @param value / @return block now documents the constant instead of the formatter. Moving DELTA_TREND_CLASS up above the /** on line 29 puts the two back together.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5fb423c8 — DELTA_TREND_CLASS moved above the /** so the @param value / @return block sits with defaultValueFormatter again. Good catch, that was a rebase artefact.

--a8c-charts-color-axis-y: none;
--a8c-charts-color-tick-y: none;
// A role a `fill` reads needs a terminal literal even to paint nothing: a
// fallback-less var() naming an undeclared property drops the declaration, and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment wants to sit next to --a8c-charts-color-label-background a few lines down - that's the role where the terminal literal is actually load-bearing. Where it is now it reads as explaining --a8c-charts-color-label, which already carries its own literal and isn't the interesting case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5fb423c8 — the comment now sits above --a8c-charts-color-label-background. You were right that it was on the wrong role: --a8c-charts-color-label is read by color, which starts at currentColor, so its literal is a default rather than protection. The fill case is the load-bearing one, and label-background is where it bites — without the terminal transparent an undeclared role puts a black plate behind every pie label.

export const PaintedYAxis: Story = {
args: {
containerWidth: '900px',
containerHeight: '340px',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The story overflows its container a little - I measured scrollHeight 359 against clientHeight 338, so the right chart's x tick labels get clipped off the bottom.

Suggested change
containerHeight: '340px',
containerHeight: '400px',

The other half is that the right caption wraps to two lines and the left one doesn't, so the two charts start at different heights and the columns come out 380px vs 454px. Since the whole point of the story is "the x axis is identical in both", it'd read much better with repeat(2, 380px) instead of repeat(2, auto) on the grid, or a shorter caption on the right so both are one line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both taken in 5fb423c8 — containerHeight: 400px, and repeat(2, 380px) on the grid. I shortened the right caption to "Axis blue, tick marks red." as well, so both captions are one line and the two charts start at the same height. The x tick labels are no longer clipped.

@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch from 7995c7f to 881e34e Compare August 31, 2026 23:02
Base automatically changed from charts-262-paint-only-colors-in-css to trunk September 1, 2026 03:22
@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch from 881e34e to 346a9c6 Compare September 1, 2026 03:23
@github-actions github-actions Bot added [Plugin] Backup A plugin that allows users to save every change and get back online quickly with one-click restores. [Plugin] Boost A feature to speed up the site and improve performance. [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] mu wpcom jetpack-mu-wpcom plugin [Plugin] Premium Analytics [Plugin] Protect A plugin with features to protect a site: brute force protection, security scanning, and a WAF. [Plugin] Search A plugin to add an instant search modal to your site to help visitors find content faster. [Plugin] Social Issues about the Jetpack Social plugin labels Sep 1, 2026
kangzj
kangzj previously approved these changes Sep 1, 2026

@kangzj kangzj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Third time's the charm 😄 Approving — went through all 10 new commits and gave the fixes a proper spin in Storybook, and everything from my earlier passes is sorted.

The funnel fix is exactly right, and I like that bar-list-chart and the pie-semi-circle error state got the same treatment while you were there. The two extra removals (rescaleYOnLegendToggle, parseRgbString) make sense bundled into this major — I grepped the monorepo and Calypso's client/apps for both and nothing uses them.

What I checked this pass:

Checked Result Status
Delta review, 660b20a → efd73eb (10 commits) Fixes match the threads, comment trims stay accurate, changelog covers the new classes and removals ✅
Funnel scoping, live in Storybook Root carries the bare conversion-funnel-chart class; track #f4f4f4 by default, follows a declaration scoped to that class, restores on removal ✅
Geo unaffected — the blocker's fix With the same scoped rule injected, geo's resolution point stays #f4f4f4 (629 dataless paths painted #f4f4f4, ramp intact); only the old on-scope shape moves it ✅
Painted Y Axis story No overflow (scrollHeight == clientHeight), both columns 380px, captions one line; left y axis computes none, right axis rgb(56,88,233) / ticks rgb(204,24,24) ✅
New stable classes bar-list-chart root carries bar-list-chart + bar-chart live; both pie-semi-circle branches carry theirs ✅
Consumers of the removed APIs None in the monorepo, none in Calypso ✅
pnpm run test / typecheck (charts) 1263 passed, 70 suites — the 11 gone are the deleted parseRgbString suite and the now-duplicate rescale test; typecheck clean ✅
CI 88 pass; the two fails are WP 7.0 e2e env errors ("Site URL could not be resolved") on Boost and post-connection, both unrelated to charts and passing on WP latest ⚠️

The story after the height fix — x tick labels visible, both charts starting level:

Painted Y Axis story after the height fix

Thanks for turning all of this around so quickly Adam, and sorry about the crossed wires on the last pass 🙂

@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch from ae763c9 to cd2decb Compare September 2, 2026 01:12
@adamwoodnz
adamwoodnz changed the base branch from trunk to charts-267-move-the-leaderboard-gaps-to-css September 2, 2026 01:12
@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch 5 times, most recently from 8d0afa2 to fa66184 Compare September 2, 2026 03:34
Base automatically changed from charts-267-move-the-leaderboard-gaps-to-css to trunk September 2, 2026 04:51
@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch from fa66184 to d8f7cc7 Compare September 2, 2026 04:57
@Automattic Automattic deleted a comment from claude Bot Sep 2, 2026
@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch from d8f7cc7 to 9a7f63b Compare September 3, 2026 21:02
@adamwoodnz

Copy link
Copy Markdown
Contributor Author

Codex review

The intentional major-release removal of deprecated color APIs is documented, and the new CSS-role paths are consistently wired through the chart components and consumer package.

@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch 8 times, most recently from 2bc3f1e to 61186fd Compare September 3, 2026 23:25
Every chart color is now a `--a8c-charts-color-*` role set in CSS. The `theme`
prop carries no colors, and the `--a8c-charts-*-theme` bridge that relayed them
is gone, so a role declared in CSS is the only way to move a color.

The catalog pointers move to `providers/chart-context/private/catalog-pointers.ts`,
where they are internal rather than reachable through `defaultTheme`. Each axis
gains its own pair of roles, so moving the x axis no longer moves the y.
`rescaleYOnLegendToggle` and `parseRgbString`, both deprecated, are removed with
them, and Premium Analytics moves its remaining overrides to `chart-roles.scss`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0147vnw9VyKQkMsknWqyXNc8
@adamwoodnz
adamwoodnz force-pushed the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch from 61186fd to 6881b5a Compare September 3, 2026 23:44
@adamwoodnz adamwoodnz added [Status] Ready to Merge Go ahead, you can push that green button! and removed [Status] Needs Review This PR is ready for review. labels Sep 3, 2026
@adamwoodnz
adamwoodnz merged commit 916d86a into trunk Sep 4, 2026
132 of 134 checks passed
@adamwoodnz
adamwoodnz deleted the charts-263-charts-remove-the-theme-color-props-and-the-override-bridge branch September 4, 2026 00:27
@github-actions github-actions Bot added [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. and removed [Status] Ready to Merge Go ahead, you can push that green button! labels Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Docs [JS Package] Charts [Package] Premium Analytics [Plugin] Backup A plugin that allows users to save every change and get back online quickly with one-click restores. [Plugin] Boost A feature to speed up the site and improve performance. [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] mu wpcom jetpack-mu-wpcom plugin [Plugin] Premium Analytics [Plugin] Protect A plugin with features to protect a site: brute force protection, security scanning, and a WAF. [Plugin] Search A plugin to add an instant search modal to your site to help visitors find content faster. [Plugin] Social Issues about the Jetpack Social plugin [Plugin] Starter Plugin [Plugin] Stats Data [Plugin] VideoPress A standalone plugin to add high-quality VideoPress videos to your site. [Plugin] Wpcomsh RNA [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants