Skip to content

Donations Block: Add Gutenberg style settings#48415

Open
angelablake wants to merge 31 commits intotrunkfrom
update/donations-block-style-options
Open

Donations Block: Add Gutenberg style settings#48415
angelablake wants to merge 31 commits intotrunkfrom
update/donations-block-style-options

Conversation

@angelablake
Copy link
Copy Markdown

@angelablake angelablake commented Apr 30, 2026

Proposed changes

Theme inheritance for surfaces (Phase 1)

  • Drop hardcoded font-size: 16px overrides on tab labels and amount tiles so theme typography flows through.
  • Use transparent background and inherit text color on inactive tabs and amount tiles, so themes with non-light backgrounds aren't broken by hardcoded white surfaces.
  • Wrap the frontend Donate button in wp-block-button and add wp-element-button so block themes can style it via theme.json styles.elements.button. The editor preview already used the wrapper; the frontend was emitting a bare anchor.

Standard Gutenberg style supports (Phase 2)

  • Enable color (background, text, button, link), typography, spacing, and __experimentalBorder (color, radius, style, width). The styles tab now exposes the usual block-level panels.
  • Move the default container border to the block wrapper so the user-set border via the Border panel overrides cleanly.
  • Use get_block_wrapper_attributes() in the PHP render so user-set border and color styles actually reach the frontend (the existing Blocks::classes() helper only emits class names, not the inline style="..." for block supports).
  • Add overflow: hidden on the block wrapper so children (tabs) clip to any user-set border-radius.

Custom inspector controls (Phase 2 cont.)

  • Add custom panels under the Styles tab for per-state colors that block supports cannot reach: active tab background/text, inactive tab background/text, and selected amount background/text.
  • Add custom panels for tab font size, tab padding, tab border color, and a Buttons vs Tabs appearance toggle.
  • Add custom panels for donate button font size, padding, alignment (including full-width), and border radius.
  • Add content alignment (toolbar AlignmentControl) for left/center/right alignment of the block content and amount tiles.
  • <ContrastChecker> warnings between fg/bg pairs in each panel for accessibility.
  • Render the per-state color rules as a per-instance scoped <style> element keyed off a unique class on the block wrapper. The plugin's postcss config inlines var() fallbacks at compile time, so theme.json presets cannot flow through SCSS — the runtime <style> sidesteps the build constraint.
  • Sanitize user-supplied color values in the PHP render before injecting them into the <style> element to prevent breakout of the CSS context.

Tests

  • PHPUnit coverage for sanitize_color_for_css (hex / rgb / hsl / var / named colors, dangerous chars stripped, length cap, type checks) and for build_custom_styles (empty when no overrides, scoped rules per state, unsafe values dropped).

Related product discussion/links

  • Part of an internal RSM project on Donations Form improvements (see internal P2 for context).

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

No tracking or data changes.

Testing instructions

  1. On a site running a block theme (Twenty Twenty-Four works well), add a Donations Form block to a post.
  2. In the inspector's Styles tab, confirm panels appear for: Color, Typography, Border (with radius), Tabs, Donate button, and Selected amount. [screenshot]
  3. Set border color and a noticeable border-radius via the Border panel. Confirm:
    • The visual border on the block updates in the editor preview. [screenshot]
    • Saving and viewing on the frontend also shows the border (this was previously missing). [screenshot]
    • Tab corners are clipped to the rounded outer corners (no protrusion).
  4. Set various colors via the Tabs panel. Active and inactive tab styles update in the editor preview. [screenshot] View the frontend and click between tabs — active styling moves with the click, inactive stays consistent.
  5. In the Selected amount panel, set a background and text color. Verify on the frontend by clicking an amount tile — the selected state should reflect the custom colors. [screenshot]
  6. Pair a low-contrast bg/fg in one of the new panels — the ContrastChecker warning should appear. [screenshot]
  7. Toggle Tabs to Buttons appearance in the Tabs panel — frequency selectors should render as pill-shaped buttons. [screenshot]
  8. Use the content alignment toolbar control to align the form left, center, and right. [screenshot]
  9. Switch to a classic theme (e.g. Twenty Twenty-One). Confirm no regressions for non-block-theme users.
  10. Confirm existing Donations blocks render without breaking — the default border moved from .donations__container to the block wrapper, but visually it should look the same when no user customizations are set.

Angela Blake and others added 2 commits April 30, 2026 10:12
- Wrap the frontend Donate button in `wp-block-button` and add the
  `wp-element-button` class on both editor preview and frontend so
  block themes can style it via theme.json `styles.elements.button`.
- Drop hardcoded `font-size: 16px` on tab labels and amount tiles so
  theme typography flows through.
- Use `transparent` background and `inherit` text color on inactive
  tabs and amount tiles. Active state and structural borders remain
  hardcoded for now; making those theme-driven via CSS custom
  properties is blocked on the build's `postcss-custom-properties`
  config (`preserve: false` inlines fallbacks at build time). A
  follow-up that adds Gutenberg `supports.color/typography/spacing`
  is the standard mechanism and is unaffected by that constraint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Standard Gutenberg supports (color, typography, spacing,
__experimentalBorder including color/radius/style/width) so users
get the usual style-tab panels for the block as a whole. The default
container border moves up to the block wrapper; users can override
its color, width, style, and radius from the inspector.

Custom inspector controls (under the Styles tab) for per-state colors
that block supports cannot reach: active tab background and text,
inactive tab background and text, and selected amount background and
text. These are stored as block attributes and rendered as a per-
instance scoped <style> element with rules keyed off a unique class
on the wrapper. This pattern is needed because the plugin's postcss
config inlines var() fallbacks at build time, so theme.json presets
cannot flow through the SCSS — the runtime <style> sidesteps the
build entirely.

PHP render sanitizes user-supplied color values before injecting them
into the <style> element to prevent breakout of the CSS context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@angelablake angelablake added Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Focus] Blocks Issues related to the block editor, aka Gutenberg, and its extensions developed in Jetpack [Block] Donations labels Apr 30, 2026
@angelablake angelablake self-assigned this Apr 30, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

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), and enable the update/donations-block-style-options branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack update/donations-block-style-options

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 added [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] In Progress labels Apr 30, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

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:

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 Apr 30, 2026
@jp-launch-control
Copy link
Copy Markdown

jp-launch-control Bot commented Apr 30, 2026

Code Coverage Summary

Coverage changed in 3 files.

File Coverage Δ% Δ Uncovered
projects/plugins/jetpack/extensions/blocks/donations/donations.php 117/331 (35.35%) 35.35% 35 💔
projects/plugins/jetpack/extensions/blocks/donations/edit.js 0/94 (0.00%) 0.00% 8 💔
projects/plugins/jetpack/extensions/blocks/donations/controls.js 0/28 (0.00%) 0.00% 2 ❤️‍🩹

2 files are newly checked for coverage.

File Coverage
projects/plugins/jetpack/extensions/blocks/donations/build-custom-styles.js 0/94 (0.00%) 💔
projects/plugins/jetpack/extensions/blocks/donations/style-controls.js 0/69 (0.00%) 💔

Full summary · PHP report · JS report

If appropriate, add one of these labels to override the failing coverage check: Covered by non-unit tests Use to ignore the Code coverage requirement check when E2Es or other non-unit tests cover the code Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR I don't care about code coverage for this PR Use this label to ignore the check for insufficient code coveage.

Angela Blake and others added 3 commits April 30, 2026 11:46
Replace isset(X) ? X : '' with X ?? '' to satisfy phan's
PhanPluginDuplicateConditionalNullCoalescing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…unded corners

* Replace `Blocks::classes()` with `get_block_wrapper_attributes()` in
  the PHP render. The Jetpack helper only emits class names; the WP
  core function emits classes plus the inline `style="..."` for
  user-set color/border/typography supports, so user-set border
  styles now actually appear on the frontend (they were already
  visible in the editor via `useBlockProps()`).
* Add `overflow: hidden` on the block wrapper so children (tabs)
  are clipped to any user-set `border-radius`. Without it, the tab
  corners poke past the rounded outer corners.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add `<ContrastChecker>` inside each per-state color panel (active
  tab, inactive tab, selected amount) so the inspector warns when a
  user pairs low-contrast foreground/background colors. Same a11y
  affordance core blocks use.
* Add PHPUnit coverage for `sanitize_color_for_css` (hex / rgb / hsl
  / var / named colors pass through; dangerous chars / over-long /
  non-string inputs are rejected) and `build_custom_styles` (empty
  when no overrides, scoped rules per state, unsafe values dropped).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Angela Blake and others added 11 commits April 30, 2026 12:41
Adds a "Tab dimensions" panel under the Styles tab with `FontSizePicker`
and `BoxControl` from `@wordpress/components`/`block-editor`, wired to
new `tabFontSize` and `tabPadding` block attributes. Standard Gutenberg
typography/spacing UIs apply only to whole blocks, so per-element
controls go through the same per-instance scoped `<style>` builder used
by the per-state colors.

Also rename `sanitize_color_for_css` to `sanitize_css_value` since it's
now used for non-color values too. The implementation didn't change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "Donate button" panel under the Styles tab with:

* `FontSizePicker` and `BoxControl` for per-instance font-size and
  padding on the donate button. Same pattern as the existing tab
  dimensions controls — values are stored as block attributes and
  rendered via the per-instance scoped <style> element.
* A `ToggleGroupControl` with four options (Left, Center, Right,
  Full width). Left/center/right emit `text-align` on the donate
  button wrapper; full width forces both wrapper and button to
  block-level 100% with `box-sizing: border-box` so padding doesn't
  overflow.

Block-level button color is already covered by `supports.color.button`
enabled previously; this PR fills in the dimensions/alignment gaps
that block supports cannot reach for sub-elements.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Registers a Gutenberg block style variation: users pick "Default" or
"Buttons" from the Styles tab's variations card row. The "Buttons"
variant renders the frequency selectors (One-time / Monthly / Yearly)
as a row of pill-shaped buttons with gaps, instead of the default
edge-to-edge tabs separated by dividers.

CSS-only — variations apply via the `is-style-{slug}` class Gutenberg
adds to the wrapper. User-set per-state colors and tab dimensions
compose on top because they target the same elements at equal-or-
higher specificity in the per-instance scoped <style>.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…g; center full-width button text

Two fine-tuning fixes from JN testing:

* In the Buttons style variation, the nav container had a fixed
  `padding: 16px 16px 0` above the pill row, independent of any
  user-set tab dimensions. Setting tab padding-top/bottom only
  affected the pill interiors; the visible row height felt
  uncoupled from the control. The per-instance <style> now also
  emits matching `padding-top` / `padding-bottom` on the nav,
  scoped to `.is-style-buttons`, so the row scales with the pill
  interior padding the user set.
* Full-width donate button: switching the button to `display: block`
  with `width: 100%` reset its text alignment to the default
  start-of-line, leaving the label visually left-aligned in a
  full-width pill. Add `text-align: center` to the full-width
  button rule.

Tests updated: full-width assertion now covers `text-align: center`,
and two new cases cover the buttons-style nav padding mirror and
absence of the rule when no padding is set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reorganize the Styles tab so all tab-related controls live in a single
"Tabs" panel:

* New `tabsAppearance` attribute (enum tabs|buttons, default tabs)
  replaces the block.json `styles` variation registration. The
  appearance toggle now lives inside the "Tabs" panel rather than the
  auto-rendered block-style card row at the top of the inspector.
  The `is-style-buttons` class is applied manually via useBlockProps
  in the editor and via the wrapper class string in PHP, so existing
  CSS keys off the same selector.
* New `tabBorderColor` attribute. The per-instance <style> emits a
  `border-color` rule on `.donations__nav` and `.donations__nav-item`
  so it covers the default-style divider lines AND the buttons-style
  pill borders with one declaration.
* The "Tab dimensions" panel is removed; FontSizePicker and BoxControl
  move into the consolidated "Tabs" panel beside the colors and
  appearance toggle. The five color pickers use
  `__experimentalColorGradientControl` so colors and non-color controls
  coexist cleanly in one PanelBody.
* Setter callbacks are memoized in `useMemo` to keep stable references
  for JSX props (react/jsx-no-bind), and `handleModalClose` in edit.js
  is wrapped in `useCallback` for the same reason.

The "Selected amount" and "Donate button" panels are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revert the per-instance rule that mirrored user-set tab padding-top/
bottom onto the buttons-style nav container. Tab Padding should only
control the pill interior padding (between pill border and text).
The space between the form border and the top of the pill row is set
by the SCSS default (`padding: 16px 16px 0` / `24px 24px 0`) and stays
fixed regardless of user input.

Test renamed and inverted: confirms the buttons-style nav rule is NOT
emitted when only tab padding is set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the "Selected amount" PanelColorSettings with a consolidated
"Amounts" PanelBody containing all amount-tile styling:

* Selected amount background and text (with ContrastChecker)
* Font size (applies to all tiles, preset + custom)
* Border via BorderBoxControl: color + width + style with link/unlink
  for individual sides — same component WP core's block-level border
  supports renders.
* Border radius via __experimentalBorderRadiusControl: uniform or
  per-corner.

Storage matches the standard Gutenberg shape:
* `amountBorder` (object): uniform `{color, style, width}` or split
  `{top: {...}, right: ..., bottom: ..., left: ...}`.
* `amountBorderRadius` (string or object): "8px" uniform or
  `{topLeft, topRight, bottomRight, bottomLeft}`.

CSS emission (in `build_custom_styles` JS + PHP) handles both shapes
via two new helpers (build_border_decls, build_radius_decls in PHP;
borderDecls, radiusDecls in JS), expanding split values into per-side
or per-corner declarations.

Tests cover uniform border, split border, and per-corner radius shapes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch the five tab color pickers from inline ColorGradientControl
back to PanelColorSettings (rendered without its own title via
showTitle={false}, nested inside the existing "Tabs" PanelBody).

This restores the compact swatch+label rows the standard Color
panel uses for "Background", "Text", etc. — clicking each row opens
a popover with the picker, instead of every picker rendering inline
and stretching the panel.

The five rows are: Active tab background, Active tab text, Inactive
tab background, Inactive tab text, Tab border. Adjacent labels share
a prefix so the active/inactive groupings read clearly. The two
ContrastCheckers stay as children of PanelColorSettings.

(Doesn't address the auto-rendered Dimensions and Border panels'
default-collapsed state — those are core-supports panels and out of
direct block control. The three custom panels (Tabs / Amounts /
Donate button) already use initialOpen={false}.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same change as the previous commit applied to the Amounts panel: the
two selected-amount color pickers (background + text) move from inline
ColorGradientControl into a PanelColorSettings (showTitle={false}) so
they render as compact swatch+label rows that open a popover on click,
matching the standard Color panel UX.

The ContrastChecker stays as a child of PanelColorSettings; the
FontSizePicker, BorderBoxControl, and BorderRadiusControl below are
unchanged. The unused ColorGradientControl import is dropped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove `supports.spacing` (padding / margin / blockGap) from the
block.json supports declaration. The auto-rendered Dimensions panel
disappears from the Styles tab.

Block-level padding/margin/gap aren't load-bearing for the donation
form's internal layout — users have higher-leverage controls (tab
padding, button padding, alignment) for the things they typically
want to adjust. Trimming this panel reduces clutter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two style-panel polish changes:

1. Combine related color pickers into compound rows (one row, multiple
   swatches), modeled after the standard Color panel's "Button" row:
   - Active tab → background + text in one row
   - Inactive tab → background + text in one row
   - Selected amount → background + text in one row
   - Tab border still single-swatch (now using the same component for a
     consistent look)

   New `CompoundColorRow` component built on `Dropdown` + `ColorIndicator`
   + `ColorGradientControl`. Render functions are wrapped in useCallback
   to keep stable refs (react/jsx-no-bind).

2. Add a `jp-donations-style-panel` className to all three custom panels
   (Tabs, Amounts, Donate button) and apply `margin-block-start: 24px`
   to every top-level child after the panel title in editor.scss. Fixes
   the cramped layout where, e.g., the BorderBoxControl's "Border" label
   sat directly under the FontSizePicker. Default control margins differ
   across components, so a wrapper rule normalizes the gap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -119,7 +119,7 @@ const Tab = ( { activeTab, attributes, setAttributes } ) => {
/>
<div className="wp-block-button donations__donate-button-wrapper">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If the Donations block were to just use the inner block structure, we could use a real core/button block here like e.g. Forms block does, and we'd inherit all the styles + styling options for free. :-)

}

