Release: Chart Builder
3.10.0· PRC Platform1.8Work period: May – June 2026 Scope:prc-chart-builder,prc-charting-libraryTracking: PRC-17 · PRC-402
Overview
Chart Builder 3.10.0 is a runtime-architecture and animation release. Three major initiatives ship together:
- Preact frontend + Script Module (PRC-17) — the chart frontend now loads as a native WordPress Script Module.
@prc/charting-libraryships a Preact bundle for the browser and keeps its existing React bundle for the editor. Reactive chart state and a programmatic action surface power the changes below. - Animation suite (PRC-17) — opt-in entry animations across every major chart family (bars, lines, areas, dot plots, pies) with author-controlled easing, duration, stagger, and reduced-motion awareness.
- Bug fixes and chart action improvements (PRC-402) — color cycling for pie and exploded-bar charts, chart/data view toggle fix for nested controllers, world map shape updates, chart peripherals (titles, tables) updatable via chart actions, Freeform metadata prepopulation, and tooltip dark-mode fixes.
- Editor production tools and tab controls — a consolidated Production Tools inspector panel, granular tab visibility controls, and a unified in-browser debug surface.
No database migrations. No breaking changes to published content. All animation and tab-control attributes are additive with safe defaults; existing charts render identically until producers opt in.
What's New
1. Preact frontend runtime (PRC-17)
Background
Chart Builder's frontend (view.js + @prc/charting-library) previously ran a full React bundle in the browser — the same bundle used by the block editor. React in production weighs ~130 KB gzipped and carries scheduler and synthetic-event overhead that the static chart frontend does not need. More structurally, view.js had no way to update chart state after initial render without manipulating block attributes and triggering a PHP re-render cycle.
PRC-17 ships a purpose-built browser runtime.
What shipped
Dual build in @prc/charting-library
@prc/charting-library now produces two bundles:
| Bundle | Usage | Runtime |
|---|---|---|
build/index.js (was build/editor.js) | Block editor | React (unchanged) |
build/frontend.js | Browser view.js | Preact (preact/compat aliased to react/react-dom) |
dependency-extraction.js at the repo root maps @prc/charting-library → window.prcChartingLibrary for the editor bundle. The frontend bundle is loaded as a WordPress Script Module (@prc/charting-library/frontend) and is enqueued separately by the chart controller's PHP, after the Interactivity API runtime.
The Preact frontend bundle is comparable in size to the React editor bundle (~475–490 KB uncompressed). The charting library bundles significant shared dependencies (d3, react-spring, etc.) that are the dominant cost regardless of UI runtime, so the React → Preact swap does not produce a meaningful size reduction in this package. The architectural benefit is correctness and separation of concerns: the frontend bundle is now a proper Script Module with deterministic load order, reactive Interactivity API state, and no editor-specific code paths.
useChartStore — reactive Interactivity API store
Chart state is now held in an Interactivity API store keyed by controller ID:
const { state, actions } = store('prc-chart-builder/controller', {
state: { charts: {} },
actions: {
setChart(chartId, { data, config, tableData }),
setData(chartId, data),
setConfig(chartId, config),
setTableData(chartId, tableData),
},
});
applyDeepPatch handles the merge semantics: plain objects are deep-merged, arrays are replaced wholesale. config changes trigger re-render by deep-tracking the entire config subtree (fixing a bug where nested setConfig mutations didn't re-render because only the top-level reference was tracked).
useChartStore is exposed as a hook for components inside the charting library to subscribe to reactive state updates.
Chart action surface
The programmatic API used by both the debug surface and external consumers:
// Access via the debug surface (browser console or automation scripts)
window.prcChartBuilder.debug.setChart(chartId, { data, config });
window.prcChartBuilder.debug.setData(chartId, newData);
window.prcChartBuilder.debug.setConfig(chartId, newConfig);
window.prcChartBuilder.debug.getChart(chartId);
window.prcChartBuilder.debug.listCharts();
Chart peripherals — titles, subtitles, notes, source lines — can also be updated via setConfig patches, so the full chart rendering surface is reachable from the action API.
2. Animation suite (PRC-17)
Background
PRC charts are static screenshots on the first impression for most readers. Entry animations on chart reveal — bars growing from the axis, lines drawing across, pie slices sweeping in — improve reader engagement and make the data storytelling more legible, particularly on mobile where the chart occupies most of the viewport.
The animation system is fully opt-in, reduced-motion-aware, and invisible in the editor by default (instant render, no animation preview unless the author toggles "Preview animation").
What shipped
React-spring foundation
All animated components are built on react-spring with a custom TransitionProvider that coordinates:
- Initial state: shapes at zero scale/opacity
- Entry state: shapes animate to final geometry
animation.initial.followoption: secondary elements (line nodes after path, dot-plot connector after dots) sequence after their parent
Chart family coverage
| Chart family | Component | Animation |
|---|---|---|
| Bar (all variants) | AnimatedBar | Height/width grows from axis origin |
| Stacked bar | AnimatedBar | Each stack segment animates sequentially |
| Column | AnimatedBar | Height grows from bottom axis |
| Diverging bar | AnimatedBar | Positive bars grow right, negative bars grow left |
| Exploded bar | AnimatedBar | Width grows with stagger across groups |
| Line | AnimatedLinePath | Path draws from left to right (stroke-dashoffset technique) |
| Area | AnimatedArea | Area fills up from the baseline |
| Line nodes | usePointGlide | Circles glide along the path as it draws |
| Dot plot | AnimatedCircle | Dots fade and scale in; connector line follows |
| Pie / Donut | AnimatedArc | Slices sweep in clockwise |
| Exploded pie | AnimatedArc | Slices sweep with offset stagger |
| Labels | AnimatedLabel | Fade at old position → tween geometry → fade in on data change |
Author-facing controls
The "Animation" inspector panel in the chart block (visible on animated chart types only) exposes:
| Control | Config key | Default |
|---|---|---|
| Enable animation | animation.enabled | false |
| Duration (ms) | animation.duration | 600 |
| Easing | animation.easing | 'easeInOut' |
| Stagger delay (ms) | animation.stagger | 80 |
| Preview in editor | animation.preview | false |
| Follow-on elements | animation.initial.follow | true |
Reduced-motion
When the reader's OS preference is prefers-reduced-motion: reduce, all animations resolve instantly. No author configuration required.
Editor behavior
The editor always renders charts in their final state unless "Preview animation" is toggled. This avoids distracting motion during block editing.
3. Bug fixes and chart action improvements (PRC-402)
Color cycling for pie and exploded-bar charts
Pie and exploded-bar charts with more categories than available palette colors now cycle through the color scale gracefully instead of repeating the last color or rendering transparent slices. Exploded-bar draggable labels also resolve color through colorScale for label/bar consistency.
Chart/data view toggle for nested controllers
The block-toolbar Chart/Data view toggle was not propagating correctly when the chart controller was nested inside another block (e.g. a grid or a columns block). The Redux store keyed by controller ID now scopes view state correctly for nested instances.
World map shape and region cuts
The world map topology has been updated with corrected boundary data and refined region cuts for Southeast Asia and Central Africa. Charts using map-world will automatically render with the corrected shapes on their next build.
Chart peripherals updatable via chart actions
setConfig patches can now reach title, subtitle, note, source, and tag fields. Previously these were excluded from the deep-patch merge path, requiring a PHP re-render to update. External tools and the debug surface can now update the full chart layout without a page reload.
Freeform metadata prepopulation
When a Freeform chart is inserted, the metadata fields (title, subtitle, note, source) are now prepopulated from the controller block's attributes. Previously Freeform charts had empty metadata until the producer manually filled them in.
Tooltip isBold dark-mode fix
Bold text in chart tooltips was not resolving the correct fill color in dark mode. The isBold style path now flows through the same getLabelFill helper used everywhere else in the renderer.
Chart peripherals stay in sync across client-side navigation
The visible data table and metadata text (title, subtitle, note, source, tag) now refresh after an Interactivity Router navigation, not only on first paint. The chart graphic already updated because it is its own data-wp-router-region; the surrounding table and metadata read the reactive store, which the router intentionally does not overwrite on navigate (populateServerData merges with override = false). chart/view.js now re-seeds the live chart slice from the fresh server payload on each navigation (syncOnNavigation), so the chart, table, and metadata reconverge together. See docs/reactive-store.md.
Data-tab table height tracks the chart as it renders
The "Data" tab table is sized to match its paired chart. Previously the match was measured once on a fixed 100ms timer, which raced the asynchronous mount of interactive charts (maps especially) and left the table too short. The height is now kept in sync with a ResizeObserver on the chart container, so the table matches as the chart finishes drawing and on any later resize. Static-image charts are unaffected.
4. Editor production tools and tab controls
Production Tools inspector panel
The chart block's inspector now has a consolidated "Production Tools" section that merges the former "Image and Data Exports" panel with new diagnostic utilities:
Reset Viewport Overrides Clears all mobile- and tablet-specific attribute overrides back to the base chart config in one click. Presented with a warning that the action is destructive (though recoverable from block history). Disabled when no overrides are present.
Force Regenerate PNG
Calls a new REST endpoint (POST /wp-json/prc-chart-builder/v1/chart/{id}/regenerate-png) that clears the cached attributes hash and re-enqueues the ScreenshotOne PNG generation job. Use this when the automatic screenshot on save renders incorrectly.
Copy Data & Config
Serialises the chart's current data (io.chartData) and full attribute config to JSON and copies it to the clipboard, ready to paste into a bug report or Slack message.
SVG download and PNG URL The SVG download button and live PNG URL display (previously in "Image and Data Exports") are now in this same panel.
Granular tab controls
The controller block's inspector "Tab Controls" panel replaces the single "Show tabs" toggle with individual controls for each frontend tab:
| Control | Default | Notes |
|---|---|---|
| Chart tab | On | |
| Data tab | On | |
| Allow data download | On | Disabled automatically when Data tab is off |
| Download Image tab | On | Only visible when a featured image / PNG is present |
| Share tab | On | |
| Include in structured data (schema.org) | On | Controls Dataset JSON-LD emission |
Hiding a tab removes it from the rendered HTML entirely. Producers who hid all but the chart tab previously had to use custom CSS — now it is a first-class editor option.
The "Allow data download" toggle has moved from the chart block's inspector to live alongside the Data tab toggle. It is automatically disabled when the Data tab is hidden, since users cannot download data they cannot see.
Unified debug surface
window.prcChartBuilder.debug is now registered by @prc/chart-builder (src/debug/index.js) rather than @prc/charting-library. The API is identical — getChart, setChart, setData, setConfig, setTableData, listCharts, chartUpdate — but it is now colocated with the rest of the chart builder's frontend code and no longer duplicated across two packages.
Behind the scenes
sync-charting-version.js— new script to keep README version lines in sync across@prc/charting-libraryand@prc/chart-builder..gitignore— adds.worktree/for agents using git worktrees during development.- BUNDLE-OPTIMIZATION.md — moved from
prc-charting-library/root intoprc-charting-library/docs/to keep docs organized. - Console helpers docs (
prc-chart-builder/docs/console-helpers.md) — updated to document the fullwindow.prcChartBuilder.debugAPI including the new action methods. - Removed dead
updateDataPOC — the unusedactions.updateDataquery-param navigation proof-of-concept inchart/view.jswas removed; runtime data updates go through thesetData/setChartaction surface.
Files Changed (high level)
| Area | Change |
|---|---|
prc-charting-library/src/ | Dual build: Preact frontend bundle + existing React editor bundle |
prc-charting-library/src/view.js | useChartStore, applyDeepPatch, Interactivity API store registration |
prc-charting-library/src/lib/Components/Animated*.tsx | New — animated bar, line, area, arc, circle, label components |
prc-charting-library/src/lib/Components/TransitionProvider.tsx | New — animation choreography provider |
prc-chart-builder/src/debug/index.js | New — unified debug surface |
prc-chart-builder/src/debug/chartUpdate.js | New — chartUpdate helper for debug surface |
prc-chart-builder/src/chart/view.js | Register debug surface; subscribe to Interactivity store; re-seed live slice on router navigation (syncOnNavigation); remove dead updateData POC |
prc-chart-builder/src/chart/class-chart.php | Add data-wp-watch--sync-navigation directive for the navigation re-seed |
prc-chart-builder/src/controller/view.js | Data-tab table height tracks the chart via ResizeObserver instead of a one-shot timer |
prc-chart-builder/src/chart/edit/production-controls.jsx | New — Production Tools inspector panel |
prc-chart-builder/src/chart/edit/chart-controls.jsx | Remove "Image and Data Exports" panel (merged into production-controls) |
prc-chart-builder/src/controller/block.json | New tab attributes: chartTabActive, dataTabActive, downloadImageTabActive |
prc-chart-builder/src/controller/Edit.jsx | Tab Controls panel with individual tab toggles; allowDataDownload moved here |
prc-chart-builder/src/controller/class-controller.php | Conditional HTML rendering gated on each tab's active attribute |
prc-chart-builder/includes/class-png-export.php | New REST endpoint for force-regenerate PNG |
Upgrade Notes
- No database migrations. All new attributes are additive with safe defaults.
- No breaking changes to published content. Existing charts continue to render identically.
- Animation is opt-in. No chart animates until a producer enables it in the Animation inspector panel.
- Tab controls are additive.
chartTabActive,dataTabActive, anddownloadImageTabActiveall default totrue. Existing controller blocks render all tabs as before. allowDataDownloadmoved. If you have automation or tooling that reads this attribute from the chart block's inspector controls, note that the UI toggle now lives in the controller block's "Tab Controls" panel. The underlying attribute location (io.allowDataDownloadon the chart block) is unchanged.- Debug surface ownership moved.
window.prcChartBuilder.debugis now populated by@prc/chart-builder. If you were relying on@prc/charting-libraryregistering this surface (e.g. in a standalone charting-library context), you will need to include@prc/chart-builder'sview.jsas well. - Preact frontend requires a rebuild. Run
npx turbo build --filter=@prc/charting-library --filter=@prc/chart-builderbefore deploying to ensure both the Preact frontend bundle and updated editor bundle are materialized.
Tracked Issues
Changesets
.changeset/chart-builder-3100-production-tools-tab-controls.md.changeset/charting-library-3100-debug-surface-cleanup.md.changeset/chart-builder-navigation-table-sync.md