Repository navigation
Conversation
A template can now name the template it is rendered inside, the way a page names the template it is rendered inside. The parent's Content block is the slot: the child template's blocks are substituted for it, so a header and a footer live in one template instead of being repeated in every one. A Content block inside a query loop belongs to the post that loop renders, so it is not the slot, and a template whose only one is there cannot render another inside. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🤖 PR meta 🤖🎉 PropsUnlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @Copilot. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. 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. Updated as activity occurs, without notifying anyone named here. Add the 🏷️ LabelsThis pull request needs exactly one label indicating its type, and has 0.
Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you. 📦 Bundle sizeSize Change: +61.4 kB (+0.68%) Total Size: 9.13 MB 📦 View Changed
⚡ PerformanceShow the resultsClient side metrics exclude the server response time. front-end-block-theme
front-end-classic-theme
media-processing
media-upload
post-editor
site-editor
🏁 Flaky testsSome tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information. Regression: updating a specific option will only change its value and will not corrupt others in
|
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # phpunit/entity-fields-api-test.php Co-authored-by: scruffian <275961+scruffian@users.noreply.github.com>
Merged |
…nside A theme names the template each of its templates is rendered inside in the `templates` section of its theme.json, so a theme works that way on activation with nothing to set up. A site overrides what the theme names, including rendering a template on its own, from the editor, and reverting a template to the one of the theme drops that choice with it. Adds the `template-inheritance` test theme, whose templates are all rendered inside its layout template, and the end to end tests that drive it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The merge of trunk converted the private selectors test to TypeScript, so the registry the tests of `getTemplateId` set up needs typing too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moves the feature to `lib/experimental/block-template-parent.php`, loaded only with the new `gutenberg-template-inheritance` experiment, while the product questions it opens are settled: named slots, what a theme may declare, and how a chain meets the template hierarchy. The `template` field of `wp_template` is registered with `@wordpress/core-fields`, which is core code with no experiment to read, so it is unregistered while the experiment is off, and the form of the template summary leaves it out. The slot now holds the blocks of the template rendered inside it rather than being replaced by them, so the content block still renders its own wrapper: its layout, its alignment and `entry-content` apply to the template inside it, and the canvas and the front end agree. Drops the `template-inheritance` test theme. The tests of what a theme declares add the declaration to the data of the block theme of the test data through the `wp_theme_json_data_theme` filter, and the end to end tests build their templates over REST, so no theme exists only to carry them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What?
Lets a template be rendered inside another template, the way a page is rendered inside the template assigned to it.
A template names the template it is rendered inside, and the parent's Content block (
core/post-content) is the slot: the child template's blocks are rendered in it. A404template can then carry only the "nothing here" message, while the header, the footer and whatever else surrounds it live in a single template.A theme names it for its own templates in the new
templatessection of itstheme.json, so a theme works that way on activation with nothing to set up. A site overrides what the theme names, including rendering a template on its own, from the Site Editor.Behind the
gutenberg-template-inheritanceexperiment, while the product questions this opens are settled: named slots, what a theme may declare, and how a chain meets the template hierarchy.Why?
Every template repeats its own header and footer today. Changing the shell of a site means editing every template that has one, and a template that only differs in its main content still has to restate everything around it.
How?
A theme declares it:
A site's own choice, stored in the
_wp_page_templatepost meta of the template's post — the same meta a page uses for the same purpose — stands for the one the theme names. The meta isdefaultwhen the site renders the template on its own and absent when the site has made no choice, so reverting a template to the one of the theme drops the choice with it. The REST field is namedtemplate, like the one a post carries.lib/experimental/block-template-parent.phpholds the server side, loaded only with the experiment:core/post-contentblock in document order, at whatever depth, takes the child's blocks as its own inner blocks rather than being replaced by them, so it still renders its wrapper: its layout, its alignment andentry-contentapply to the template rendered inside it, and the canvas and the front end agree. It is marked with atemplateSlotattribute in the markup, and aregister_block_type_argsfilter makes the block render those blocks instead of the post of the request.template_includeoncelocate_block_template()has settled on a template. It walks the chain, stopping at a template that names none, at one already in the chain, and at one with no slot. The templateslocate_block_template()already read are reused, so a site that uses none of this pays no extra query; they are forgotten when a template's post cache is cleaned.wp_template:template, the slug it is rendered inside, validated against the templates it may name, andhas_content_slot, whether it has a slot of its own.templatesis a new top-leveltheme.jsonsection: added to the schema, toWP_Theme_JSON_Gutenberg::VALID_TOP_LEVEL_KEYS, and read through a newget_templates()besideget_template_parts().packages/core-fields/src/wp_template/template/is the Template field of a template. The choices leave out the template itself, every template already rendered inside it, and every template with no slot. The field belongs to@wordpress/core-fields, which is core code with no experiment to read, so it is unregistered while the experiment is off and the template summary form leaves it out.getTemplateId()answers the template a template is rendered inside, and the provider gives the parent's Content block the template being edited as its context, so the blocks in the slot edit the child rather than the parent.A Content block inside a query loop is not the slot. It belongs to the post that loop is rendering, so putting the child there would repeat it once per post. The editor leaves those out of the post content blocks it makes editable for the same reason. This is why
index.htmlas themes ship it cannot be a parent: its only Content block is inside the Query Loop.Testing Instructions
To check what a theme declares, add a
templatessection to thetheme.jsonof a block theme, pointing one of its templates at another, and confirm the front end and the summary both follow it with nothing set in the editor.Screenshots or screencast
Use of AI Tools
Authored with Claude Code (Opus 5), directed and reviewed by me. The tests were written before the implementation and verified failing first. The front end and the editor flows were checked by hand in WordPress Playground; the end to end spec has not been run, as Docker would not start in that environment, so its selectors were pinned against the running editor rather than by executing it.