/**
* Build_custom_styles emits font-size and per-side padding for tabs.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure if it would work smoothly, but what do you think if Donations block would use Tabs block as inner block? That would give a lot of styling options and whatnot for free.

@simison simison requested a review from aaronrobertshaw April 30, 2026 20:13
@simison
Copy link
Copy Markdown
Member

simison commented Apr 30, 2026

Cool to see iteration on Donations block! :-)

@aaronrobertshaw I added you here too but no pressure; just thought you might wanna be interested to see global styles applied to a legacy block and might have some tips. 👋 :-)

Angela Blake and others added 13 commits April 30, 2026 15:26
…lor rows

Restructure the compound color rows so they look identical to the
auto-rendered "Color (Block support panel)" rows from supports.color:

* Each row is now a `__experimentalToolsPanelItem` with the
  `block-editor-tools-panel-color-gradient-settings__item` class — the
  borders, rounded corners, and per-row reset (kebab menu) come from
  core's existing CSS for that class, no duplicated styles in our
  stylesheet.
* The labeled-indicators toggle uses `__experimentalZStack` with
  `offset={-8}` to overlap the swatches by 8px, matching exactly how
  core's "Button" row stacks its text + background indicators.
* Each panel's compound rows are wrapped in a `__experimentalToolsPanel`
  (with the `color-block-support-panel` class) plus a `resetAll`
  callback. Required for ToolsPanelItem to register properly.

Same component (`CompoundColorRow`) renders both single-swatch (Tab
border) and multi-swatch (Active tab, Inactive tab, Selected amount)
rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the wrapping that core's ColorToolsPanel uses around its
ToolsPanel content — the missing piece was the grid layout that
`hasInnerWrapper={true}` activates plus the
`color-block-support-panel__inner-wrapper` div around children.

Both the Tabs and Amounts color sections now use the same
ToolsPanel props as core (`hasInnerWrapper`, `headingLevel={3}`,
the experimental first/last visible-item classes) and wrap their
CompoundColorRow children in the standard
`color-block-support-panel__inner-wrapper` div, so the grid
layout, row borders, rounded corners, and overlapping swatches
all come from core CSS that was already loaded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ividing line

Two final style-menu polish changes:

1. Compound color rows (Active tab, Inactive tab, Selected amount)
   now open into a tabbed popover instead of stacking the two
   ColorGradientControls. One tab per setting (Background / Text),
   exactly matching the standard "Button" row in the Color block-
   support panel.

   Built with TabPanel from `@wordpress/components` (the public Tabs
   component is gated behind `privateApis`, so we use the older but
   stable TabPanel which produces an equivalent UX).

2. Strip border-top / border-bottom on top-level controls inside our
   `.jp-donations-style-panel` PanelBodies. A few of the controls we
   render (notably BorderBoxControl / BorderRadiusControl / BoxControl)
   ship with their own dividers that read as a horizontal line above
   the settings — standard core panels don't show such a line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply CSS in editor.scss to make individual control labels inside our
custom style panels (Tabs / Amounts / Donate button) match the look
of the standard supports-panel labels:

* Small uppercase grey labels (`text-transform: uppercase`, 11px,
  weight 499, color #757575)
* Consistent 8px gap between label and control
* All controls fill the panel width (100%)

Targets `.components-base-control__label` and the per-component label
classes used by ToggleGroupControl, BoxControl, BorderBoxControl, and
BorderControl. Width applied to the matching control wrappers.

This is a CSS-only approximation of the standard look — broad rules
that should hit most control labels in our panels. If a specific
control still doesn't match, follow-up tweaks can target it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap the popover content in
`<div className="block-editor-panel-color-gradient-settings__dropdown-content">`
the same way core does. The existing core CSS rule then sizes the
inner `.block-editor-color-gradient-control__panel` to width 260px
and padding 16px without us writing any of it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructure each PanelBody so non-color settings (Appearance, Font
size, Padding, Border, Border radius, Alignment) are wrapped in
`__experimentalToolsPanelItem` inside an `__experimentalToolsPanel`.
This is the same pattern core's auto-rendered Typography / Border /
Spacing panels use, so we get for free:

* `components-grid` + `components-tools-panel` grid layout on the
  panel.
* `components-tools-panel-item` plus `first` / `last` classes on each
  item for proper visual separation.
* Standard label rendering (uppercase, weight, spacing) — no more
  hand-rolled CSS.
* Per-item kebab reset, panel-level "Reset all" via the dropdown
  menu.

Each ToolsPanelItem gets a stable `hasValue` / `onDeselect` callback
via a `useMemo` lookup, so JSX props don't recreate functions on
every render (react/jsx-no-bind).

Editor stylesheet now only forces label `color: inherit` so labels
inherit from `.interface-complementary-area` rather than the hardcoded
component grey.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five small consistency fixes:

1. Reorder Tabs panel to Appearance → Colors → Font size → Padding by
   splitting the single "Tab settings" ToolsPanel into two — one above
   the colors panel for Appearance, one below for the dimensions.
2. Move Appearance / Font size / Padding out from under a "Tab settings"
   sub-heading. Ditto for Amounts and Donate button via the
   `.components-tools-panel-header { display: none }` rule below.
3. Title-case the compound color row labels (Active Tab, Inactive Tab,
   Tab Border, Selected Amount). Add a CSS rule to undo the standard
   `max-width: calc(100% - 44px)` clip so the full label is visible.
4. Drop the inner ToolsPanel's emotion 16px padding — the outer
   PanelBody already provides padding so the inner adds redundant
   spacing.
5. Adjust the between-section margin so the first non-title child sits
   flush with the panel title (was: every non-first child got 24px;
   now: only third+ children).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…earance

Old donation blocks created before the appearance toggle moved off
the block-style variation system have `className: "is-style-buttons"`
saved in their attributes. Once the variation registration was removed
from block.json, that className kept rendering the buttons style on
the frontend but the new Appearance toggle (which only reads
`tabsAppearance`) showed "Tabs" — so the toggle did nothing.

Add a one-time migration in `edit.js`: when `attributes.className`
contains `is-style-buttons`, set `tabsAppearance: 'buttons'` and strip
the class. The migration runs on next edit of any old block, so the
toggle starts reflecting the rendered state and switching back to
"Tabs" works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hide the inner ToolsPanel headers (already done via CSS in editor.scss)
and replace the per-section reset menus with a single "Reset" Button
at the bottom of each PanelBody. Each Reset clears all attributes for
that panel:

* Tabs Reset: appearance, all 5 colors, font size, padding
* Amounts Reset: 2 colors, font size, border, border radius
* Donate button Reset: font size, padding, alignment

The inner ToolsPanels still need a `resetAll` callback (it's a required
prop), but with the header hidden the dropdown menu it powers is
unreachable, so we pass a noop. Per-item kebabs are unreachable for the
same reason; their `onDeselect` handlers are kept so the API contract
stays valid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. Tabs > Buttons appearance: the active pill border now picks up the
   user-set Tab Border color too. Removed the buttons-style active
   border-color override in common.scss; the per-instance border-color
   rule now also targets `.donations__nav-item.is-active`.
2. In compound color rows (Active Tab, Inactive Tab, Selected Amount):
   Text comes before Background — both in the visible swatch order
   and the picker tab order in the popover.
3-4. Color section in Tabs and Amounts panels now shows a "COLORS"
   heading at the top. CSS only hides the header on non-color
   ToolsPanels, so the color panel's heading stays. A nested rule
   hides the kebab menu inside the colors header (we use the
   panel-level Reset button instead).
5. The panel-level Reset button is right-aligned via
   `.components-panel__row { justify-content: flex-end }`.
6. New Border Radius control under Padding in the Donate button
   panel. Adds `buttonBorderRadius` attribute (string|object) and
   emits `border-radius` on `.donations__donate-button` — uniform or
   per-corner via the existing `radiusDecls` / `build_radius_decls`
   helpers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…remove BoxControl reset

Three small UX fixes:

1. BoxControl's built-in "Reset" button is now disabled on Tab Padding
   and Button Padding (`allowReset={false}`). The panel-level Reset
   button at the bottom of each PanelBody covers the same need.

2. Don't fall back to default text when a content area is left blank.
   Previously the editor and frontend filled empty heading / extraText
   / chooseAmountText / customAmountText / buttonText with the
   `DEFAULT_TEXTS` strings so something always rendered. Now both
   render whatever the user typed (including empty), matching the
   placeholder UX of the editor. Empty headings, paragraphs, and the
   "Or enter a custom amount" hint are conditionally not emitted in
   the PHP so they don't take vertical space when blank.

3. New `contentAlignment` block-level attribute (left / center /
   right). Surfaced via `AlignmentControl` in the BlockControls
   toolbar. Emits `text-align: <value>` on `.donations__content`,
   so all child text inherits. The existing button alignment still
   wins for the donate button because it sets `text-align` directly
   on `.donations__donate-button-wrapper`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Left/Center/Right toolbar control was applying `text-align` to
`.donations__content`, which centered text and inline content but did
nothing to the amount tiles — they live in `.donations__amounts`,
which uses `display: flex`, and `text-align` doesn't move flex items.

Now we also emit a matching `justify-content` rule on
`.donations__amounts` whenever `contentAlignment` is set:

* Left   → `text-align: left`   + `justify-content: flex-start`
* Center → `text-align: center` + `justify-content: center`
* Right  → `text-align: right`  + `justify-content: flex-end`

So picking "Center" centers everything (text + tiles) and the user
doesn't need a separate amounts-only alignment control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…leared

Switch all the text fallbacks from `||` (which falls back when the
value is empty string) to `??` (which only falls back when the value
is `undefined`).

* New blocks: attributes are `undefined` for heading / buttonText /
  extraText / chooseAmountText / customAmountText, so the user sees
  the default text in the editor and on the frontend, like before.
* User explicitly clears a field: attribute becomes `""`, which is
  preserved through `??`, so the field renders blank — no fallback.
* User typed text: respected as before.

In `tab.js` the fallback is in the RichText `value` props (`??`) and
in the `chooseAmountText` / `customAmountText` destructure defaults
(destructure defaults only fire when the property is `undefined`).

In `donations.php` the heading / buttonText defaults are restored to
the `array_merge` first-array (which `array_merge` overrides only with
keys actually present in the user attributes), and the chooseAmount /
customAmount / extraText fallbacks use `??` for the same semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@aaronrobertshaw
Copy link
Copy Markdown
Contributor

Thanks for the ping 👍

I've only managed to have a super quick glance at this so take this with a grain of salt.

Add custom panels under the Styles tab for per-state colors that block supports cannot reach: active tab background/text, inactive tab background/text, and selected amount background/text.

There is some work in this area that you might be interested in:

Also, I think you might be able to leverage the style engine more to generate styles. Just a thought.

@simison
Copy link
Copy Markdown
Member

simison commented May 1, 2026

Remember to check if previously inserted blocks (in old format) work in the editor, and that we don't need deprecation functions.

@angelablake angelablake marked this pull request as ready for review May 1, 2026 15:57
@angelablake angelablake marked this pull request as draft May 1, 2026 16:08
Angela Blake and others added 2 commits May 1, 2026 11:16
The `test_build_custom_styles_emits_tab_border_color` assertion was
written for the original 2-selector rule (`.donations__nav` +
`.donations__nav-item`). When the rule was widened to also cover the
buttons-appearance active pill (`.donations__nav-item.is-active`), the
test wasn't updated. Update the expected string to the 3-selector form
the function now emits.

Failing on every PHP test job in the matrix; this brings them green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@angelablake angelablake marked this pull request as ready for review May 4, 2026 17:27
@angelablake
Copy link
Copy Markdown
Author

@simison @aaronrobertshaw Thanks so much for reviewing this!

I read your comments, and here's where I landed:

Absolutely LOVE the idea of using inner blocks for tabs and button, but I ended up deciding against it because of complexity around how these elements interact with each other to produce the Stripe link for the button (I'm not a developer, so I don't trust myself to navigate that complexity well, even with Claude's help), and because the UI could become confusing to non-technical or inexperienced WordPresss users (how the block settings change depending on what's selected can be difficult to navigate).

I had Claude explore the related work, and some of it seemed useful, but Claude recommended against refactoring because the juice wasn't worth the squeeze (yet). There was a longer explanation, but I'm going to keep an eye out for future opportunities to tie into these.

I double-checked that the previously inserted blocks work (both via Claude & through manual testing), so we should be good to go.

Next steps: I gave Jetpack happiness a heads-up & will look to release this week unless they highlight any major concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Block] Donations Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Focus] Blocks Issues related to the block editor, aka Gutenberg, and its extensions developed in Jetpack [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] In Progress [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants