Repository navigation
Global Styles: Allow editing duotone palettes - #81605
Conversation
jasmussen
left a comment
There was a problem hiding this comment.
Don't merge this until you get a green check from a dev review.
However on design and ux, this is how I expect it should work.
|
Thanks so much for reviewing it, UX and design wise! Very much agreed around the code. Happy to scale back the tests and in general have someone else take over. |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
I made some updates here on top of @annezazu's work (thank you for getting it started!)
Kapture.2026-08-18.at.15.48.54.mp4There's a conflict with changelog, but we can take care of that later coz it'll just pop up again as folks merge more PRs that update that file. |
| name | ||
| ); | ||
|
|
||
| const [ customDuotone ] = useSetting( 'color.duotone.custom' ) || []; |
There was a problem hiding this comment.
pure deletion: the read-only duotone list moves out of the Gradient tab into the new Duotone tab.
There was a problem hiding this comment.
Pull request overview
Adds editable duotone palettes to Global Styles and ensures custom duotones render in the editor.
Changes:
- Adds a Duotone palette tab and editor.
- Extends
PaletteEditandDuotonePickerfor duotones. - Generates editor SVG filters for custom presets.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
tools/eslint/suppressions.json |
Updates suppression count. |
packages/global-styles-ui/src/style.scss |
Styles the duotone panel. |
packages/global-styles-ui/src/screen-color-palette.tsx |
Adds the Duotone tab. |
packages/global-styles-ui/src/gradients-palette-panel.tsx |
Removes read-only duotones. |
packages/global-styles-ui/src/duotone-palette-panel.tsx |
Implements palette editing. |
packages/global-styles-ui/CHANGELOG.md |
Records the enhancement. |
packages/global-styles-engine/src/test/render.test.ts |
Tests custom SVG filters. |
packages/global-styles-engine/src/core/render.tsx |
Generates custom-origin filters. |
packages/global-styles-engine/CHANGELOG.md |
Records the filter fix. |
packages/components/src/palette-edit/types.ts |
Adds duotone types. |
packages/components/src/palette-edit/test/index.tsx |
Tests adding and editing. |
packages/components/src/palette-edit/style.scss |
Outlines the duotone bar. |
packages/components/src/palette-edit/stories/index.story.tsx |
Adds a duotone story. |
packages/components/src/palette-edit/index.tsx |
Implements duotone editing. |
packages/components/src/duotone-picker/types.ts |
Adds preset metadata callbacks. |
packages/components/src/duotone-picker/stories/duotone-picker.story.tsx |
Handles callback metadata. |
packages/components/src/duotone-picker/duotone-picker.tsx |
Reports selected preset identity. |
packages/components/CHANGELOG.md |
Documents component changes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "packages/global-styles-ui/src/gradients-palette-panel.tsx": { | ||
| "@wordpress/use-recommended-components": { | ||
| "count": 2 | ||
| "count": 1 |
There was a problem hiding this comment.
__experimentalSpacer as Spacer, was dropped
4b27297 to
e009fe1
Compare
| <CustomDuotoneBar | ||
| value={ getDuotoneColors( element ) } | ||
| onChange={ ( newColors ) => { | ||
| // A duotone needs two colors to render, and the | ||
| // bar refuses to drop below two stops, so an | ||
| // empty value means there is nothing to save. | ||
| if ( ! newColors?.length ) { | ||
| return; | ||
| } | ||
| onChange( { ...element, colors: newColors } ); | ||
| } } | ||
| /> |
There was a problem hiding this comment.
The duotone bar tells users they can move gradient stops ("Use your left or right arrow keys or drag and drop … to change the gradient position."), but it discards their position data? That instruction describes an operation the duotone model cannot save.
This behavior probably already existed in CustomDuotoneBar, but it would be nice to fix it (since it's now being used more)
There was a problem hiding this comment.
I can create an issue. I don't want to steer too far from the original premise of this PR.
There was a problem hiding this comment.
| onChange( | ||
| isSelected ? undefined : colors, | ||
| index, | ||
| slug | ||
| ); |
There was a problem hiding this comment.
DuotonePicker.onChange previously received one argument and now receives three for preset clicks; this is an API change that would cause TS errors (ie. cause breakages).
The older one-argument handlers remain safe, but we may get TypeScript errors on handlers that infer and forward the complete arg list.
This is also shown in the Storybook example from this PR, where the Storybook wrapper had to change from (...changeArgs) => setValue( ...changeArgs ) to extracting newValue before forwarding the remaining arguments.
Maybe we could preserve the single-arg onChange( value ) contract and expose preset identity through a new optional callback such as onPresetSelect?
There was a problem hiding this comment.
Also, related to this comment, we should update README.md, where onChange is only described as a generic function. We could take inspiration from GradientPicker?
There was a problem hiding this comment.
Thanks for picking that up. 🙇🏻
Tested and it looks like onChange={ ( ...args ) => setValue( ...args ) } would fail (TS2554).
I'm a bit torn on the advice here for the following reasons:
GradientPickerhad this exact bug and it was fixed the same way in GradientPicker: select by slug so two presets sharing a gradient keep their identity #80554onChange( value, index, slug )is already the convention in both sibling pickers
Happy to go the onPresetSelect route if you still prefer it, and the severity level/risk is high, though it'll leave DuotonePicker as the outlier.
README updated in a207a55 👍🏻
There was a problem hiding this comment.
Right, I guess consistency is important, too. We can probably keep it as-is.
Also cc @WordPress/gutenberg-components in case they have other suggestions
|
UX-wise, I second @jasmussen's words. This is the UX I expect. Very good work ✨ |
Adds a Duotone tab to the Global Styles "Edit palette" screen, alongside Color and Gradient, so theme and default duotones can be edited and custom duotones added and removed. Duotone was previously shown as a read-only swatch grid at the bottom of the Gradient tab. That block is removed; the Gradient tab is now only about gradients. `PaletteEdit` gains a third variant rather than growing a parallel component: the private `isGradient` boolean becomes a `variant` discriminator, and a new `duotones` prop (with an optional `colorPalette`) renders the duotone editor from the pieces `DuotonePicker` already uses — `CustomDuotoneBar` and `ColorListPicker` for the popover, `DuotonePicker` for the collapsed preview, and `getDefaultColors` to seed a newly added duotone from the site's palette. Palette colors that do not resolve to a concrete value are filtered out of the duotone paths. Duotone values become SVG filter matrices, so a color like `color-mix(...)` — which Twenty Twenty-Five ships — cannot be used. `colord` treats it as black, so without the filter it would win as the "darkest" color and seed every new duotone with a value neither the editor nor the front end can render. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to the duotone palette editing work, on top of annezazu's commit. - Fill in the CHANGELOG pull request number placeholders. - Don't persist a duotone with no colors when the bar reports an empty value. The gradient bar refuses to drop below two stops, so the old fallback was unreachable and encoded the wrong recovery anyway. - Match DuotonePicker's falsy check when refilling a cleared shadow or highlight, rather than a nullish one. - Memoize the color palette handed to the duotone panel, so the lookups that PaletteEdit and DuotonePicker memoize on it are not redone on every render. - Outline the duotone bar in the palette edit popover. A duotone with a white highlight was indistinguishable from the popover background, so you could not tell where the draggable area ended.
Two duotone presets can hold the same pair of colors, so the value alone does not identify which one was picked. Adding two duotones with the "+" button seeds both from the palette's darkest and lightest colors, so duplicate values are the normal case rather than an edge one. PaletteEdit was recovering the index with findIndex over the colors, which returns the first match, so editing the second of two duotones silently rewrote the first. It now uses the index the picker reports, which is how ColorPalette already works. The index and slug are optional trailing arguments, so existing consumers are unaffected. The unset, clear and custom bar paths pass neither, since they do not correspond to a preset.
The editor generated duotone filter definitions for the theme and default origins only. The front end has no such restriction, since WP_Duotone iterates every origin, so a duotone a user had created rendered on the front end but pointed at a filter that did not exist on the canvas. Applying it showed no preview. This was only reachable once custom duotone presets could be created through the UI. The block toolbar's custom duotone is a separate path that injects its own filter inline, so it was unaffected.
colord in the editor accepts CSS named colors, because namesPlugin is registered globally across the bundle. The front end parses duotone colors with a PHP port of colord that only accepts hex, rgb() and hsl(), so a palette color such as "black" saved into a duotone rendered in the editor and was dropped on the front end with a _doing_it_wrong notice. Colors surviving the existing validity filter are now normalized to hex. The same normalized array feeds the shadows and highlights picker, so the selected swatch still matches the saved value. No bundled theme ships a named palette color, so this is latent rather than something users hit today.
- Add a `selectedSlug` prop, mirroring `ColorPalette` and `GradientPicker`. Selection compared colors only, so two presets holding the same pair both appeared selected, and clicking either cleared the value rather than picking it. - Add tests for `DuotonePicker`, which had none. Duplicate presets render, select by slug, fall back to color matching when no slug is given, and report their index and slug on click. - Cover the duotone edit path in `PaletteEdit`. Color filtering and normalization were only tested when adding a duotone, so the edit path could regress without failing a test. - Name each swatch picker after its palette heading, so it is announced as Theme, Default or Custom rather than as an unlabelled list. - Document `onChange` and `selectedSlug` in the README, following the wording used by `GradientPicker`.
The story forwarded onChange's arguments blindly, which demonstrated nothing about what the index and slug are for, and left selectedSlug undocumented in Storybook. Follows the shape GradientPicker's story already uses: names all three arguments, holds the slug in state and feeds it back, and adds a DuplicateDuotones story where two presets share a pair of colors, so slug-based selection is visible.
Clicking the selected preset passed its index and slug back alongside an undefined value, so a controlled consumer storing the slug kept marking the swatch as selected after its value had been cleared. Deselecting now calls onChange( undefined ) alone, which is what ColorPalette and GradientPicker do through clearColor and clearGradient. Adds controlled tests covering deselection and moving the selection, which the existing uncontrolled tests could not catch.
4197958 to
d4522d6
Compare
|
I think this is ready for a final test. One outstanding point: #81605 (comment) If we merge and find out later there's an alternative, I'm happy to put a follow up on my list. Cheers, folks! |
Excellent. Thank you! |
Two presets can hold the same pair of colors, and adding custom duotones seeds each one from the palette's darkest and lightest, so duplicates are the ordinary case rather than a contrived one. The applied preset was resolved by matching colors, which finds whichever comes first. The block toolbar and the block inspector both now carry the slug: - The toolbar reports the picked slug on change and receives the stored one back as selectedSlug. - The inspector panel does the same, reading the slug from the raw value. It decodes for display, and decoding resolves a preset reference to its colors, so the hook now hands it the reference rather than the result. getDuotoneSlugFromPreset lives in components/global-styles/utils rather than beside its siblings in hooks/duotone, because that module imports the filters panel and the panel cannot import back from it.
The hairline added around the duotone bar is inconsistent with every other place the gradient bar is used, and whether the bar needs a visible edge against the popover background is a design decision rather than something to settle here.
andrewserong
left a comment
There was a problem hiding this comment.
This is testing great for me! Just gave it a quick look over and re-test again, and I think this is in a good place to land. It's quite a large PR so my vote would be to get it in, and we can continue polishing interactions, etc in follow-ups. Unnecessary video of me excitedly creating new Duotones:
2026-08-20.16.46.38.mp4
Smoke-tested that creating and editing color and gradient presets is still working nicely, too.
LGTM 🚀
|
Thanks for all the help getting this off the ground folks.
Happy to look after any follow ups. Non-shipping code (tests + stories) make up just more than 50%! |
|
This is a nice old issue fixed, thanks all for helping! |
|
Thank you all so much for taking this over and making it happen in all the ways. |
Keep the Duotone picker from WordPress#81605 and apply asButtons to Color, Gradient, and Duotone swatches. Point the CHANGELOG entry at WordPress#81836. Co-authored-by: Cursor <cursoragent@cursor.com>
This updates the pinned commit hash of the Gutenberg repository from `ae8b34d78e1abc6d1c866fdc00080a1452de82ad ` (version `23.8.0`) to `658c8cbe89db5d90279051d3bb816ca210fe4646` (version `23.9.0`). A full list of changes included in this commit can be found on GitHub: https://github.com/WordPress/gutenberg/compare/v23.8.0..v23.9.0. - Env: Update git to latest commit when `--update` passed (WordPress/gutenberg#81447) - InputControl: Vertically center date and time input values in Safari (WordPress/gutenberg#81361) - DataViews: vendor `ValidatedComboboxControl` (WordPress/gutenberg#81449) - Icon: Fix margin being applied twice in the editor (WordPress/gutenberg#81292) - Docs: Add Background image control Readme (WordPress/gutenberg#69409) - Dimensions: Fix aspect ratio and scale controls not reflecting external updates (WordPress/gutenberg#80747) - DataViews: vendor `ValidatedFormTokenField` (WordPress/gutenberg#81451) - Docs: Fix dead link in the dynamic blocks tutorial (WordPress/gutenberg#81399) - DataViews: vendor `ValidatedToggleGroupControl` (WordPress/gutenberg#81450) - Update 23.8 release changes on trunk (WordPress/gutenberg#81485) - Adds README for inspector-controls-tabs component (WordPress/gutenberg#69072) - BlockEditVisuallyButton: Update Developer Documentation (WordPress/gutenberg#68987) - Storybook: Upgrade Vite to 8 (WordPress/gutenberg#81494) - Packages: Move unreleased changelog entries out of the 23.8 published versions (WordPress/gutenberg#81502) - Theme: Avoid root-level relational selectors (WordPress/gutenberg#81457) - Theme: Move changelog entry to unreleased (WordPress/gutenberg#81508) - Admin UI: Add Navigation component and Page navigation slot (WordPress/gutenberg#79746) - Editor: Correctly mark ToolsMoreMenuGroup as a private SlotFill (WordPress/gutenberg#81503) - Fix: Term Description block skips display filters inside Terms Query. (WordPress/gutenberg#81290) - DataForm: Fix the datetime control sending two updates per calendar interaction (WordPress/gutenberg#81440) - Editor: Stop `:has()` selectors recalculating the whole document on every block selection (WordPress/gutenberg#81471) - Fix: getQueryArgs discards everything after the second = in a query argument value (WordPress/gutenberg#81066) - DOM: treat a caret at a soft line wrap as an ambiguous position in Firefox (WordPress/gutenberg#81003) - In-between indicator: make symmetric (WordPress/gutenberg#68074) - Fix: Delete key on empty heading transforms following paragraph into heading (WordPress/gutenberg#78779) - Media editor: keep initial modal focus on the dialog frame (WordPress/gutenberg#81541) - Writing flow: select next block on Enter key (WordPress/gutenberg#63484) - Media Editor Route Experiment: Override media edit links to point to the new experimental media editor (WordPress/gutenberg#81559) - DataViews: vendor `ValidatedToggleControl` (WordPress/gutenberg#81492) - Release: Improve changelog categorization (WordPress/gutenberg#81475) - Docs: Add Block Editing Mode Readme. (WordPress/gutenberg#69411) - Editor: Use PluginSidebar in GlobalStylesSidebar (WordPress/gutenberg#81570) - Docs: Fix broken links to the Block API reference guides. (WordPress/gutenberg#81571) - Adds JSdoc for `column` and `columns` block's edit.js (WordPress/gutenberg#68873) - Adds JSdoc for `audio` block's edit.js (WordPress/gutenberg#68865) - Refactor: Convert `createColorHOC` to functional component (WordPress/gutenberg#70408) - Adds JSdoc for `avatar` block's edit.js (WordPress/gutenberg#68867) - Editor: Speed up the hierarchical term selector for large taxonomies (WordPress/gutenberg#81376) - Extensible Site Editor: Preview the site on Home for classic themes (WordPress/gutenberg#81578) - TypeScript: Add typecheck script and split root tsconfig into build and dev solutions (WordPress/gutenberg#81499) - Packages: Split blob, grid, interactivity, and theme tsconfigs (WordPress/gutenberg#81509) - Vitest: Resolve @flakiness/vitest reporter to an absolute path (WordPress/gutenberg#81582) - Update waveform player dependency patch (WordPress/gutenberg#81454) - Dashboard Widgets: promote high-relevance actions into a chrome footer (WordPress/gutenberg#81556) - Extensible Site Editor: Gate theme screens on theme support (WordPress/gutenberg#81581) - Editor: Register the editor keyboard shortcuts from the provider (WordPress/gutenberg#81580) - Base styles: Streamline focus ring override (WordPress/gutenberg#81242) - Components: Align legacy form control focus rings with WPDS (WordPress/gutenberg#80417) - Base Styles: Align `input-control` mixin to design system (WordPress/gutenberg#81357) - Add Gutenberg version details for WordPress 7.1 (WordPress/gutenberg#81600) - Fix viewport state values set by grid resizer (WordPress/gutenberg#81601) - Block editor: add an inserter to the parent selector (WordPress/gutenberg#81532) - url: clamp the truncated file name slice in filterURLForDisplay (WordPress/gutenberg#81529) - Fix parent inserter alignment with text labels enabled (WordPress/gutenberg#81608) - URL: Stop normalizePath throwing on a malformed percent sequence (WordPress/gutenberg#81086) - Extensible Site Editor: Add the Identity route (WordPress/gutenberg#81576) - ComboboxControl: narrow `onChange` callback type to `string` | `null` (WordPress/gutenberg#81568) - Storybook: Add Story for URLPopup component (WordPress/gutenberg#68449) - Extensible Site Editor: Show only the style book on Styles for classic themes (WordPress/gutenberg#81579) - Global Styles: Expose additional elements in Typography and Colors (WordPress/gutenberg#80852) - Storybook: Add Story for BlockPopover (WordPress/gutenberg#68681) - Block Editor: Never spotlight blocks in a preview (WordPress/gutenberg#81615) - Boot: Let the editor canvas navigate between entity records (WordPress/gutenberg#81590) - Block API: Declare block keyboard shortcuts on variations and transforms (WordPress/gutenberg#81588) - Storybook: Add Story for InnerBlocks (WordPress/gutenberg#68651) - Boot: Translate the canvas click-to-edit label (WordPress/gutenberg#81620) - Perf: Report the first block selection as its own metric (WordPress/gutenberg#81616) - Boot: Contain a surface failure to that surface (WordPress/gutenberg#81622) - Boot: Warn before leaving with unsaved changes (WordPress/gutenberg#81625) - Env: Fall back to cached docker images when the registry is unreachable (WordPress/gutenberg#81631) - Editor: Refactor 'ErrorBoundary' to use the new recommended components (WordPress/gutenberg#81638) - Fix playlist track toolbar insertion (WordPress/gutenberg#81639) - Playlist Track: Show upload spinner inline (WordPress/gutenberg#81445) - Admin UI: Fix the primary color of the Fresh admin color scheme (WordPress/gutenberg#81618) - Inner Blocks: Fix bug that caused alignment controls to show in Image blocks within a Gallery (WordPress/gutenberg#81606) - Layout block supports: cast numeric grid attributes before use (WordPress/gutenberg#81560) - Make Back button grid column flexible. (WordPress/gutenberg#81701) - List View: Make focus on mount opt in (WordPress/gutenberg#81659) - Lazy Editor: Render the editor preferences modal (WordPress/gutenberg#81630) - Edit Site Init: Seed the editor preferences the editor expects (WordPress/gutenberg#81628) - Boot: Do not offer to edit a trashed entity (WordPress/gutenberg#81632) - Block API: Address review feedback on block keyboard shortcuts (WordPress/gutenberg#81621) - Editor: Simplify List View sidebar shortcut handling (WordPress/gutenberg#81693) - RichText: Use the current record when handling Enter (WordPress/gutenberg#81696) - Boot: Sync the editor's device preview with the URL (WordPress/gutenberg#81617) - Stop saving Table of Contents output (WordPress/gutenberg#80404) - Lazy Editor: Keep the theme's editor styles on the canvas (WordPress/gutenberg#81747) - List View: Fix flaky global shortcut test and re-enable it (WordPress/gutenberg#81690) - Boot: Preview the site where the canvas has nothing to open (WordPress/gutenberg#81749) - Docs: Require site editor features to land in the extensible site editor too (WordPress/gutenberg#81752) - Build Tools: Stop watcher process trees on shutdown (WordPress/gutenberg#81504) - Update Terrazzo packages to 2.5.0 (WordPress/gutenberg#81082) - Site Editor: Use inverted ThemeProvider seed for portaled UI (WordPress/gutenberg#81653) - Components: Migrate DropdownContentWrapper to SCSS module (WordPress/gutenberg#81522) - Playlist: Add aria-label to prevent placeholder as label (WordPress/gutenberg#81654) - UI: Collapse item-popup item sizing to default and small (WordPress/gutenberg#81354) - Add `__unstable_wp_sync_storage` filter for pluggable storage backends (WordPress/gutenberg#81697) - Bump github/codeql-action/upload-sarif (WordPress/gutenberg#81436) - Feat: support block spacing (WordPress/gutenberg#79689) - Enable axial gap for Group and restrict to flex and grid layouts. (WordPress/gutenberg#81476) - Packages: Split tsconfigs for utility and mid-size packages (WordPress/gutenberg#81514) - Media Editor Route: Make the route look like it's part of the media menu in the left-hand sidebar (WordPress/gutenberg#81565) - Global Styles: filter and indicate blocks that have custom styles (WordPress/gutenberg#81373) - Prevent white screen crash during undo operations when Media Modal is open (WordPress/gutenberg#79898) - Fix clipped focus outlines in the media editor Details sidebar (WordPress/gutenberg#81703) - Fix: URL: normalizePath drops the rest of the query after a second "?" (WordPress/gutenberg#81612) - Block Editor: Add translator context to pseudo-state labels (WordPress/gutenberg#81770) - Global styles: Register <label> as element (WordPress/gutenberg#81160) - Site editor: update template section description. (WordPress/gutenberg#81613) - Fix: Accordion: Anchored hash inside the accordion throws the malformed URL console error (WordPress/gutenberg#81780) - BoxControl: update the opposite side when ALT is held on the left or right input (WordPress/gutenberg#81530) - Tabs: Give tab IDs their own counter so numbering starts at 1 and never skips (WordPress/gutenberg#81781) - Modal: Stop Escape key propagation on dismiss (WordPress/gutenberg#81785) - RTC: Couple RTC and polling provider as an opt-in experiment (WordPress/gutenberg#80658) - ui/Card: Use the default neutral border (WordPress/gutenberg#81746) - Update playlist track placeholder to match aria label (WordPress/gutenberg#81762) - Notes: keep the note actions menu in view for long display names (WordPress/gutenberg#81414) - Fix DataViews list layout color token usage (WordPress/gutenberg#81074) - UI: Add Spinner component (WordPress/gutenberg#81358) - Fields: Export the package stylesheet (WordPress/gutenberg#81769) - Fix the failing e2e test (WordPress/gutenberg#81739) - RTC: Add fallback identities when user profiles are unavailable (WordPress/gutenberg#81408) - Align collaboration migration version markers with the 23.8.0 release (WordPress/gutenberg#81789) - Dashboard: add a Site Health detail page as the dashboard's second route (WordPress/gutenberg#81729) - Dashboard Widgets: resolve widget types without a metadata module (WordPress/gutenberg#81738) - Theme: Improve color ramp contrast safeguards (WordPress/gutenberg#81185) - More block: clarify description about excerpt vs full content (WordPress/gutenberg#80619) - Schemas: Document what the theme.json schema checks (WordPress/gutenberg#81773) - Components, DataViews, Editor, UI: Split tsconfigs into build and dev projects (WordPress/gutenberg#81515) - Command Palette: Allow commands to override the category icon (WordPress/gutenberg#81787) - Fix Broken Links in Package file (WordPress/gutenberg#81700) - Notes: Stop forcing capitalization of user names (WordPress/gutenberg#81788) - Media Editor Route: Add undo/redo and save/cancel actions to the header (WordPress/gutenberg#81563) - Fix: Post Editor: Canvas animates from the left when switching mobile/tablet preview to desktop (WordPress/gutenberg#81484) - Build Package: Use Core's boot module when a plugin has none (WordPress/gutenberg#81761) - Docs: Fix broken link to the Document-Isolation-Policy explainer. (WordPress/gutenberg#81790) - Tests: Make PHPUnit tests independent of test config flags and theme root (WordPress/gutenberg#79159) - Editor: Debounce term filtering and announce results once (WordPress/gutenberg#81422) - Packages: Split story package tsconfigs into build and dev projects (WordPress/gutenberg#81516) - Packages: Complete the tsconfig build and dev project split (WordPress/gutenberg#81517) - TypeScript: Require the typecheck run for type checking in CI (WordPress/gutenberg#81518) - Block Editor: Remove leftover files (WordPress/gutenberg#81793) - Rename the media editor toolbar button to 'Edit image' (WordPress/gutenberg#81705) - Playlist: Remove block toolbar sharing between parent and child blocks (WordPress/gutenberg#81432) - Components: Fix Storybook accessibility issues in stories (WordPress/gutenberg#81777) - Fields: migrate last remaining JS file to TS (WordPress/gutenberg#81808) - Menu: Add UI component (WordPress/gutenberg#79560) - Patterns explorer: Refactor the category sidebar to use Tabs (WordPress/gutenberg#81807) - add context to change "revisions code diff" (WordPress/gutenberg#81820) - Add command: View post (WordPress/gutenberg#66720) - Fix: Post Editor: 403 on /wp/v2/settings at boot for users without manage_options cap (WordPress/gutenberg#81813) - UI: Add initial agents guidance mapping to contributing guidelines (WordPress/gutenberg#81400) - UI: Mark Input-related components as recommended (WordPress/gutenberg#81658) - Post Template: Fix mover arrows and labels for blocks inside the grid view (WordPress/gutenberg#81120) - Docs: Fix broken link to the getEntityRecords selector reference. (WordPress/gutenberg#81791) - Quick Inserter: Standardize the design of the Browse all button (WordPress/gutenberg#81827) - UI: Add TextareaControl component (WordPress/gutenberg#81359) - Docs: Narrow the tsconfig split guidance to the standard layout (WordPress/gutenberg#81828) - Storybook: Fix production build warnings (WordPress/gutenberg#81758) - Components: migrate ResizableBox to SCSS module- WordPress/gutenberg#80720 (WordPress/gutenberg#81792) - Bump astral-sh/setup-uv from 9.0.0 to 10.0.0 in /.github/workflows (WordPress/gutenberg#81823) - Global Styles: Keep storing shadow presets as CSS variables when custom presets exist (WordPress/gutenberg#81346) - Docs: Fix broken skip ahead anchor on the custom block editor guide. (WordPress/gutenberg#81835) - Fix MediaPlaceholder drag eligibility (WordPress/gutenberg#81431) - Updated Aria Landmark Example URL (WordPress/gutenberg#81832) - TypeScript: Put route projects under the typecheck run (WordPress/gutenberg#81829) - Add missing dependency to media-editor route (WordPress/gutenberg#81845) - Add transform to Row for Columns block (WordPress/gutenberg#81802) - Editor: Remove FlexItem usage from NoteCard (WordPress/gutenberg#81851) - UI: Expose keyboard shortcut presentation utilities (WordPress/gutenberg#81826) - Theme: Add color warning diagnostics workbench (WordPress/gutenberg#81817) - Editor: Surface debugging details in the ErrorBoundary (WordPress/gutenberg#81642) - Update save & submit docs (WordPress/gutenberg#81077) - Boot: Migrate styles to CSS Modules (WordPress/gutenberg#81756) - Build: Emit TypeScript declarations with --noCheck (WordPress/gutenberg#81839) - Core Data: Validate the parsed-blocks cache against registered block types (WordPress/gutenberg#81809) - Dependencies: Dedupe, update adm-zip, nx (WordPress/gutenberg#81870) - Core Data: Allow keyless entities to be addressed without a record ID (WordPress/gutenberg#81857) - UI: Mark Autocomplete, Field, and Fieldset as recommended (WordPress/gutenberg#80636) - Editor: Announce publish date changes to screen readers (WordPress/gutenberg#81629) - Storybook: Fix default background color for Example Application theme story (WordPress/gutenberg#81765) - Fix/79641 rtc cursor hidden details block v2 (WordPress/gutenberg#81322) - UI: add ControlWithError component (WordPress/gutenberg#81574) - Global Styles: Group shadow editor rows as items (WordPress/gutenberg#81871) - Storybook: Handle production module load failures (WordPress/gutenberg#81867) - Pattern: Disable visibility support (WordPress/gutenberg#81866) - Design System: Add portable agent skills (WordPress/gutenberg#80597) - Global Styles: Allow editing duotone palettes (WordPress/gutenberg#81605) - Color Palette panel: Remove empty whitespace when there are no theme colors (WordPress/gutenberg#81894) - Try caching gutenberg_get_global_styles (WordPress/gutenberg#81889) - Refactor parameter type hints and clean comments (WordPress/gutenberg#81898) - Preserve layout and style attributes on Column transform (WordPress/gutenberg#81855) - Global Styles: Close the revisions screen on the first back click (WordPress/gutenberg#81897) - Editor: Set the width an entity opens at from the editor provider (WordPress/gutenberg#81750) - Pre-publish panel: Remove the headings that repeat the panel title (WordPress/gutenberg#81806) - Components: migrate ComboboxControl to SCSS module (WordPress/gutenberg#80471) - UI: Update Base UI to 1.7.0 (WordPress/gutenberg#81390) - Update Ariakit packages (0.4.35 => 0.4.37) (WordPress/gutenberg#81080) - chore: Update the combobox SCSS to have a breaking changes log (WordPress/gutenberg#81923) - Use `WCTextareaControl` alias for legacy TextareaControl imports (WordPress/gutenberg#81834) - Components: Improve text detection for button icon-with-text (WordPress/gutenberg#81521) - Font Library: Fix the preview weight for variable fonts (WordPress/gutenberg#81748) - Editor: Migrate hierarchical term selector to @wordpress/ui (WordPress/gutenberg#81848) - Validation: update dependency-audit to 0.4.6 and ignore dataviews ./wp bundle (WordPress/gutenberg#81928) - SearchableChipSelect: Add grouped items support (WordPress/gutenberg#80989) - Use `WCInputControl` alias for legacy InputControl imports (WordPress/gutenberg#81830) - Storybook: Upgrade to 10.5 (WordPress/gutenberg#80692) - Documentation: Update coding guidelines to recommend TypeScript (WordPress/gutenberg#81763) - Font library: Add skeleton loader for the font preview images (WordPress/gutenberg#81047) - Build Scripts: Force tsc --pretty so CI reports the project behind global diagnostics (WordPress/gutenberg#81937) - Update is-plain-object to 5.1.0 (WordPress/gutenberg#81926) - DataViews: Sync the displayed calendar month with external value changes (WordPress/gutenberg#81635) - DataForm: Make the panel layout edit button the real field trigger (WordPress/gutenberg#80689) - Dependency updates Aug 2026 (2) (WordPress/gutenberg#81906) - Playlist: Add bulk track metadata editing (WordPress/gutenberg#81441) - Automated Testing: Change Storybook default a11y test to error on failure (WordPress/gutenberg#81597) - Playlist Track: Improve track upload error flows (WordPress/gutenberg#81878) - Block Library: Fix failing test in playlist block (WordPress/gutenberg#81950) - Block editor: Move isElementVisible changelog entry to Unreleased (WordPress/gutenberg#81953: shail-mehta <shailu25@git.wordpress.org> Co-authored-by: Mamaduka <mamaduka@git.wordpress.org> - DataViews: Fix date and datetime controls selecting the adjacent day (WordPress/gutenberg#81498) - UI: Accept locale codes in Calendar components (WordPress/gutenberg#81814) - List: Add Align Support (WordPress/gutenberg#68002) - TypeScript: Bring the remaining route projects under the typecheck run (WordPress/gutenberg#81847) - Components: migrate GradientPicker, ColorPalette to SCSS module (WordPress/gutenberg#80473) - Docs: Align @PARAM tag columns in test/ docblocks (WordPress/gutenberg#81905) - Hide layout panel when no controls are available (WordPress/gutenberg#81968) - UI: Add ValidatedInputControl component (WordPress/gutenberg#81627) - Components: migrate StyledLabel to SCSS module (WordPress/gutenberg#80001) - fix: Button rtl loader issue (WordPress/gutenberg#81901) - Theme: Update Terrazzo packages to 2.7.1 (WordPress/gutenberg#81978) - CollapsibleCard: Support multiple header descriptions (WordPress/gutenberg#81227) - DataViews: use `Menu` from `@wordpress/ui` (WordPress/gutenberg#81783) - DataViews: Localize the date/datetime calendars from the site locale and direction (WordPress/gutenberg#81592) - Replace the Storybook test runner with the Vitest integration (WordPress/gutenberg#81918) - Block Editor: Don't close inserter panel when inline quick inserter opens (WordPress/gutenberg#76241) - Updated broken links in document files (WordPress/gutenberg#81961) - Boot: Mount the registered plugins (WordPress/gutenberg#81754) - Extensible Site Editor: Add a theme preview page with global styles editing (WordPress/gutenberg#81954) - Block Styles: Refactor the variation UI to use ui/Button (WordPress/gutenberg#81833) - Styles Route: Reference the api-fetch and html-entities projects (WordPress/gutenberg#81989) - Fix: Line Break Tags Added to Scripts and Styles (WordPress/gutenberg#77542) - Fix: skip empty block markup in navigation to prevent split UL elements (WordPress/gutenberg#78793) - Pattern List: Import a pattern from JSON (WordPress/gutenberg#81919) - UI: Add tooltips to Calendar and RangeCalendar navigation buttons (WordPress/gutenberg#81983) - UI: Add `ValidatedTextArea` control (WordPress/gutenberg#81984) - Fix: Responsive styles: When enabled and a font-size is setting, the corresponding CSS variable remains undefined (WordPress/gutenberg#81854) - Components: Migrate `TextareaControl` to SCSS module (WordPress/gutenberg#81353) - UI: Set Calendar text direction automatically (WordPress/gutenberg#81982) - Theme: Rename ThemeProvider wrapper CSS class from root to wrapper (WordPress/gutenberg#81996) - Editor: Offer the theme export from both site editors (WordPress/gutenberg#81992) - Fix: Stop stripping spaces inside inline HTML elements on paste (WordPress/gutenberg#76696) - DataViews: Add a lint rule preventing private API imports (WordPress/gutenberg#81478) - Components: Migrate ProgressBar styles from Emotion to SCSS module (WordPress/gutenberg#80512) - RTC: Disable syncing for unpersistable post types (WordPress/gutenberg#81946) - Boot: Restore centered snackbar notices (WordPress/gutenberg#81995) - BorderControl, BorderBoxControl: Migrate styles to CSS Modules (WordPress/gutenberg#80437) - Duotone Picker: Stop the duotone bar offering to move its control points (WordPress/gutenberg#81850) - DataViews: Add an aspect ratio toggle for grid layouts (WordPress/gutenberg#81604) - Remove `fixable` on `i18n-no-flanking-whitespace` (WordPress/gutenberg#70279) - Dynamic Gallery block: Skip transforms that expect inner blocks, e.g. Image and Grid transforms (WordPress/gutenberg#82009) - Accordion Heading: Route theme.json spacing to the toggle button (WordPress/gutenberg#81976) - Element: Serialize memo-wrapped components instead of an empty string (WordPress/gutenberg#81986) - Style: Update color to use CSS variable in global-styles-ui (WordPress/gutenberg#79053) - Dashboard Widgets: let the host decide which component renders an action link (WordPress/gutenberg#81740) - Tabs: make inactive panel content findable in page (WordPress/gutenberg#81743) - Border Panel: Fix incorrect panel label (WordPress/gutenberg#78830) - Components: Migrate ZStack styles from Emotion to SCSS module (WordPress/gutenberg#80514) - Storybook: Add missing @types/jest devDependency (WordPress/gutenberg#82017) - TypeScript: Drop redundant undefined casts for keyless entity record IDs (WordPress/gutenberg#82018) - Editor: Fix start page/template modal footer overflow (WordPress/gutenberg#82021) - Storybook: Keep Vitest story scanning independent of the Vite root (WordPress/gutenberg#82020) - Test: Port shared Vitest test environment (WordPress/gutenberg#81038) - Query No Results: Add Border & Spacing Support (WordPress/gutenberg#64601) - RadioControl: allow individual options to be disabled (WordPress/gutenberg#82026) - Components: Migrate `BoxControl` to SCSS module (WordPress/gutenberg#80715) - SearchableChipSelectControl: Add form control to @wordpress/ui (WordPress/gutenberg#80980) - UI: Move controlled prop guidance to contributing docs (WordPress/gutenberg#82044) - Components: Migrate Scrollable to SCSS module (WordPress/gutenberg#80694) - UI: Fix accessibility issues in form primitives and their stories (WordPress/gutenberg#81853) - UI: Align Calendar styling with WPDS tokens (WordPress/gutenberg#81438) - Calendar: Support custom root roles (WordPress/gutenberg#81443) - E2E Tests: Use str_starts_with()/str_contains() in hide-user-rest-routes plugin (WordPress/gutenberg#82030) - Site editor v2: Fix block error with navigation-edit route (WordPress/gutenberg#82051) - ActionItem: Move the more menu deduplication out of the Slot (WordPress/gutenberg#81507) - DataViews: Fix invisible sticky Actions column divider (WordPress/gutenberg#82055) - Quick draft: replace Button+Link with LinkButton (WordPress/gutenberg#82031) - Lint: Prevent private API imports in all bundled packages (WordPress/gutenberg#82015) - PaletteEdit: Expose palette swatches as command buttons (WordPress/gutenberg#82023) - Test: Add Vitest React, Emotion compilation, console, and snapshot parity (WordPress/gutenberg#81039) - remove dead class reference in quick-draft (WordPress/gutenberg#82064) - `InputControl`: Hide the value-like placeholder Safari renders in empty date and time inputs in `ui` and `components` packages (WordPress/gutenberg#81991) - Site editor v2: Fix template part list pagination and sorting (WordPress/gutenberg#82054) - Build: Fix ESM declaration resolution (WordPress/gutenberg#82022) - Core Data: Stop storing NaN pagination totals for unpaginated endpoints (WordPress/gutenberg#82059) - Dashboard widgets & Grid: use kebab-case for CSS selectors (WordPress/gutenberg#82053) - CI: Skip the package CHANGELOG check when only the CHANGELOG changed (WordPress/gutenberg#82069) - Menu: Close non-modal menus on iframe interaction (WordPress/gutenberg#81952) - Packages: Recover August 26 npm release metadata (WordPress/gutenberg#82086) - Private APIs: Add back '@wordpress/dataviews' to the allowed core modules (WordPress/gutenberg#82221) Fixes #66067. git-svn-id: https://develop.svn.wordpress.org/trunk@63535 602fd350-edb4-49c9-b593-d223f7449a82
This updates the pinned commit hash of the Gutenberg repository from `ae8b34d78e1abc6d1c866fdc00080a1452de82ad ` (version `23.8.0`) to `658c8cbe89db5d90279051d3bb816ca210fe4646` (version `23.9.0`). A full list of changes included in this commit can be found on GitHub: https://github.com/WordPress/gutenberg/compare/v23.8.0..v23.9.0. - Env: Update git to latest commit when `--update` passed (WordPress/gutenberg#81447) - InputControl: Vertically center date and time input values in Safari (WordPress/gutenberg#81361) - DataViews: vendor `ValidatedComboboxControl` (WordPress/gutenberg#81449) - Icon: Fix margin being applied twice in the editor (WordPress/gutenberg#81292) - Docs: Add Background image control Readme (WordPress/gutenberg#69409) - Dimensions: Fix aspect ratio and scale controls not reflecting external updates (WordPress/gutenberg#80747) - DataViews: vendor `ValidatedFormTokenField` (WordPress/gutenberg#81451) - Docs: Fix dead link in the dynamic blocks tutorial (WordPress/gutenberg#81399) - DataViews: vendor `ValidatedToggleGroupControl` (WordPress/gutenberg#81450) - Update 23.8 release changes on trunk (WordPress/gutenberg#81485) - Adds README for inspector-controls-tabs component (WordPress/gutenberg#69072) - BlockEditVisuallyButton: Update Developer Documentation (WordPress/gutenberg#68987) - Storybook: Upgrade Vite to 8 (WordPress/gutenberg#81494) - Packages: Move unreleased changelog entries out of the 23.8 published versions (WordPress/gutenberg#81502) - Theme: Avoid root-level relational selectors (WordPress/gutenberg#81457) - Theme: Move changelog entry to unreleased (WordPress/gutenberg#81508) - Admin UI: Add Navigation component and Page navigation slot (WordPress/gutenberg#79746) - Editor: Correctly mark ToolsMoreMenuGroup as a private SlotFill (WordPress/gutenberg#81503) - Fix: Term Description block skips display filters inside Terms Query. (WordPress/gutenberg#81290) - DataForm: Fix the datetime control sending two updates per calendar interaction (WordPress/gutenberg#81440) - Editor: Stop `:has()` selectors recalculating the whole document on every block selection (WordPress/gutenberg#81471) - Fix: getQueryArgs discards everything after the second = in a query argument value (WordPress/gutenberg#81066) - DOM: treat a caret at a soft line wrap as an ambiguous position in Firefox (WordPress/gutenberg#81003) - In-between indicator: make symmetric (WordPress/gutenberg#68074) - Fix: Delete key on empty heading transforms following paragraph into heading (WordPress/gutenberg#78779) - Media editor: keep initial modal focus on the dialog frame (WordPress/gutenberg#81541) - Writing flow: select next block on Enter key (WordPress/gutenberg#63484) - Media Editor Route Experiment: Override media edit links to point to the new experimental media editor (WordPress/gutenberg#81559) - DataViews: vendor `ValidatedToggleControl` (WordPress/gutenberg#81492) - Release: Improve changelog categorization (WordPress/gutenberg#81475) - Docs: Add Block Editing Mode Readme. (WordPress/gutenberg#69411) - Editor: Use PluginSidebar in GlobalStylesSidebar (WordPress/gutenberg#81570) - Docs: Fix broken links to the Block API reference guides. (WordPress/gutenberg#81571) - Adds JSdoc for `column` and `columns` block's edit.js (WordPress/gutenberg#68873) - Adds JSdoc for `audio` block's edit.js (WordPress/gutenberg#68865) - Refactor: Convert `createColorHOC` to functional component (WordPress/gutenberg#70408) - Adds JSdoc for `avatar` block's edit.js (WordPress/gutenberg#68867) - Editor: Speed up the hierarchical term selector for large taxonomies (WordPress/gutenberg#81376) - Extensible Site Editor: Preview the site on Home for classic themes (WordPress/gutenberg#81578) - TypeScript: Add typecheck script and split root tsconfig into build and dev solutions (WordPress/gutenberg#81499) - Packages: Split blob, grid, interactivity, and theme tsconfigs (WordPress/gutenberg#81509) - Vitest: Resolve @flakiness/vitest reporter to an absolute path (WordPress/gutenberg#81582) - Update waveform player dependency patch (WordPress/gutenberg#81454) - Dashboard Widgets: promote high-relevance actions into a chrome footer (WordPress/gutenberg#81556) - Extensible Site Editor: Gate theme screens on theme support (WordPress/gutenberg#81581) - Editor: Register the editor keyboard shortcuts from the provider (WordPress/gutenberg#81580) - Base styles: Streamline focus ring override (WordPress/gutenberg#81242) - Components: Align legacy form control focus rings with WPDS (WordPress/gutenberg#80417) - Base Styles: Align `input-control` mixin to design system (WordPress/gutenberg#81357) - Add Gutenberg version details for WordPress 7.1 (WordPress/gutenberg#81600) - Fix viewport state values set by grid resizer (WordPress/gutenberg#81601) - Block editor: add an inserter to the parent selector (WordPress/gutenberg#81532) - url: clamp the truncated file name slice in filterURLForDisplay (WordPress/gutenberg#81529) - Fix parent inserter alignment with text labels enabled (WordPress/gutenberg#81608) - URL: Stop normalizePath throwing on a malformed percent sequence (WordPress/gutenberg#81086) - Extensible Site Editor: Add the Identity route (WordPress/gutenberg#81576) - ComboboxControl: narrow `onChange` callback type to `string` | `null` (WordPress/gutenberg#81568) - Storybook: Add Story for URLPopup component (WordPress/gutenberg#68449) - Extensible Site Editor: Show only the style book on Styles for classic themes (WordPress/gutenberg#81579) - Global Styles: Expose additional elements in Typography and Colors (WordPress/gutenberg#80852) - Storybook: Add Story for BlockPopover (WordPress/gutenberg#68681) - Block Editor: Never spotlight blocks in a preview (WordPress/gutenberg#81615) - Boot: Let the editor canvas navigate between entity records (WordPress/gutenberg#81590) - Block API: Declare block keyboard shortcuts on variations and transforms (WordPress/gutenberg#81588) - Storybook: Add Story for InnerBlocks (WordPress/gutenberg#68651) - Boot: Translate the canvas click-to-edit label (WordPress/gutenberg#81620) - Perf: Report the first block selection as its own metric (WordPress/gutenberg#81616) - Boot: Contain a surface failure to that surface (WordPress/gutenberg#81622) - Boot: Warn before leaving with unsaved changes (WordPress/gutenberg#81625) - Env: Fall back to cached docker images when the registry is unreachable (WordPress/gutenberg#81631) - Editor: Refactor 'ErrorBoundary' to use the new recommended components (WordPress/gutenberg#81638) - Fix playlist track toolbar insertion (WordPress/gutenberg#81639) - Playlist Track: Show upload spinner inline (WordPress/gutenberg#81445) - Admin UI: Fix the primary color of the Fresh admin color scheme (WordPress/gutenberg#81618) - Inner Blocks: Fix bug that caused alignment controls to show in Image blocks within a Gallery (WordPress/gutenberg#81606) - Layout block supports: cast numeric grid attributes before use (WordPress/gutenberg#81560) - Make Back button grid column flexible. (WordPress/gutenberg#81701) - List View: Make focus on mount opt in (WordPress/gutenberg#81659) - Lazy Editor: Render the editor preferences modal (WordPress/gutenberg#81630) - Edit Site Init: Seed the editor preferences the editor expects (WordPress/gutenberg#81628) - Boot: Do not offer to edit a trashed entity (WordPress/gutenberg#81632) - Block API: Address review feedback on block keyboard shortcuts (WordPress/gutenberg#81621) - Editor: Simplify List View sidebar shortcut handling (WordPress/gutenberg#81693) - RichText: Use the current record when handling Enter (WordPress/gutenberg#81696) - Boot: Sync the editor's device preview with the URL (WordPress/gutenberg#81617) - Stop saving Table of Contents output (WordPress/gutenberg#80404) - Lazy Editor: Keep the theme's editor styles on the canvas (WordPress/gutenberg#81747) - List View: Fix flaky global shortcut test and re-enable it (WordPress/gutenberg#81690) - Boot: Preview the site where the canvas has nothing to open (WordPress/gutenberg#81749) - Docs: Require site editor features to land in the extensible site editor too (WordPress/gutenberg#81752) - Build Tools: Stop watcher process trees on shutdown (WordPress/gutenberg#81504) - Update Terrazzo packages to 2.5.0 (WordPress/gutenberg#81082) - Site Editor: Use inverted ThemeProvider seed for portaled UI (WordPress/gutenberg#81653) - Components: Migrate DropdownContentWrapper to SCSS module (WordPress/gutenberg#81522) - Playlist: Add aria-label to prevent placeholder as label (WordPress/gutenberg#81654) - UI: Collapse item-popup item sizing to default and small (WordPress/gutenberg#81354) - Add `__unstable_wp_sync_storage` filter for pluggable storage backends (WordPress/gutenberg#81697) - Bump github/codeql-action/upload-sarif (WordPress/gutenberg#81436) - Feat: support block spacing (WordPress/gutenberg#79689) - Enable axial gap for Group and restrict to flex and grid layouts. (WordPress/gutenberg#81476) - Packages: Split tsconfigs for utility and mid-size packages (WordPress/gutenberg#81514) - Media Editor Route: Make the route look like it's part of the media menu in the left-hand sidebar (WordPress/gutenberg#81565) - Global Styles: filter and indicate blocks that have custom styles (WordPress/gutenberg#81373) - Prevent white screen crash during undo operations when Media Modal is open (WordPress/gutenberg#79898) - Fix clipped focus outlines in the media editor Details sidebar (WordPress/gutenberg#81703) - Fix: URL: normalizePath drops the rest of the query after a second "?" (WordPress/gutenberg#81612) - Block Editor: Add translator context to pseudo-state labels (WordPress/gutenberg#81770) - Global styles: Register <label> as element (WordPress/gutenberg#81160) - Site editor: update template section description. (WordPress/gutenberg#81613) - Fix: Accordion: Anchored hash inside the accordion throws the malformed URL console error (WordPress/gutenberg#81780) - BoxControl: update the opposite side when ALT is held on the left or right input (WordPress/gutenberg#81530) - Tabs: Give tab IDs their own counter so numbering starts at 1 and never skips (WordPress/gutenberg#81781) - Modal: Stop Escape key propagation on dismiss (WordPress/gutenberg#81785) - RTC: Couple RTC and polling provider as an opt-in experiment (WordPress/gutenberg#80658) - ui/Card: Use the default neutral border (WordPress/gutenberg#81746) - Update playlist track placeholder to match aria label (WordPress/gutenberg#81762) - Notes: keep the note actions menu in view for long display names (WordPress/gutenberg#81414) - Fix DataViews list layout color token usage (WordPress/gutenberg#81074) - UI: Add Spinner component (WordPress/gutenberg#81358) - Fields: Export the package stylesheet (WordPress/gutenberg#81769) - Fix the failing e2e test (WordPress/gutenberg#81739) - RTC: Add fallback identities when user profiles are unavailable (WordPress/gutenberg#81408) - Align collaboration migration version markers with the 23.8.0 release (WordPress/gutenberg#81789) - Dashboard: add a Site Health detail page as the dashboard's second route (WordPress/gutenberg#81729) - Dashboard Widgets: resolve widget types without a metadata module (WordPress/gutenberg#81738) - Theme: Improve color ramp contrast safeguards (WordPress/gutenberg#81185) - More block: clarify description about excerpt vs full content (WordPress/gutenberg#80619) - Schemas: Document what the theme.json schema checks (WordPress/gutenberg#81773) - Components, DataViews, Editor, UI: Split tsconfigs into build and dev projects (WordPress/gutenberg#81515) - Command Palette: Allow commands to override the category icon (WordPress/gutenberg#81787) - Fix Broken Links in Package file (WordPress/gutenberg#81700) - Notes: Stop forcing capitalization of user names (WordPress/gutenberg#81788) - Media Editor Route: Add undo/redo and save/cancel actions to the header (WordPress/gutenberg#81563) - Fix: Post Editor: Canvas animates from the left when switching mobile/tablet preview to desktop (WordPress/gutenberg#81484) - Build Package: Use Core's boot module when a plugin has none (WordPress/gutenberg#81761) - Docs: Fix broken link to the Document-Isolation-Policy explainer. (WordPress/gutenberg#81790) - Tests: Make PHPUnit tests independent of test config flags and theme root (WordPress/gutenberg#79159) - Editor: Debounce term filtering and announce results once (WordPress/gutenberg#81422) - Packages: Split story package tsconfigs into build and dev projects (WordPress/gutenberg#81516) - Packages: Complete the tsconfig build and dev project split (WordPress/gutenberg#81517) - TypeScript: Require the typecheck run for type checking in CI (WordPress/gutenberg#81518) - Block Editor: Remove leftover files (WordPress/gutenberg#81793) - Rename the media editor toolbar button to 'Edit image' (WordPress/gutenberg#81705) - Playlist: Remove block toolbar sharing between parent and child blocks (WordPress/gutenberg#81432) - Components: Fix Storybook accessibility issues in stories (WordPress/gutenberg#81777) - Fields: migrate last remaining JS file to TS (WordPress/gutenberg#81808) - Menu: Add UI component (WordPress/gutenberg#79560) - Patterns explorer: Refactor the category sidebar to use Tabs (WordPress/gutenberg#81807) - add context to change "revisions code diff" (WordPress/gutenberg#81820) - Add command: View post (WordPress/gutenberg#66720) - Fix: Post Editor: 403 on /wp/v2/settings at boot for users without manage_options cap (WordPress/gutenberg#81813) - UI: Add initial agents guidance mapping to contributing guidelines (WordPress/gutenberg#81400) - UI: Mark Input-related components as recommended (WordPress/gutenberg#81658) - Post Template: Fix mover arrows and labels for blocks inside the grid view (WordPress/gutenberg#81120) - Docs: Fix broken link to the getEntityRecords selector reference. (WordPress/gutenberg#81791) - Quick Inserter: Standardize the design of the Browse all button (WordPress/gutenberg#81827) - UI: Add TextareaControl component (WordPress/gutenberg#81359) - Docs: Narrow the tsconfig split guidance to the standard layout (WordPress/gutenberg#81828) - Storybook: Fix production build warnings (WordPress/gutenberg#81758) - Components: migrate ResizableBox to SCSS module- WordPress/gutenberg#80720 (WordPress/gutenberg#81792) - Bump astral-sh/setup-uv from 9.0.0 to 10.0.0 in /.github/workflows (WordPress/gutenberg#81823) - Global Styles: Keep storing shadow presets as CSS variables when custom presets exist (WordPress/gutenberg#81346) - Docs: Fix broken skip ahead anchor on the custom block editor guide. (WordPress/gutenberg#81835) - Fix MediaPlaceholder drag eligibility (WordPress/gutenberg#81431) - Updated Aria Landmark Example URL (WordPress/gutenberg#81832) - TypeScript: Put route projects under the typecheck run (WordPress/gutenberg#81829) - Add missing dependency to media-editor route (WordPress/gutenberg#81845) - Add transform to Row for Columns block (WordPress/gutenberg#81802) - Editor: Remove FlexItem usage from NoteCard (WordPress/gutenberg#81851) - UI: Expose keyboard shortcut presentation utilities (WordPress/gutenberg#81826) - Theme: Add color warning diagnostics workbench (WordPress/gutenberg#81817) - Editor: Surface debugging details in the ErrorBoundary (WordPress/gutenberg#81642) - Update save & submit docs (WordPress/gutenberg#81077) - Boot: Migrate styles to CSS Modules (WordPress/gutenberg#81756) - Build: Emit TypeScript declarations with --noCheck (WordPress/gutenberg#81839) - Core Data: Validate the parsed-blocks cache against registered block types (WordPress/gutenberg#81809) - Dependencies: Dedupe, update adm-zip, nx (WordPress/gutenberg#81870) - Core Data: Allow keyless entities to be addressed without a record ID (WordPress/gutenberg#81857) - UI: Mark Autocomplete, Field, and Fieldset as recommended (WordPress/gutenberg#80636) - Editor: Announce publish date changes to screen readers (WordPress/gutenberg#81629) - Storybook: Fix default background color for Example Application theme story (WordPress/gutenberg#81765) - Fix/79641 rtc cursor hidden details block v2 (WordPress/gutenberg#81322) - UI: add ControlWithError component (WordPress/gutenberg#81574) - Global Styles: Group shadow editor rows as items (WordPress/gutenberg#81871) - Storybook: Handle production module load failures (WordPress/gutenberg#81867) - Pattern: Disable visibility support (WordPress/gutenberg#81866) - Design System: Add portable agent skills (WordPress/gutenberg#80597) - Global Styles: Allow editing duotone palettes (WordPress/gutenberg#81605) - Color Palette panel: Remove empty whitespace when there are no theme colors (WordPress/gutenberg#81894) - Try caching gutenberg_get_global_styles (WordPress/gutenberg#81889) - Refactor parameter type hints and clean comments (WordPress/gutenberg#81898) - Preserve layout and style attributes on Column transform (WordPress/gutenberg#81855) - Global Styles: Close the revisions screen on the first back click (WordPress/gutenberg#81897) - Editor: Set the width an entity opens at from the editor provider (WordPress/gutenberg#81750) - Pre-publish panel: Remove the headings that repeat the panel title (WordPress/gutenberg#81806) - Components: migrate ComboboxControl to SCSS module (WordPress/gutenberg#80471) - UI: Update Base UI to 1.7.0 (WordPress/gutenberg#81390) - Update Ariakit packages (0.4.35 => 0.4.37) (WordPress/gutenberg#81080) - chore: Update the combobox SCSS to have a breaking changes log (WordPress/gutenberg#81923) - Use `WCTextareaControl` alias for legacy TextareaControl imports (WordPress/gutenberg#81834) - Components: Improve text detection for button icon-with-text (WordPress/gutenberg#81521) - Font Library: Fix the preview weight for variable fonts (WordPress/gutenberg#81748) - Editor: Migrate hierarchical term selector to @wordpress/ui (WordPress/gutenberg#81848) - Validation: update dependency-audit to 0.4.6 and ignore dataviews ./wp bundle (WordPress/gutenberg#81928) - SearchableChipSelect: Add grouped items support (WordPress/gutenberg#80989) - Use `WCInputControl` alias for legacy InputControl imports (WordPress/gutenberg#81830) - Storybook: Upgrade to 10.5 (WordPress/gutenberg#80692) - Documentation: Update coding guidelines to recommend TypeScript (WordPress/gutenberg#81763) - Font library: Add skeleton loader for the font preview images (WordPress/gutenberg#81047) - Build Scripts: Force tsc --pretty so CI reports the project behind global diagnostics (WordPress/gutenberg#81937) - Update is-plain-object to 5.1.0 (WordPress/gutenberg#81926) - DataViews: Sync the displayed calendar month with external value changes (WordPress/gutenberg#81635) - DataForm: Make the panel layout edit button the real field trigger (WordPress/gutenberg#80689) - Dependency updates Aug 2026 (2) (WordPress/gutenberg#81906) - Playlist: Add bulk track metadata editing (WordPress/gutenberg#81441) - Automated Testing: Change Storybook default a11y test to error on failure (WordPress/gutenberg#81597) - Playlist Track: Improve track upload error flows (WordPress/gutenberg#81878) - Block Library: Fix failing test in playlist block (WordPress/gutenberg#81950) - Block editor: Move isElementVisible changelog entry to Unreleased (WordPress/gutenberg#81953: shail-mehta <shailu25@git.wordpress.org> Co-authored-by: Mamaduka <mamaduka@git.wordpress.org> - DataViews: Fix date and datetime controls selecting the adjacent day (WordPress/gutenberg#81498) - UI: Accept locale codes in Calendar components (WordPress/gutenberg#81814) - List: Add Align Support (WordPress/gutenberg#68002) - TypeScript: Bring the remaining route projects under the typecheck run (WordPress/gutenberg#81847) - Components: migrate GradientPicker, ColorPalette to SCSS module (WordPress/gutenberg#80473) - Docs: Align @PARAM tag columns in test/ docblocks (WordPress/gutenberg#81905) - Hide layout panel when no controls are available (WordPress/gutenberg#81968) - UI: Add ValidatedInputControl component (WordPress/gutenberg#81627) - Components: migrate StyledLabel to SCSS module (WordPress/gutenberg#80001) - fix: Button rtl loader issue (WordPress/gutenberg#81901) - Theme: Update Terrazzo packages to 2.7.1 (WordPress/gutenberg#81978) - CollapsibleCard: Support multiple header descriptions (WordPress/gutenberg#81227) - DataViews: use `Menu` from `@wordpress/ui` (WordPress/gutenberg#81783) - DataViews: Localize the date/datetime calendars from the site locale and direction (WordPress/gutenberg#81592) - Replace the Storybook test runner with the Vitest integration (WordPress/gutenberg#81918) - Block Editor: Don't close inserter panel when inline quick inserter opens (WordPress/gutenberg#76241) - Updated broken links in document files (WordPress/gutenberg#81961) - Boot: Mount the registered plugins (WordPress/gutenberg#81754) - Extensible Site Editor: Add a theme preview page with global styles editing (WordPress/gutenberg#81954) - Block Styles: Refactor the variation UI to use ui/Button (WordPress/gutenberg#81833) - Styles Route: Reference the api-fetch and html-entities projects (WordPress/gutenberg#81989) - Fix: Line Break Tags Added to Scripts and Styles (WordPress/gutenberg#77542) - Fix: skip empty block markup in navigation to prevent split UL elements (WordPress/gutenberg#78793) - Pattern List: Import a pattern from JSON (WordPress/gutenberg#81919) - UI: Add tooltips to Calendar and RangeCalendar navigation buttons (WordPress/gutenberg#81983) - UI: Add `ValidatedTextArea` control (WordPress/gutenberg#81984) - Fix: Responsive styles: When enabled and a font-size is setting, the corresponding CSS variable remains undefined (WordPress/gutenberg#81854) - Components: Migrate `TextareaControl` to SCSS module (WordPress/gutenberg#81353) - UI: Set Calendar text direction automatically (WordPress/gutenberg#81982) - Theme: Rename ThemeProvider wrapper CSS class from root to wrapper (WordPress/gutenberg#81996) - Editor: Offer the theme export from both site editors (WordPress/gutenberg#81992) - Fix: Stop stripping spaces inside inline HTML elements on paste (WordPress/gutenberg#76696) - DataViews: Add a lint rule preventing private API imports (WordPress/gutenberg#81478) - Components: Migrate ProgressBar styles from Emotion to SCSS module (WordPress/gutenberg#80512) - RTC: Disable syncing for unpersistable post types (WordPress/gutenberg#81946) - Boot: Restore centered snackbar notices (WordPress/gutenberg#81995) - BorderControl, BorderBoxControl: Migrate styles to CSS Modules (WordPress/gutenberg#80437) - Duotone Picker: Stop the duotone bar offering to move its control points (WordPress/gutenberg#81850) - DataViews: Add an aspect ratio toggle for grid layouts (WordPress/gutenberg#81604) - Remove `fixable` on `i18n-no-flanking-whitespace` (WordPress/gutenberg#70279) - Dynamic Gallery block: Skip transforms that expect inner blocks, e.g. Image and Grid transforms (WordPress/gutenberg#82009) - Accordion Heading: Route theme.json spacing to the toggle button (WordPress/gutenberg#81976) - Element: Serialize memo-wrapped components instead of an empty string (WordPress/gutenberg#81986) - Style: Update color to use CSS variable in global-styles-ui (WordPress/gutenberg#79053) - Dashboard Widgets: let the host decide which component renders an action link (WordPress/gutenberg#81740) - Tabs: make inactive panel content findable in page (WordPress/gutenberg#81743) - Border Panel: Fix incorrect panel label (WordPress/gutenberg#78830) - Components: Migrate ZStack styles from Emotion to SCSS module (WordPress/gutenberg#80514) - Storybook: Add missing @types/jest devDependency (WordPress/gutenberg#82017) - TypeScript: Drop redundant undefined casts for keyless entity record IDs (WordPress/gutenberg#82018) - Editor: Fix start page/template modal footer overflow (WordPress/gutenberg#82021) - Storybook: Keep Vitest story scanning independent of the Vite root (WordPress/gutenberg#82020) - Test: Port shared Vitest test environment (WordPress/gutenberg#81038) - Query No Results: Add Border & Spacing Support (WordPress/gutenberg#64601) - RadioControl: allow individual options to be disabled (WordPress/gutenberg#82026) - Components: Migrate `BoxControl` to SCSS module (WordPress/gutenberg#80715) - SearchableChipSelectControl: Add form control to @wordpress/ui (WordPress/gutenberg#80980) - UI: Move controlled prop guidance to contributing docs (WordPress/gutenberg#82044) - Components: Migrate Scrollable to SCSS module (WordPress/gutenberg#80694) - UI: Fix accessibility issues in form primitives and their stories (WordPress/gutenberg#81853) - UI: Align Calendar styling with WPDS tokens (WordPress/gutenberg#81438) - Calendar: Support custom root roles (WordPress/gutenberg#81443) - E2E Tests: Use str_starts_with()/str_contains() in hide-user-rest-routes plugin (WordPress/gutenberg#82030) - Site editor v2: Fix block error with navigation-edit route (WordPress/gutenberg#82051) - ActionItem: Move the more menu deduplication out of the Slot (WordPress/gutenberg#81507) - DataViews: Fix invisible sticky Actions column divider (WordPress/gutenberg#82055) - Quick draft: replace Button+Link with LinkButton (WordPress/gutenberg#82031) - Lint: Prevent private API imports in all bundled packages (WordPress/gutenberg#82015) - PaletteEdit: Expose palette swatches as command buttons (WordPress/gutenberg#82023) - Test: Add Vitest React, Emotion compilation, console, and snapshot parity (WordPress/gutenberg#81039) - remove dead class reference in quick-draft (WordPress/gutenberg#82064) - `InputControl`: Hide the value-like placeholder Safari renders in empty date and time inputs in `ui` and `components` packages (WordPress/gutenberg#81991) - Site editor v2: Fix template part list pagination and sorting (WordPress/gutenberg#82054) - Build: Fix ESM declaration resolution (WordPress/gutenberg#82022) - Core Data: Stop storing NaN pagination totals for unpaginated endpoints (WordPress/gutenberg#82059) - Dashboard widgets & Grid: use kebab-case for CSS selectors (WordPress/gutenberg#82053) - CI: Skip the package CHANGELOG check when only the CHANGELOG changed (WordPress/gutenberg#82069) - Menu: Close non-modal menus on iframe interaction (WordPress/gutenberg#81952) - Packages: Recover August 26 npm release metadata (WordPress/gutenberg#82086) - Private APIs: Add back '@wordpress/dataviews' to the allowed core modules (WordPress/gutenberg#82221) Fixes #66067. Built from https://develop.svn.wordpress.org/trunk@63535 git-svn-id: http://core.svn.wordpress.org/trunk@62711 1a063a9b-81f0-0310-95a4-ce76da25c4cd



What?
This adds a Duotone tab to the Global Styles "Edit palette" screen, alongside Color and Gradient, so duotone palettes can be edited and custom duotones added.
I welcome someone to take this over, create a new PR inspired by this, etc. This is meant to give us something to play around with and evaluate, especially to analyze if there's enough space with three items there.
Closes #36541.
Why?
Colors and gradients have been editable in Global Styles but duotones still cannot be. They appeared as a read-only swatch grid at the bottom of the Gradient tab, so you could see the theme's duotones but not change them or add your own.
This follows the direction @jasmussen proposed with three tabs (Colour, Gradient, Duotone) with the same UI underneath, and the duotone editor when you add a custom duotone.
How?
PaletteEditgains a third variant. The privateisGradientboolean becomes avariant: 'color' | 'gradient' | 'duotone'discriminator, and a newduotonesprop (with an optionalcolorPalette) joins the props union. The public surface is additive.The duotone branches reuse pieces
DuotonePickeralready owns:CustomDuotoneBar+ColorListPicker(Shadows / Highlights)duotone-pickerDuotonePickerduotone-pickergetGradientFromCSSColors( colors, '135deg' )duotone-picker/utilsgetDefaultColors( colorPalette )duotone-picker/utilsA new
DuotonePalettePanelmirrorsGradientPalettePanel: Theme and Default sections withcanOnlyChangeValues, a Custom section withslugPrefix="custom-", and the Default section gated oncolor.defaultDuotone. The read-only duotone list is removed from the Gradient tab.Also fixed along the way:
color-mix()palette entry thatcolordreads as black, which would seed every new duotone with an unrenderable value. Surviving colors are normalized to hex, because the front end's parser accepts only hex,rgb()andhsl()whilecolordin the editor also accepts CSS named colors.DuotonePickergainsselectedSlugand reports the picked preset's index and slug toonChange, matchingColorPaletteandGradientPicker(GradientPicker: select by slug so two presets sharing a gradient keep their identity #80554). Selection compared colors alone, so two presets holding the same pair both appeared selected, and editing one edited the other.+seeds every new duotone from the palette's darkest and lightest colors, so duplicates are the normal case.No PHP changes needed
settings.color.duotone.customis already a valid user-origin path, andWP_Duotone::get_all_global_styles_presets()iterates every origin. Confirmed on a test site that a custom-origin duotone produces a validfeColorMatrixfilter.Deferred
Two pre-existing issues in this area, neither caused by this PR:
Testing Instructions
+under Custom. A duotone should be added, named "Duotone 1", seeded from your palette's darkest and lightest colors.+again, then edit the second duotone. The one you clicked should change, not the first.⋮menu: "Remove all duotones" and "Reset duotones".To test a Theme section showing up, add some duotones via
theme.jsonor awp_theme_json_data_themefilter.Screenshots
duotone.PR.mov
🤖 Generated with Claude Code