Skip to content

feat(core): Pattern Overrides support for Webentor blocks (release 0.15.7) - #6

Merged
palicko merged 1 commit into
mainfrom
feat/pattern-overrides
Aug 4, 2026
Merged

feat(core): Pattern Overrides support for Webentor blocks (release 0.15.7)#6
palicko merged 1 commit into
mainfrom
feat/pattern-overrides

Conversation

@palicko

@palicko palicko commented Aug 4, 2026

Copy link
Copy Markdown
Member

WordPress 7.0 opened Pattern Overrides to custom blocks. This opts the Webentor core blocks in, so a synced pattern can be reused across pages with per-instance content while the design keeps propagating from the original.

How it works

WP 7.0 collapsed the whole mechanism into one server-side filter, block_bindings_supported_attributes (wp-includes/block-bindings.php:141). That single list drives:

Consumer Where
"Enable overrides" button (Advanced panel) editor.js:49988
Overrides panel in the pattern editor patterns.js:320
Editor read/write plumbing block-editor.js:7400 — swaps in bound attributes, reroutes setAttributessource.setValues
Server-side substitution WP_Block::render()
contentOnly / disabled locking inside instances block-editor.js:9544

So no JS changes were needed — core handles read/write generically for any block. And because Webentor blocks are dynamic (save: () => null + Blade), bound values simply arrive in $attributes; no HTML API selector work.

Blocks opted in

Block Attributes
e-button button
e-accordion title
e-tab-container title
e-image imgId, link
e-svg imgId
e-icon-picker icon
e-gallery images

Extensible via the new webentor/block_bindings_supported_attributes filter.

Also fixed

The block render_callback discarded its $attributes argument — the value WP guarantees carries resolved Block Bindings values for dynamic blocks — and re-read $block->attributes instead. render_block_blade() now takes them as an optional third argument. No behavior change for blocks without bindings.

Explored and deliberately not shipped

  • A pattern/overrides usesContext filter. Unnecessary — WP_Block::process_block_bindings() injects the source's own uses_context before calling get_value().
  • An apply_block_bindings() resolver for the recursive Blade renderer, on the assumption nested blocks would miss bindings. Verified by stubbing it out that nested overrides still render correctly: render() merges bound values into $this->attributes in place and WP_Block_List caches instances, so the merge has already happened by the time our recursion reads them. Removed ~50 lines duplicating a private core method.

Caveats (documented)

  • Object attributes are overridden as a whole. WP keys bindings by top-level attribute name only — no button.title sub-paths. So e-button's single button object is stored wholesale: once an instance overrides a button it keeps its own variant/size/icon and stops inheriting later design changes from the pattern. Confirmed empirically. Avoiding this would mean changing the attribute contract every consumer theme's Blade views and filters build on, so it was a deliberate call.
  • Container blocks (e-accordion, e-tabs, …) are overridden through their inner blocks, not the container. Per-instance structure is impossible: core/block stores only ref + content, instances serialize self-closing, and ReusableBlockEdit wires inner blocks with onInput/onChange: NOOP.

Verification (test-site, WP 7.0 + PHP 8.4)

  • Pattern editor: Overrides panel lists overridable blocks and excludes non-enabled ones; "Enable overrides" present in Advanced.
  • Editing modes in instances: overridable → contentOnly, everything else → disabled.
  • Read + write paths driven in-browser: overrides land on the instance's content keyed by metadata.name; pattern original and sibling instance untouched. Both scalar (e-accordion.title) and object (e-button.button) paths.
  • Frontend after save matches the editor, including nested l-section > e-button and the tab nav via get_tabs_nav_data(); e-image.imgId swaps per instance.
  • scripts/check-blocks-console.mjs: 26 blocks, 0 invalid, frontend 200, no new console errors (only pre-existing __next40pxDefaultSize warnings, removal target WP 7.1 — separate item).
  • pnpm check:versions in sync; phpcs clean.

Release

core 0.15.60.15.7. Patch, so .webentor/project.json coreVersion stays ^0.15 — no consumer constraint or codemod changes. Note this is a feature-level addition that semver would put at 0.16.0; shipping as a patch means consumers on ^0.15 pick it up automatically. Safe, since it is additive and inert until someone enables overrides on a block.

New docs page: docs/src/guides/pattern-overrides.md, plus a webentor/block_bindings_supported_attributes entry in the PHP API reference.

https://claude.ai/code/session_015gnVTZywLgQYkM6Epr2uW2

…15.7)

WP 7.0 opened Pattern Overrides to custom blocks through a single server-side
opt-in: any attribute listed in `block_bindings_supported_attributes` becomes
overridable per synced-pattern instance. That one list drives the "Enable
overrides" button, the Overrides panel, the editor read/write plumbing, the
server-side substitution in WP_Block::render() and the contentOnly/disabled
locking of instances -- so no JS was needed, core handles it generically.

Opts in the content attributes of e-button (button), e-accordion and
e-tab-container (title), e-image (imgId, link), e-svg (imgId), e-icon-picker
(icon) and e-gallery (images), behind a new
`webentor/block_bindings_supported_attributes` filter so projects can extend
the map or opt their own blocks in.

Also fixes a latent issue this exposed: the block render_callback discarded its
$attributes argument -- the value WP guarantees carries resolved binding values
for dynamic blocks -- and re-read $block->attributes instead. It now passes
them into render_block_blade(), which takes an optional third argument.

Two things explored and deliberately not shipped:

- A `pattern/overrides` usesContext filter. Unnecessary:
  WP_Block::process_block_bindings() injects the source's own uses_context
  before calling get_value().
- An apply_block_bindings() resolver for the recursive Blade renderer, on the
  assumption nested blocks would miss bindings. Verified by stubbing it out
  that nested overrides still render correctly: render() merges bound values
  into $this->attributes in place and WP_Block_List caches instances, so the
  merge has already happened by the time our recursion reads them.

Verified on test-site (WP 7.0): pattern editor controls, contentOnly/disabled
modes, read and write paths (typed in-browser -- overrides land on the
instance's content keyed by metadata.name, pattern original untouched),
frontend after save including nested l-section > e-button and the tab nav in
get_tabs_nav_data(), e-image swapping per instance, and 26/26 blocks valid with
no new console errors via scripts/check-blocks-console.mjs.

Documented caveat: WP keys bindings by top-level attribute name only, so
e-button's single `button` object is overridden as a whole. An overridden
instance keeps its own variant/size/icon and stops inheriting later design
changes from the pattern -- confirmed empirically. Container blocks are
overridden through their inner blocks; per-instance structure is not possible
(core/block stores only ref + content, and instances serialize self-closing).

Claude-Session: https://claude.ai/code/session_015gnVTZywLgQYkM6Epr2uW2
@palicko
palicko merged commit 645b44d into main Aug 4, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant