Commit ae8ac5a9c55 for woocommerce
commit ae8ac5a9c557b6e9714cf202f4fda61346824fc5
Author: Brian Coords <bacoords@gmail.com>
Date: Tue Nov 3 08:28:04 2026 -0800
Add block theme development guides (#70564)
* Add block theme development guides
* initial commit of original docs drafts
* Correct block theme development guides
* Fix callout markup in block theme guides
* Refine block theme guide references and starter guidance
* Add product and archive block theme guides
* Polish block theme development guides
* Fix block theme development guide accuracy
* Replace block theme docs screenshots with Iris examples
* Correct block theme documentation guidance
* Correct block theme guide implementation details
diff --git a/docs/theming/block-theme-development/_media/category-product-row.png b/docs/theming/block-theme-development/_media/category-product-row.png
new file mode 100644
index 00000000000..60d7f743ab8
Binary files /dev/null and b/docs/theming/block-theme-development/_media/category-product-row.png differ
diff --git a/docs/theming/block-theme-development/_media/global-styles-blocks-panel.png b/docs/theming/block-theme-development/_media/global-styles-blocks-panel.png
new file mode 100644
index 00000000000..b0cd7133739
Binary files /dev/null and b/docs/theming/block-theme-development/_media/global-styles-blocks-panel.png differ
diff --git a/docs/theming/block-theme-development/_media/global-styles-export-menu.png b/docs/theming/block-theme-development/_media/global-styles-export-menu.png
new file mode 100644
index 00000000000..a286a0eb6cf
Binary files /dev/null and b/docs/theming/block-theme-development/_media/global-styles-export-menu.png differ
diff --git a/docs/theming/block-theme-development/_media/on-sale-product-grid.png b/docs/theming/block-theme-development/_media/on-sale-product-grid.png
new file mode 100644
index 00000000000..9c728a702ed
Binary files /dev/null and b/docs/theming/block-theme-development/_media/on-sale-product-grid.png differ
diff --git a/docs/theming/block-theme-development/_media/product-collection-layout-options.png b/docs/theming/block-theme-development/_media/product-collection-layout-options.png
new file mode 100644
index 00000000000..63169696fc4
Binary files /dev/null and b/docs/theming/block-theme-development/_media/product-collection-layout-options.png differ
diff --git a/docs/theming/block-theme-development/_media/product-filters-sidebar-layout.png b/docs/theming/block-theme-development/_media/product-filters-sidebar-layout.png
new file mode 100644
index 00000000000..5de456c7784
Binary files /dev/null and b/docs/theming/block-theme-development/_media/product-filters-sidebar-layout.png differ
diff --git a/docs/theming/block-theme-development/_media/product-page-layout-iris-and-co.png b/docs/theming/block-theme-development/_media/product-page-layout-iris-and-co.png
new file mode 100644
index 00000000000..7f2a8f82504
Binary files /dev/null and b/docs/theming/block-theme-development/_media/product-page-layout-iris-and-co.png differ
diff --git a/docs/theming/block-theme-development/_media/shop-and-archive-page-layout-iris-and-co.png b/docs/theming/block-theme-development/_media/shop-and-archive-page-layout-iris-and-co.png
new file mode 100644
index 00000000000..7bb5e9e555c
Binary files /dev/null and b/docs/theming/block-theme-development/_media/shop-and-archive-page-layout-iris-and-co.png differ
diff --git a/docs/theming/block-theme-development/cart-and-checkout.md b/docs/theming/block-theme-development/cart-and-checkout.md
index 5fa95e67d3f..c3227c057a9 100644
--- a/docs/theming/block-theme-development/cart-and-checkout.md
+++ b/docs/theming/block-theme-development/cart-and-checkout.md
@@ -1,15 +1,16 @@
---
-post_title: Cart and Checkout blocks theming
-sidebar_label: Cart and Checkout blocks theming
-
+post_title: Cart and Checkout blocks theming
+sidebar_label: Cart and Checkout blocks theming
+sidebar_position: 6
---
-# Cart and checkout blocks theming
+# Cart and checkout blocks theming
-> [!IMPORTANT]
-> We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be "private", and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_.
+:::warning
+We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be "private", and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_.
+:::
-If your theme overrides `page-cart.html` or `page-checkout.html`, keep the Cart and Checkout blocks in the assigned page content and render that content from the template with `core/post-content`. See [Cart and Checkout page templates](/docs/theming/block-theme-development/theming-woo-blocks#cart-and-checkout-page-templates) for the recommended template structure.
+If your theme overrides `page-cart.html` or `page-checkout.html`, keep the Cart and Checkout blocks in the assigned page content and render that content from the template with `core/post-content`. See [Cart, Checkout, and Order Confirmation](/docs/theming/block-theme-development/template-hierarchy/#cart-checkout-and-order-confirmation) for the recommended template structure.
## Buttons
diff --git a/docs/theming/block-theme-development/css-styling.md b/docs/theming/block-theme-development/css-styling.md
index c9efcedabdc..4a672f89f43 100644
--- a/docs/theming/block-theme-development/css-styling.md
+++ b/docs/theming/block-theme-development/css-styling.md
@@ -1,14 +1,16 @@
---
post_title: CSS styling for themes
sidebar_label: CSS styling for themes
+sidebar_position: 7
---
# CSS styling for themes
## Block and component class names
-> [!IMPORTANT]
-> We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be "private", and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_.
+:::important
+We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be "private", and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_.
+:::
WooCommerce Blocks follows BEM for class names, as [stated in our coding guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/client/blocks/docs/contributors/coding-guidelines.md). All classes start with one of these two prefixes:
diff --git a/docs/theming/block-theme-development/global-styles.md b/docs/theming/block-theme-development/global-styles.md
new file mode 100644
index 00000000000..484f4c6653d
--- /dev/null
+++ b/docs/theming/block-theme-development/global-styles.md
@@ -0,0 +1,375 @@
+---
+post_title: 'Block theme development: Global styles for WooCommerce'
+sidebar_label: Global styles
+sidebar_position: 3
+---
+
+# Global styles for WooCommerce
+
+Global styles let a Block theme define typography, color, and spacing once in `theme.json`, then apply those choices everywhere a matching Block appears. WooCommerce Blocks are a part of this system, but there's differing adoption between them. This means a few product elements render through core WordPress Blocks, which changes the selector you target.
+
+This section will cover how global styles apply across a WooCommerce store through `theme.json`, starting with applying colors and typography. From there, it moves into using Block style variations, registering your own style variations, customizing the appearance in the Site Editor, and exporting a finished style configuration.
+
+## Requirements
+
+Before you begin, make sure you have the following in place:
+
+- **A Block theme.** Classic themes can also use `theme.json`, but this guide's Site Editor workflow and WooCommerce Block-template examples assume a Block theme.
+- **Familiarity with `theme.json`.** The guidance builds directly on the `theme.json` styles and settings structure rather than introducing it from scratch.
+
+## How global styles apply to WooCommerce Blocks
+
+You can target registered Blocks from `theme.json` under `styles.blocks`. A Block's registered supports tell you which editor controls and standard style handling it explicitly exposes. [WooCommerce Blocks](https://developer.woocommerce.com/docs/block-development/reference/block-references/) offer varied support, so confirm both the support and the selector used for editor and front-end output.
+
+For example, `woocommerce/product-price` supports color, typography, and spacing. The `woocommerce/product-collection` wrapper supports alignment and layout, while its descendant Blocks provide the styling controls for product cards. As such, confirm what each Block supports before writing style rules.
+
+Also, some elements are actually core WordPress Blocks configured for a product context rather than dedicated WooCommerce Blocks:
+
+- **Product titles** render through `core/post-title` in WooCommerce's current Single Product template.
+- **Product summaries** render through `core/post-excerpt` in that template.
+- **Product categories and tags** render through `core/post-terms`, configured with WooCommerce-specific taxonomy variations rather than a dedicated WooCommerce Block.
+- **Product grids created with Product Collection** render each item's title through `core/post-title` when the template follows WooCommerce's own patterns.
+
+WooCommerce builds its templates by mixing its own and core WordPress Blocks wherever the latter already does the job. The reliable way to confirm what you're styling is to inspect the template's Block markup through the **Code editor** in the Site Editor. Use the WordPress REST API's Block Types endpoint separately to inspect a registered Block's supports, selectors, styles, and variations.
+
+## Using `theme.json` to define WooCommerce-specific styles
+
+Most of your styling work happens within `theme.json`. The next three sections cover typography for text-based product details, setting colors for on-screen elements, and spacing for product grids.
+
+### Styling typography for product titles, prices, and descriptions
+
+`woocommerce/product-price` and `woocommerce/product-summary` both offer typography support, including font size, line height, font family, font weight, and letter spacing. You can style them directly when those Blocks appear in your template:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-price": {
+ "typography": {
+ "fontSize": "1.25rem",
+ "fontWeight": "600"
+ }
+ },
+ "woocommerce/product-summary": {
+ "typography": {
+ "fontSize": "var(--wp--preset--font-size--medium)",
+ "lineHeight": "1.6"
+ }
+ }
+ }
+ }
+}
+```
+
+Product titles need a different approach depending on where they appear. WooCommerce's current Single Product and Product Collection templates both use `core/post-title`. The dedicated `woocommerce/product-title` Block still exists for other product contexts, so target it only when the template markup contains it:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "core/post-title": {
+ "typography": {
+ "fontSize": "var(--wp--preset--font-size--x-large)",
+ "fontWeight": "700"
+ }
+ }
+ }
+ }
+}
+```
+
+This `core/post-title` rule also affects Post Title Blocks outside WooCommerce. If product titles need a store-specific treatment, set styles in the WooCommerce template markup or use narrowly scoped CSS rather than applying a site-wide Block rule.
+
+If you're ever unsure of the rendering, inspect the Block markup for the template. WooCommerce's template composition has changed over time, so the safest confirmation is always the markup itself.
+
+`woocommerce/product-summary` supports font size, line height, text alignment, font family, weight, style, and letter spacing. WooCommerce's current Single Product template uses `core/post-excerpt` for the short description, so confirm which Block the template uses before targeting either one. The longer description can be rendered through a different Block or a Product Details section.
+
+### Applying color to badges, buttons, and alerts
+
+`woocommerce/product-sale-badge` supports background color, gradients, typography, margin, and border color, radius, and width, which makes it well suited to a pill-shaped badge:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-sale-badge": {
+ "color": {
+ "background": "var(--wp--preset--color--accent-2)"
+ },
+ "border": {
+ "radius": "999px",
+ "width": "0"
+ },
+ "spacing": {
+ "margin": { "top": "0.5rem", "right": "0.5rem" }
+ }
+ }
+ }
+ }
+}
+```
+
+The Add to Cart button (`woocommerce/product-button`) supports background and text color:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-button": {
+ "color": {
+ "background": "var(--wp--preset--color--contrast)",
+ "text": "var(--wp--preset--color--base)"
+ }
+ }
+ }
+ }
+}
+```
+
+The `woocommerce/store-notices` Block does not register color or typography supports, so it does not expose those controls in the editor. A `theme.json` rule can style the Block wrapper, but each notice type has internal markup and state-specific styling. Use a block stylesheet when you need to target those internal notice variants precisely.
+
+### Setting spacing and layout for product grids
+
+`woocommerce/product-collection`, the Block behind product grids, supports alignment and its own internal layout controls. However, it doesn't register color, typography, or spacing supports.
+
+Spacing a grid consistently means two separate jobs: using the Block's built-in **Layout** controls for layout type, column count, and responsive column behavior, and styling the inner Blocks that repeat inside each grid item. Product Collection does not currently provide a separate gap control.
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-image": {
+ "spacing": {
+ "margin": { "bottom": "0.75rem" }
+ }
+ },
+ "woocommerce/product-price": {
+ "spacing": {
+ "margin": { "bottom": "0.5rem" }
+ }
+ }
+ }
+ }
+}
+```
+
+The wrapper around each item in the grid (`woocommerce/product-template`) *does* accept color and typography, which makes it a reasonable place to set a shared text color or font across every product card without repeating the rule on each child Block.
+
+Its layout controls are intentionally locked from further editing in the interface. This keeps the grid structure consistent, but the color and typography rules you set through `theme.json` still apply and cascade down to the inner Blocks unless there's a child Block override.
+
+In summary, overall grid structure and column count belong to the Product Collection Block's own Layout controls. Use inner Block margins or a block stylesheet when you need to control gaps between product cards. Spacing between a product's image, title, and price within a card belongs to the margin and padding rules on those individual inner Blocks.
+
+## Block-level style variations for WooCommerce Blocks
+
+Block style variations give a Block two or more selectable visual presets, switchable from the Block toolbar and stylable independently through `theme.json`.
+
+`woocommerce/product-button` ships with two: the default Fill style and Outline. You can restyle either directly:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-button": {
+ "variations": {
+ "outline": {
+ "border": {
+ "radius": "0"
+ },
+ "color": {
+ "background": "transparent",
+ "text": "var(--wp--preset--color--contrast)"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+Product categories and tags offer a similar-looking choice between a default list and a pill-shaped display. This option belongs to `core/post-terms` rather than a WooCommerce Block, since WooCommerce configures that core Block with product-specific taxonomy variations rather than registering its own.
+
+The pill style itself is registered by the active theme rather than by WordPress or WooCommerce core, so it won't appear on every install. Where it's available, style it the same way:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "core/post-terms": {
+ "variations": {
+ "pill": {
+ "border": {
+ "radius": "999px",
+ "width": "1px"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+:::info
+A 'Block style variation' covered here changes a Block's visual appearance and is selectable from the Block toolbar's **Styles** panel. A 'Block variation' is used to offer different preset configurations of the same Block's attributes.
+:::
+
+The latter is behind the switch between Categories, Tags, and Brands on the Post Terms Block. The two use cases overlap here, which is part of why it's an easy pair of concepts to mix up.
+
+## Creating and registering custom style variations
+
+Registering a new style variation for a Block involves registering the style itself, then styling it through `theme.json`. For instance, through your theme's `functions.php` file (or a custom plugin) you can register a variation for `woocommerce/product-button` with `register_block_style()`:
+
+```php
+function theme_register_button_styles() {
+ register_block_style(
+ 'woocommerce/product-button',
+ array(
+ 'name' => 'rounded',
+ 'label' => __( 'Rounded', 'your-textdomain' ),
+ )
+ );
+}
+
+add_action( 'init', 'theme_register_button_styles' );
+```
+
+:::info
+Remember to switch out the placeholder in the array for your own text domain.
+:::
+
+Once registered, the new **Rounded** option appears in the Block's style picker. Style it through `styles.blocks.woocommerce/product-button.variations`, using the registered `rounded` slug rather than its display label:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-button": {
+ "variations": {
+ "rounded": {
+ "border": {
+ "radius": "999px"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+WordPress 6.6 and newer can also register a Block style variation from a `theme.json` partial in the theme's `/styles` directory by providing `slug`, `title`, `blockTypes`, and `styles` properties. This is useful when you want registration and styling to live in one JSON file.
+
+Global style variations are a separate, theme-wide mechanism unrelated to individual Block styles. A theme can ship complete alternate style presets as JSON files in its `/styles` directory:
+
+```json
+{
+ "$schema": "https://schemas.wp.org/trunk/theme.json",
+ "version": 3,
+ "title": "Bold",
+ "settings": {
+ "color": {
+ "palette": [
+ { "slug": "contrast", "color": "#1a1a1a", "name": "Contrast" }
+ ]
+ }
+ }
+}
+```
+
+A full global style variation appears as a selectable preset in the Site Editor's **Styles** panel, labeled with its `title` property. The `/styles` directory can also contain Block, color, and typography variation partials, so not every JSON file there represents a complete theme-wide preset.
+
+:::tip
+You can name your file anything you like, but it's a good idea to stick to the convention used by the other files in the `/styles` folder. Pin `$schema` to the oldest WordPress version your theme supports when you need validation against that version rather than trunk.
+:::
+
+Once you select one, WordPress copies the configuration into the database as an active customization, so a later update to the theme file won't reach stores that have already made a selection.
+
+:::tip
+When shipping a theme update, changing a `/styles` file after stores have adopted it updates the option for new selections only, not for stores already using it. Your theme changelogs should therefore call out style variation changes explicitly.
+:::
+
+A style variation file can include any part of `theme.json`, not just color. For instance, a single file might set a full color palette, adjust the default spacing scale, and swap the heading font family together. This gives you a single, clean choice rather than several separate settings to coordinate.
+
+## Customizing WooCommerce Blocks in the Site Editor
+
+Many color, typography, and spacing controls described so far are also available from the Site Editor's interface, without writing `theme.json` directly. To find them, open **Appearance > Editor** within WordPress, then select the **Styles** icon to reach the global **Typography**, **Colors**, and **Layout** panels. Here, you'll also find a **Blocks** panel for Block-specific styles:
+
+
+
+An individual product Block exposes only the controls it supports in its Block settings sidebar. The exact controls therefore differ between Product Price, Product Title, Product Summary, Product Collection, and other Blocks.
+
+:::info
+Changes made this way are saved as user global-style customizations and take priority over the theme's `theme.json` values.
+:::
+
+This makes the Site Editor a way to prototype before committing anything to code. Because every change appears immediately against real product data, it's a faster way to judge whether a font size or color choice actually reads well on a product card than repeatedly editing `theme.json` and reloading the page.
+
+Once a design is settled, the same values can be transcribed into `theme.json` for version control. Alternatively, the Site Editor's export feature can produce an updated theme package.
+
+## Exporting and sharing your global styles
+
+A finished set of global styles, whether built through `theme.json` or assembled in the Site Editor, can be [exported with the active theme](https://wordpress.org/documentation/article/site-editor/#how-to-export-templates-and-styles). From **Appearance > Editor**, open the three-dot menu and select **Tools** > **Export**. The exact menu position can change between WordPress releases.
+
+
+
+WordPress generates a ZIP file containing an exported theme with the site's templates, template parts, and style settings. You can install that exported theme on another WooCommerce site or extract its files into a theme project. This is not a styles-only import into an independently updated copy of the original theme.
+
+This is useful since it produces a snapshot of the current design that can sit alongside version control as a reference point, separate from whatever has changed in the theme's own files since. It's also the fastest way to hand a client-approved design to a second site without re-entering every color and typography choice by hand.
+
+## Common patterns for consistent store styling
+
+A few patterns come up repeatedly when styling a WooCommerce store: keeping every button on the site visually consistent, pairing a sale badge's color with its text, and giving prices more visual weight than the copy around them.
+
+Consistent button styling across certain Blocks and elements is straightforward because the same Fill and Outline style names are registered:
+
+- `woocommerce/product-button`
+- `woocommerce/mini-cart-shopping-button-block`
+- `woocommerce/mini-cart-cart-button-block`
+- `woocommerce/mini-cart-checkout-button-block`
+
+You can repeat the same Block-specific variation styles for these Blocks or use shared variation styling supported by `theme.json` version 3. Sale badge styling combines visual and text customization: color and border rules control the appearance, while the badge's text is controlled separately through a PHP filter rather than `theme.json`:
+
+```php
+add_filter( 'woocommerce_sale_badge_text', function( $sale_text, $product ) {
+ return __( 'Limited Offer', 'your-textdomain' );
+}, 10, 2 );
+```
+
+Price formatting typically means controlling the visual weight of the price rather than the number format itself, which WooCommerce handles through its currency settings. A common pattern is giving the price more visual weight than surrounding text:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-price": {
+ "typography": {
+ "fontWeight": "700",
+ "fontSize": "1.1em"
+ }
+ }
+ }
+ }
+}
+```
+
+## Checking your work before you ship it
+
+Understanding which Block renders a given element, what that Block supports, and which style variations are available all depend on the WordPress and WooCommerce versions in use. In the case of Block styles (such as the Post Terms 'pill' option), the active theme matters too.
+
+Before relying on any selector in production, look to confirm it against your own site rather than solely on an external resource:
+
+- **Check a Block's actual supports** through the WordPress REST API's Block Types endpoint, such as `/wp-json/wp/v2/block-types/woocommerce/product-price`. This reflects what's registered at runtime.
+- **Look at which Block is rendering** by exporting the markup for the template you're editing, particularly for product titles and taxonomy displays. WooCommerce composes these from core and WooCommerce Blocks depending on context.
+- **View the available style variations** in the Site Editor's Block toolbar before writing `theme.json` rules against them, since some depend on the active theme rather than WooCommerce.
+
+With those checks in place, you can move through a complete path from a bare Block theme to a fully styled WooCommerce store: typography, color, and spacing through `theme.json`, style variations for consistent visual choices across related Blocks, and the Site Editor for building and exporting the result.
diff --git a/docs/theming/block-theme-development/migrating-from-classic-to-block-themes.md b/docs/theming/block-theme-development/migrating-from-classic-to-block-themes.md
new file mode 100644
index 00000000000..8d81a76d165
--- /dev/null
+++ b/docs/theming/block-theme-development/migrating-from-classic-to-block-themes.md
@@ -0,0 +1,120 @@
+---
+post_title: Migrating from classic to Block themes
+sidebar_label: Migrating from classic themes
+sidebar_position: 8
+---
+
+# Migrating from classic to Block themes
+
+If you have built WooCommerce themes using PHP templates, `style.css` overrides, and `functions.php` hooks, the good news is that most of your knowledge carries over to Block themes. The underlying store pages still exist and WooCommerce has ways to override templates and apply styles.
+
+However, the difference is where that work happens. This page will map some classic patterns to its Block theme equivalent. It will also point you to documentation that covers it in greater detail.
+
+## Requirements
+
+Before you begin, make sure you have the following in place:
+
+- **Familiarity with classic WooCommerce theme development.** This page assumes working knowledge of PHP templates, action hooks, and CSS overrides.
+- **A code editor capable of editing HTML and JSON files.** Block templates are HTML and theme styles are defined in `theme.json`.
+- **The Create Block Theme plugin (optional).** This can help create or export a Block theme, but it is not required and does not automatically translate classic WooCommerce PHP templates or hooks.
+
+## Template overrides
+
+Classic and Block themes both support template overrides, but the file format and the folder location changes:
+
+- **Classic themes** copy PHP files from the WooCommerce plugin into a `/woocommerce` directory in the theme root (for example, `yourtheme/woocommerce/single-product.php`).
+- **Block themes** place HTML Block templates in a `/templates` folder in the theme root (for example, `yourtheme/templates/single-product.html`).
+
+Some top-level store views retain familiar slugs across both approaches. For example, `single-product.php` and `single-product.html` both represent the single product view. This is not a direct file conversion: many PHP partials have no HTML template counterpart, and the HTML template's content must be composed from WooCommerce and WordPress Blocks rather than copied from the PHP markup.
+
+To override the single product template in a classic theme, you copy and edit a PHP file:
+
+```text
+wp-content/themes/yourtheme/woocommerce/single-product.php
+```
+
+In a Block theme, you create an HTML template in the equivalent location:
+
+```text
+wp-content/themes/yourtheme/templates/single-product.html
+```
+
+WooCommerce registers HTML Block templates and fallback slugs for core store views, including single product, product archive, cart, checkout, search, and taxonomy archives. A merchant's Site Editor customization takes priority, followed by a matching file in the theme's `/templates` folder and then the WooCommerce default.
+
+The [template hierarchy reference](/docs/theming/block-theme-development/template-hierarchy/) covers every available template and explains how the fallback order works.
+
+## Declaring WooCommerce support
+
+Classic themes commonly declare WooCommerce support in `functions.php`. Block themes are treated as WooCommerce-compatible without declaring the base support flag:
+
+- `add_theme_support( 'woocommerce' )` is not required for WooCommerce to recognize a Block theme or load its HTML template overrides.
+- Arguments passed with that support, such as classic image widths or product-grid limits, can still affect legacy templates and older product Blocks. Audit those values before removing the declaration.
+- `add_theme_support( 'wc-product-gallery-zoom' )` and the related lightbox and slider flags are still read by the Product Image Gallery Block to enqueue legacy gallery behavior.
+
+In a nutshell, do not copy support declarations into a new Block theme automatically, but do not remove them automatically either. Keep the declarations whose behavior your theme still needs, test the current Product Gallery and Product Image Gallery Blocks, and remove only the declarations that are genuinely obsolete.
+
+## Styling
+
+Classic WooCommerce themes apply styles in `style.css`, often targeting unprefixed class names such as `.price`, `.button`, and `.star-rating`. Block themes use `theme.json` as the primary styling layer.
+
+Defining styles happens at the Block level using the Block's registered namespace, while WordPress generates the required CSS from those definitions.
+
+Using `theme.json` for WooCommerce styles brings several advantages over CSS overrides:
+
+- **Reduced reliance on private markup.** Styles are defined against a registered Block and its selector rather than a deep chain of internal class names. Block registrations can still change, so verify names and supports when updating WooCommerce.
+- **Site Editor compatibility.** Site owners can override your styles in the Site Editor without touching code.
+- **Managed precedence.** WordPress resolves core, theme, and user global styles through the same style engine, reducing duplicate declarations and specificity conflicts.
+
+The following example sets the text color and font weight for the Product Price Block:
+
+```json
+{
+ "version": 3,
+ "styles": {
+ "blocks": {
+ "woocommerce/product-price": {
+ "color": {
+ "text": "#333333"
+ },
+ "typography": {
+ "fontWeight": "700"
+ }
+ }
+ }
+ }
+}
+```
+
+CSS is still available where `theme.json` does not cover a specific scenario. Prefer registered Block selectors and WordPress block stylesheets over deep selectors. Do not assume every `.wc-block-` or `.wc-block-components-` class is a public API; WooCommerce considers internal Block markup subject to change. The [CSS styling reference](https://developer.woocommerce.com/docs/theming/block-theme-development/css-styling/) covers the class naming conventions and their stability, while there's also documentation on the recommended approach to [Cart and Checkout Block styling](https://developer.woocommerce.com/docs/theming/block-theme-development/cart-and-checkout/).
+
+## Layout hooks
+
+Classic themes use `woocommerce_before_main_content` and `woocommerce_after_main_content` in `functions.php` to wrap WooCommerce output in the markup a theme's layout requires:
+
+```php
+remove_action( 'woocommerce_before_main_content', 'woocommerce_output_content_wrapper', 10 );
+remove_action( 'woocommerce_after_main_content', 'woocommerce_output_content_wrapper_end', 10 );
+
+add_action( 'woocommerce_before_main_content', 'mytheme_wrapper_start', 10 );
+add_action( 'woocommerce_after_main_content', 'mytheme_wrapper_end', 10 );
+
+function mytheme_wrapper_start() {
+ echo '<section id="main">';
+}
+
+function mytheme_wrapper_end() {
+ echo '</section>';
+}
+```
+
+WooCommerce's compatibility layer runs these hooks around relevant Blocks in the Blockified Single Product and product archive templates. That preserves many extension callbacks, but the exact insertion point follows the Block template rather than the old PHP wrapper. It can also be disabled with the `woocommerce_disable_compatibility_layer` filter.
+
+Layout control in a Block theme belongs in the template itself, either by editing the relevant template in the Site Editor or by placing an HTML override in the theme's `/templates` folder. Test hook-based customizations during migration, and move wrapper and layout concerns into the Block template instead of relying on the compatibility layer's placement.
+
+## Migration process
+
+If you are converting an existing classic theme, the [Create Block Theme plugin](https://wordpress.org/plugins/create-block-theme/) can help generate or export a Block theme file structure and `theme.json`. Treat its output as a starting point: you still need to recreate PHP template overrides as Block markup, move layout decisions out of hooks, and translate styles deliberately.
+
+[Purple](https://wordpress.org/themes/purple/) is a WooCommerce starter Block theme available in the WordPress.org theme directory. Its commerce-ready templates and patterns provide a working reference or starting point for a new theme.
+
+Once the base structure is in place, you can apply the WooCommerce-specific customizations covered in this section directly. The [Develop Your First Low-Code Block Theme](https://learn.wordpress.org/course/develop-your-first-low-code-block-theme/) course on Learn WordPress is a great way to establish that foundational structure.
diff --git a/docs/theming/block-theme-development/product-page-theming.md b/docs/theming/block-theme-development/product-page-theming.md
new file mode 100644
index 00000000000..65a9e2ce1ca
--- /dev/null
+++ b/docs/theming/block-theme-development/product-page-theming.md
@@ -0,0 +1,421 @@
+---
+post_title: 'Block theme development: Product page theming'
+sidebar_label: Product page theming
+sidebar_position: 4
+---
+
+# Product page theming
+
+WooCommerce's Single Product template is what a shopper will spend the most time viewing before they decide to buy. It's built from three overlapping layers: the classic PHP template, a Block-based compatibility layer that preserves the classic template's action hooks, and the fully Blockified template that default Block themes use.
+
+This guide covers Blockified templates. It builds on the [Template hierarchy reference](/docs/theming/block-theme-development/template-hierarchy/) and [Global styles for WooCommerce](/docs/theming/block-theme-development/global-styles/), so if template overrides or `theme.json` styling are unfamiliar concepts, start with those first.
+
+## Requirements
+
+Before you begin, make sure you have the following in place:
+
+- **A Block theme.** Every Block discussed here renders on the Blockified Single Product template, which only Block themes use by default.
+- **Familiarity with the [Template hierarchy reference](/docs/theming/block-theme-development/template-hierarchy/).** You should understand how `single-product.html` resolves and how theme overrides take priority.
+- **Familiarity with the Site Editor's List View and Code editor.** Several examples in this guide need you to inspect Block markup directly rather than relying on what's visible in the canvas.
+- **Knowledge of Global styles for WooCommerce.** You use `theme.json` to style Blocks, so understanding this aspect is vital.
+- **An understanding of your catalog's product types.** Simple, variable, grouped, and external products each render a different add-to-cart form.
+
+## Anatomy of the Single Product template
+
+Locating `single-product.html` can be confusing, since WooCommerce's own classic template file is called `single-product.php`. The `.html` file is a Block Template that resolves through the template hierarchy. WooCommerce ships two versions:
+
+- A plain version that hands the page over to a classic PHP template.
+- The Blockified `single-product.html` covered here.
+
+WooCommerce chooses the correct template and a theme can override it with its own copy stored under the `/templates` folder. It's also editable through the Site Editor:
+
+
+
+:::tip
+Open **Templates \> Single Product** within the Site Editor and check for a single `woocommerce/legacy-template` Block. This means WooCommerce is using the classic PHP template. The Blockified versions use Blocks such as `woocommerce/product-image-gallery` and `woocommerce/add-to-cart-form`.
+:::
+
+The markup wraps the page in the site's shared header and footer template parts, then renders a breadcrumb trail and store notices above the two-column layout:
+
+```html
+<!-- wp:template-part {"slug":"header"} /-->
+
+<!-- wp:group {"tagName":"main","layout":{"inherit":true,"type":"constrained"}} -->
+<main class="wp-block-group">
+ <!-- wp:woocommerce/breadcrumbs /-->
+ <!-- wp:woocommerce/store-notices /-->
+
+ <!-- wp:columns {"align":"wide"} -->
+ <div class="wp-block-columns alignwide">
+ <!-- wp:column {"width":"512px"} -->
+ <div class="wp-block-column" style="flex-basis:512px">
+ <!-- wp:woocommerce/product-image-gallery /-->
+ </div>
+ <!-- /wp:column -->
+
+ <!-- wp:column -->
+ <div class="wp-block-column">
+ <!-- wp:post-title {"level": 1, "__woocommerceNamespace":"woocommerce/product-title"} /-->
+
+ <!-- wp:woocommerce/product-rating /-->
+
+ <!-- wp:woocommerce/product-price {"fontSize":"large"} /-->
+
+ <!-- wp:post-excerpt {"__woocommerceNamespace":"woocommerce/product-summary", "excerptLength":100} /-->
+
+ <!-- wp:woocommerce/add-to-cart-form /-->
+
+ <!-- wp:woocommerce/product-meta -->
+ <div class="wp-block-woocommerce-product-meta">
+ <!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap"}} -->
+ <div class="wp-block-group">
+ <!-- wp:woocommerce/product-sku /-->
+
+ <!-- wp:post-terms {"term":"product_cat","prefix":"Category: "} /-->
+
+ <!-- wp:post-terms {"term":"product_tag","prefix":"Tags: "} /-->
+ </div>
+ <!-- /wp:group -->
+ </div>
+ <!-- /wp:woocommerce/product-meta -->
+ </div>
+ <!-- /wp:column -->
+ </div>
+ <!-- /wp:columns -->
+
+ <!-- wp:woocommerce/product-details {"align":"wide","className":"is-style-minimal"} /-->
+
+ <!-- wp:pattern {"slug":"woocommerce-blocks/related-products"} /-->
+</main>
+<!-- /wp:group -->
+
+<!-- wp:template-part {"slug":"footer"} /-->
+```
+
+Everything inside the main Group is specific to the Single Product template. The header and footer template parts are shared across every other template your theme registers. However, this guide only concerns itself with what's inside main.
+
+## The Blocks that build the page
+
+Alongside a few single Blocks, the template's primary Blocks fall into three groups: a gallery, an add-to-cart area, and the Product Details Block. This last one covers a product's description, attributes, and reviews.
+
+WooCommerce Blocks preserve a defined set of classic PHP action hooks by anchoring them to specific Blocks. This helps many hook-based extensions continue to work on a Block theme, but only where WooCommerce maps the extension's hook to an available Block anchor.
+
+Regardless, each of these has quirks that you'll need to understand before you start moving things around.
+
+:::tip
+Every `block.json` referenced throughout this guide lives in `wp-content/plugins/woocommerce/client/blocks/assets/js/`. It's split between single-purpose display Blocks at `blocks/product-elements-blocks/{name}/block.json` and container Blocks at `blocks/{name}/block.json`.
+:::
+
+### Choosing a gallery Block
+
+The default template uses `woocommerce/product-image-gallery`, a single Block with no visible child Blocks. Unlike a typical Block settings panel, this has no image size, product link, or sale badge controls. Instead, it wraps around WooCommerce's classic PHP gallery functions. It's the same compatibility layer found in the Product Details Block.
+
+However, there's a second, richer gallery Block: `woocommerce/product-gallery`. It consists of two child Blocks: thumbnails, and a large image viewer that contains the product image, sale badge, and next/previous controls.
+
+You'll see this version if you use the Single Product Block to embed a product elsewhere on the site rather than on the Single Product template itself:
+
+```html
+<!-- wp:woocommerce/product-gallery -->
+<div class="wp-block-woocommerce-product-gallery wc-block-product-gallery">
+ <!-- wp:woocommerce/product-gallery-thumbnails /-->
+ <!-- wp:woocommerce/product-gallery-large-image -->
+ <div class="wp-block-woocommerce-product-gallery-large-image wc-block-product-gallery-large-image__inner-blocks">
+ <!-- wp:woocommerce/product-image {"showProductLink":false,"showSaleBadge":false} /-->
+ <!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
+ <!-- wp:woocommerce/product-gallery-large-image-next-previous /-->
+ </div>
+
+ <!-- /wp:woocommerce/product-gallery-large-image -->
+</div>
+<!-- /wp:woocommerce/product-gallery -->
+```
+
+If your theme's Single Product template only shows the simpler gallery and you want thumbnails and a large-image viewer instead, note that `product-gallery` restricts its `ancestor` within `block.json` to `woocommerce/single-product`. As such, the Block inserter won't offer it to you.
+
+### Two ways to add to cart
+
+Two different Blocks handle adding a product to the cart. `woocommerce/add-to-cart-form` renders a single form that adapts internally to the product type. It's a simpler starting point as it uses a `quantitySelectorStyle` attribute to switch between a text input and a stepper. Here, the Block uses the same per-product action hooks (and compatibility pattern) WooCommerce employs elsewhere, rather than use independent markup.
+
+`woocommerce/add-to-cart-with-options` is newer and replaces the single form with a container that renders a different template part depending on the product type: simple, variable, grouped, or external.
+
+:::tip
+A wrapper Block's markup is always empty. The editable markup for each product type is in separate files:
+
+- `wp-content/plugins/woocommerce/templates/parts/simple-product-add-to-cart-with-options.html`
+- `wp-content/plugins/woocommerce/templates/parts/variable-product-add-to-cart-with-options.html`
+- `wp-content/plugins/woocommerce/templates/parts/grouped-product-add-to-cart-with-options.html`
+- `wp-content/plugins/woocommerce/templates/parts/external-product-add-to-cart-with-options.html`
+
+To customize a layout, you'll need to override the relevant file in your theme's `/parts` folder.
+:::
+
+WooCommerce generates a template part reference for each registered product type without filtering by your current catalog. This is typically the four built-in types plus any extra you register. WooCommerce only provides template part files for the four core types, so a third-party type must provide a valid template part file path through the experimental `__experimental_woocommerce_{product_type}_add_to_cart_with_options_block_template_part` filter. A matching file in the theme's `/parts` folder can then override that registered template part, but it cannot enable the product type by itself. Without a valid filter-provided path, the Block has no add-to-cart template to render for that product type.
+
+For example, a variable product's part builds its attribute selector from `add-to-cart-with-options-variation-selector` and its child Blocks, then adds a quantity selector and the Add to Cart Block underneath:
+
+```html
+<!-- wp:woocommerce/add-to-cart-with-options-variation-selector -->
+<div class="wp-block-woocommerce-add-to-cart-with-options-variation-selector" role="list">
+ <!-- wp:woocommerce/add-to-cart-with-options-variation-selector-attribute -->
+ <div class="wp-block-woocommerce-add-to-cart-with-options-variation-selector-attribute" role="listitem">
+ <!-- wp:group {"style":{"spacing":{"blockGap":"0.5rem","margin":{"top":"1rem","bottom":"1rem"}}},"layout":{"type":"flex","orientation":"vertical","flexWrap":"nowrap"}} -->
+ <div class="wp-block-group" style="margin-top: 1rem; margin-bottom: 1rem">
+ <!-- wp:woocommerce/add-to-cart-with-options-variation-selector-attribute-name /-->
+ <!-- wp:woocommerce/product-filter-chips -->
+ <div class="wp-block-woocommerce-product-filter-chips wc-block-product-filter-chips"></div>
+ <!-- /wp:woocommerce/product-filter-chips -->
+ </div>
+ <!-- /wp:group -->
+ </div>
+ <!-- /wp:woocommerce/add-to-cart-with-options-variation-selector-attribute -->
+</div>
+<!-- /wp:woocommerce/add-to-cart-with-options-variation-selector -->
+
+<!-- wp:woocommerce/add-to-cart-with-options-variation-description {"style":{"spacing":{"margin":{"top":"1rem","bottom":"1rem"}}}} /-->
+
+<!-- wp:woocommerce/product-stock-indicator {"style":{"spacing":{"margin":{"top":"1rem","bottom":"1rem"}}}} /-->
+
+<!-- wp:group {"layout":{"type":"flex","flexWrap":"wrap","verticalAlignment":"stretch"}} -->
+<div class="wp-block-group">
+ <!-- wp:woocommerce/add-to-cart-with-options-quantity-selector {"quantitySelectorStyle":"stepper"} /-->
+ <!-- wp:woocommerce/product-button {"textAlign":"left"} /-->
+</div>
+<!-- /wp:group -->
+```
+
+Because each product type gets its own file under a theme's `/parts` folder, you can override one product type's add-to-cart layout without touching the others. For instance, a grouped product needs a very different layout. Each child product renders a quantity input, checkbox, or Add to Cart button. This is decided on a per-item basis depending on whether the child product is sold individually, is out of stock, or has options.
+
+This is handled by giving each type a separate template part rather than one form with conditional logic inside it.
+
+### The Product Details Block: not as empty as it looks
+
+`woocommerce/product-details` can look empty as its `block.json` declares only an `align` and a `hideTabTitle` attribute with no default child Blocks of its own:
+
+```html
+<!-- wp:woocommerce/product-details {"className":"is-style-minimal"} -->
+<div class="wp-block-woocommerce-product-details alignwide is-style-minimal"></div>
+<!-- /wp:woocommerce/product-details -->
+```
+
+On the front end, this renders WooCommerce's Description, Additional information, and Reviews tabs. This is a compatibility fallback: when the Block has no inner Blocks, it renders the same tab markup WooCommerce generates on a singular product page.
+
+To get an editable, Blockified version of the same three tabs, add `core/accordion` (or `woocommerce/accordion-group`) inside the Product Details Block, then place them as accordion items:
+
+- `woocommerce/product-description` displays the product's full description.
+- `woocommerce/product-specifications` shows the weight, dimensions, and attributes, with `showAttributes`, `showDimensions`, and `showWeight` attributes to control which appear.
+- `woocommerce/product-reviews` gives you the review list, along with its own child Blocks for the review's form, author name, rating, date, and pagination.
+
+WooCommerce also auto-detects non-default tabs returned through the `woocommerce_product_tabs` filter, regardless of whether a plugin, theme, or another customization added them. If you'd rather turn this off, you can use the `woocommerce_disable_compatibility_layer` filter.
+
+:::note
+Some Blocks have permanently empty wrapper markup (such as `woocommerce/add-to-cart-with-options`). For those Blocks, content will be generated at render time based on the template files.
+:::
+
+## Rearranging the page layout without breaking it
+
+Swapping which template part renders for a given product type is one way of rearranging your page layout. There are also two more:
+
+- Restructuring the shared `single-product.html` template that every product uses.
+- Overriding the layout for one specific product.
+
+Of course, each has its own mechanics and its own limitations to contend with.
+
+### Moving Blocks: context over position
+
+The data for Product Blocks on a Single Product template doesn't rely on its position in the markup. For example, moving the Product Price Block out of its column (or out of the two-column layout entirely) still renders the correct price. This is because the data comes from the template's product context (the `postId` the template is rendering), not from its position in the DOM.
+
+The limitation is that several product Blocks declare an `ancestor` restriction in `block.json`, which controls the Block inserter's placement options. For instance, `woocommerce/product-button` restricts its `ancestor` to `woocommerce/all-products`, `woocommerce/single-product`, `core/post-template`, and `woocommerce/product-template`.
+
+In practice, this means the inserter won't offer you the Add to Cart Button as a top-level Block dropped directly onto an arbitrary template; it expects one of the nearby container Blocks. This is separate from the `postId` context:
+
+- `ancestor` gates where a Block can be inserted and checks against the Block's actual parents in the tree.
+- `usesContext` (typically `postId`, `query`, and `queryId`) determines what data a placed Block renders, which is resolved from the template or Query Loop it sits inside.
+
+However, while the Site Editor's **List View** will show you the Block tree, it doesn't flag which Blocks have contextual ties. The solution is to preview the front end rather than hoping the editor will catch it.
+
+### What removing a Block can break
+
+The compatibility layer means some Blocks are doing more under the hood, so removing them has consequences. WooCommerce's classic template fires a sequence of hooks around the product summary, all attached to the `woocommerce_single_product_summary` action:
+
+| Hook | Priority | Output |
+| :---: | :---: | :---: |
+| `woocommerce_template_single_title` | 5 | Product title |
+| `woocommerce_template_single_rating` | 10 | Star rating |
+| `woocommerce_template_single_price` | 10 | Price |
+| `woocommerce_template_single_excerpt` | 20 | Short description |
+| `woocommerce_template_single_add_to_cart` | 30 | Add-to-Cart form |
+| `woocommerce_template_single_meta` | 40 | SKU, categories, tags |
+| `woocommerce_template_single_sharing` | 50 | Sharing integrations |
+
+On the Blockified template, the `woocommerce_single_product_summary` action is wired to the `core/post-excerpt` Block rendering the short description. Any extension that still adds content through this hook is anchoring itself to that Block. In practice, you'll use this for elements such as trust badges and upsell prompts.
+
+The compatibility layer handles sharing separately. It removes `woocommerce_template_single_sharing` from the summary action and injects the underlying `woocommerce_share` action before Product Details. Removing the short-description Block doesn't remove sharing output, but removing Product Details can.
+
+Also, Product Details only calls WordPress' `the_post()` function while rendering its legacy fallback tabs. Once you make it Blockified, this call disappears. Related Products' underlying `woocommerce/product-template` Block calls `the_post()` for each product in the loop, then `wp_reset_postdata()`. This restores global `$product` to the product being viewed, independent of the state of Product Details.
+
+:::tip
+Before removing the short-description Post Excerpt, Product Details, or Related Products Blocks from a live template, test the change against whatever extensions your store runs. An empty layout slot isn't always a visual change.
+:::
+
+### Going narrower: overriding one product
+
+The broadest scope of rearrangement applies to every product, but only one has the narrowest. On the Single Product template, this means a file named `single-product-{slug}.html` in your theme's `/templates` folder takes priority over `single-product.html` whenever you view the product. The others continue to fall back to the general template.
+
+This would suit a flagship or seasonal product that needs a layout the rest of your catalog doesn't, while not duplicating the logic in PHP or maintaining a separate page template. Here, you can copy your theme's existing `single-product.html`, rename it to match the product's slug, and adjust:
+
+```text
+your-theme/
+└── templates/
+ ├── single-product.html
+ └── single-product-limited-edition-hoodie.html
+```
+
+WordPress resolves this based on the product being viewed and the same ancestor and compatibility-layer considerations also apply to a per-product override.
+
+## Styling product Blocks with `theme.json`
+
+The [Global styles guide](/docs/theming/block-theme-development/global-styles/) covers the general system for styling WooCommerce Blocks through `theme.json`. However, before you rely on that system, there are some specifics with the product page Blocks to understand.
+
+### Where a style will apply
+
+Several product Blocks declare a top-level `selectors` object in `block.json`. Its `root` property tells WordPress where to apply `theme.json` styles instead of assuming the Block's outer wrapper element.
+
+```jsonc
+// woocommerce/product-image
+"selectors": {
+ "root": ".wp-block-woocommerce-product-image.wc-block-components-product-image, .wp-block-woocommerce-product-image .wc-block-components-product-image"
+}
+
+// woocommerce/product-rating
+"selectors": {
+ "root": ".wp-block-woocommerce-product-rating.wc-block-components-product-rating, .wp-block-woocommerce-product-rating .wc-block-components-product-rating"
+}
+
+// woocommerce/product-price
+"selectors": {
+ "root": ".wp-block-woocommerce-product-price .wc-block-components-product-price"
+}
+```
+
+These selectors can target the wrapper, a nested element, or both. In practice, styling a Block in `theme.json` doesn't always mean styling the wrapper element. If a color or typography change through `styles.blocks.woocommerce/product-price` doesn't appear where you expect it in the rendered markup, check the Block's registered `selectors.root` value to see which element receives the styles.
+
+:::tip
+Remember: a Block's registered selectors aren't visible from the Site Editor. Make sure to check the Block's `block.json` in the WooCommerce source, or inspect the generated global styles `<style>` tag in your browser's DevTools first.
+:::
+
+### Partial support isn't full support
+
+A Block's `supports` object tells you which style categories `theme.json` can touch for the Block, with various support on offer per-Block. For example, `woocommerce/product-button` declares border support for radius only, so it does not expose border color, width, or style through `theme.json`. In contrast, `woocommerce/product-image` supports border color, width, style, and radius.
+
+If a design calls for a colored border around the Product Button, this is a [job for CSS](https://developer.woocommerce.com/docs/theming/block-theme-development/css-styling/) rather than `theme.json`.
+
+This same principle applies to color: `woocommerce/product-rating` supports text color but not background or link color:
+
+```json
+{
+ "styles": {
+ "blocks": {
+ "woocommerce/product-rating": {
+ "color": {
+ "text": "var(--wp--preset--color--accent-1)"
+ }
+ }
+ }
+ }
+}
+```
+
+### Styling Fill and Outline directly
+
+`woocommerce/product-button` registers two **Fill** and **Outline** Block styles. Both are standard Block style variations that `theme.json` can style independently using a scoped `variations` property based on what supported properties you need:
+
+```json
+{
+ "styles": {
+ "blocks": {
+ "woocommerce/product-button": {
+ "color": {
+ "text": "var(--wp--preset--color--base)",
+ "background": "var(--wp--preset--color--contrast)"
+ },
+ "variations": {
+ "outline": {
+ "color": {
+ "text": "var(--wp--preset--color--contrast)"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+Because border color isn't part of this Block's supported style properties, the **Outline** style's border rendering comes from the Block's bundled stylesheet. So, `theme.json` can shift the text and background colors used within each variation, but not the border itself.
+
+## Practical customization examples
+
+There are a few typical tasks for the Single Product template that can stay fully within the Block system rather than reaching for PHP. This lets you edit through the Site Editor rather than tie it to a theme's code.
+
+### Building the Blockified Product Details tabs
+
+This is a good example of a Blockified version showing the compatibility layer's fallback behavior explained earlier:
+
+```html
+<!-- wp:woocommerce/product-details {"align":"wide"} -->
+<div class="wp-block-woocommerce-product-details alignwide">
+ <!-- wp:accordion -->
+ <div class="wp-block-accordion" role="group">
+ <!-- wp:accordion-item -->
+ <div class="wp-block-accordion-item">
+ <!-- wp:accordion-heading {"showIcon":false} -->
+ <h3 class="wp-block-accordion-heading">
+ <button type="button" class="wp-block-accordion-heading__toggle">
+ <span class="wp-block-accordion-heading__toggle-title">Description</span>
+ </button>
+ </h3>
+ <!-- /wp:accordion-heading -->
+ <!-- wp:accordion-panel -->
+ <div class="wp-block-accordion-panel" role="region">
+ <!-- wp:woocommerce/product-description /-->
+ </div>
+ <!-- /wp:accordion-panel -->
+ </div>
+ <!-- /wp:accordion-item -->
+ </div>
+ <!-- /wp:accordion -->
+</div>
+<!-- /wp:woocommerce/product-details -->
+```
+
+Non-default tabs returned through the `woocommerce_product_tabs` filter are automatically injected by WooCommerce. The default Specifications and Reviews tabs aren't, so you'll need to repeat the pattern for `woocommerce/product-specifications` and `woocommerce/product-reviews`.
+
+### Replacing Related Products with a curated pick
+
+The default Related Products pattern queries by shared categories and tags, which isn't always the right fit where you need specific product pairings, such as an accessory that goes only with one product. Swapping in a hand-picked query means changing layout and sizing attributes as well as the query attributes:
+
+```html
+<!-- wp:woocommerce/product-collection {"queryId":0,"query":{"perPage":4,"pages":1,"postType":"product","inherit":false,"isProductCollectionBlock":true,"woocommerceHandPickedProducts":["154","289","310","422"]},"collection":"woocommerce/product-collection/hand-picked","displayLayout":{"type":"flex","columns":4,"shrinkColumns":true},"dimensions":{"widthType":"fill"}} -->
+<div class="wp-block-woocommerce-product-collection">
+ <!-- wp:heading {"textAlign":"center","level":2,"style":{"spacing":{"margin":{"bottom":"1rem"}}}} -->
+ <h2 class="wp-block-heading has-text-align-center">You may also like</h2>
+ <!-- /wp:heading -->
+
+ <!-- wp:woocommerce/product-template -->
+ <!-- wp:woocommerce/product-image {"showSaleBadge":false,"style":{"dimensions":{"aspectRatio":"1/1"}}} -->
+ <div class="wp-block-woocommerce-product-image">
+ <!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
+ </div>
+ <!-- /wp:woocommerce/product-image -->
+
+ <!-- wp:post-title {"textAlign":"center","level":2,"fontSize":"medium","style":{"spacing":{"margin":{"bottom":"0.75rem","top":"0"}},"typography":{"lineHeight":"1.4"}},"isLink":true,"__woocommerceNamespace":"woocommerce/product-title"} /-->
+
+ <!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small"} /-->
+
+ <!-- wp:woocommerce/product-button {"textAlign":"center","fontSize":"small"} /-->
+ <!-- /wp:woocommerce/product-template -->
+</div>
+<!-- /wp:woocommerce/product-collection -->
+```
+
+You can replace this in place of the `woocommerce-blocks/related-products` pattern in your template override, then update the product IDs in `woocommerceHandPickedProducts` if the pairing needs to differ across your catalog.
diff --git a/docs/theming/block-theme-development/shop-and-archive-page-theming.md b/docs/theming/block-theme-development/shop-and-archive-page-theming.md
new file mode 100644
index 00000000000..ff1fba3b301
--- /dev/null
+++ b/docs/theming/block-theme-development/shop-and-archive-page-theming.md
@@ -0,0 +1,408 @@
+---
+post_title: 'Block theme development: Shop and archive page theming'
+sidebar_label: Shop and archive page theming
+sidebar_position: 5
+---
+
+# Shop and archive page theming
+
+The Product Catalog template will be one of the first interactions when browsing a WooCommerce site. This involves browsing a grid built from the Product Collection Block, which WooCommerce reuses across every product listing on the store.
+
+This guide walks through the Product Catalog template to show you its structure, the grid and its layout choices, sorting and filtering, pagination, how a theme customizes an individual category or tag, and more. It builds on the [Template hierarchy reference](/docs/theming/block-theme-development/template-hierarchy/) and [Global styles for WooCommerce](/docs/theming/block-theme-development/global-styles/), so if template fallbacks or `theme.json` styling are unfamiliar concepts, start with those first. Where a Block also appears on the Single Product template, [Product page theming](/docs/theming/block-theme-development/product-page-theming/) covers styling options for it in more depth than this guide repeats.
+
+## Requirements
+
+Before you begin, make sure you have the following in place:
+
+- **A Block theme.** Every Block discussed here renders on the Blockified Product Catalog template, which only Block themes use by default.
+- **Familiarity with the Template hierarchy reference.** You should understand how `archive-product.html` resolves and how the taxonomy templates fall back to it.
+- **Familiarity with Global styles for WooCommerce.** Several Blocks on this template style through `theme.json`.
+- **Familiarity with the Site Editor's List View and Code editor.** Several examples in this guide need you to inspect Block markup directly.
+
+## How the template is put together
+
+WooCommerce's own classic template for this page is `archive-product.php`, so the Block Template shares the same `archive-product.html` name. It resolves through the same template hierarchy as any other page type.
+
+WooCommerce ships a default `archive-product.html` inside its own plugin folder, which a Block theme falls back to unless the theme provides its own copy at the same path:
+
+
+
+```html
+<!-- wp:template-part {"slug":"header"} /-->
+
+<!-- wp:group {"tagName":"main","layout":{"inherit":true,"type":"constrained"}} -->
+<main class="wp-block-group">
+ <!-- wp:woocommerce/breadcrumbs /-->
+ <!-- wp:query-title {"type":"archive","showPrefix":false,"align":"wide"} /-->
+ <!-- wp:term-description {"align":"wide"} /-->
+ <!-- wp:woocommerce/store-notices /-->
+
+ <!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap","justifyContent":"space-between"}} -->
+ <div class="wp-block-group alignwide">
+ <!-- wp:woocommerce/product-results-count /-->
+ <!-- wp:woocommerce/catalog-sorting /-->
+ </div>
+ <!-- /wp:group -->
+
+ <!-- wp:woocommerce/product-collection {"queryId":0,"query":{"inherit":true,"isProductCollectionBlock":true},"displayLayout":{"type":"flex","columns":3,"shrinkColumns":true},"align":"wide"} -->
+ <div class="wp-block-woocommerce-product-collection alignwide">
+ <!-- wp:woocommerce/product-template -->
+ <!-- wp:woocommerce/product-image {"showSaleBadge":false,"imageSizing":"thumbnail"} -->
+ <!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
+ <!-- /wp:woocommerce/product-image -->
+ <!-- wp:post-title {"textAlign":"center","level":2,"isLink":true,"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-title","style":{"typography":{"lineHeight":"1.4"}}} /-->
+ <!-- wp:woocommerce/product-price {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
+ <!-- wp:woocommerce/product-button {"textAlign":"center","fontSize":"small","style":{"spacing":{"margin":{"bottom":"1rem"}}}} /-->
+ <!-- /wp:woocommerce/product-template -->
+
+ <!-- wp:query-pagination {"layout":{"type":"flex","justifyContent":"center"}} -->
+ <!-- wp:query-pagination-previous /-->
+ <!-- wp:query-pagination-numbers /-->
+ <!-- wp:query-pagination-next /-->
+ <!-- /wp:query-pagination -->
+
+ <!-- wp:woocommerce/product-collection-no-results -->
+ <!-- wp:group {"layout":{"type":"flex","orientation":"vertical","justifyContent":"center","flexWrap":"wrap"}} -->
+ <div class="wp-block-group">
+ <!-- wp:pattern {"slug":"woocommerce/no-products-found-clear-filters"} /-->
+ </div>
+ <!-- /wp:group -->
+ <!-- /wp:woocommerce/product-collection-no-results -->
+ </div>
+ <!-- /wp:woocommerce/product-collection -->
+</main>
+<!-- /wp:group -->
+
+<!-- wp:template-part {"slug":"footer"} /-->
+```
+
+Both `core/query-title` and `core/term-description` are core WordPress Blocks. WooCommerce configures both for the product context, but styling and behavior for either follows core WordPress conventions rather than anything WooCommerce-specific.
+
+As the Template hierarchy reference covers, `archive-product.html` is the fallback for every taxonomy archive: Products by Category, Products by Tag, Products by Attribute, and Products by Brand. However, two cases are worth calling out.
+
+First, **Products by Attribute** uses `archive-product.html` by default. A theme template named `taxonomy-product_attribute.html`, or a customized database template with that slug, takes priority when present. To give attribute archives a separate design, add `taxonomy-product_attribute.html` to your theme.
+
+Also, **Product Search Results** (`product-search-results.html`) swaps the query title's `type` to `search` and renders two separate patterns in its no-results state: `woocommerce/no-products-found` and `woocommerce/product-search-form`. Typically, a shopper landing here has already tried to find something specific, so the separate search form is a nice UX addition.
+
+## The product grid: Blocks and layout choices
+
+`woocommerce/product-collection` is the Block behind every product grid on the storefront. On a taxonomy or search template, the `query.inherit` attribute set to `true` tells the Block to read its product set from the current request rather than a fixed list. This is what makes one Block definition work correctly regardless of what a shopper is viewing.
+
+
+
+The Block offers three layout options in its sidebar settings:
+
+- **Grid** renders a configurable number of columns, with a Responsive toggle that lets columns shrink on narrower viewports. This is the default for the Product Catalog template and every taxonomy archive that falls back to it.
+- **Stack** renders products in a single vertical list rather than a grid, which is the closest thing this Block has to a dedicated list view.
+- **Carousel** renders a horizontal slider, which is better suited to a featured strip on a landing page than a full catalog listing.
+
+Inside the Block, `woocommerce/product-template` wraps repeating markup for each product. It accepts color and typography through `theme.json`, even though the Product Collection Block itself doesn't. As such, it's a reasonable place to set shared styles across every card without repeating the rule on each child Block.
+
+### Choosing to style in `theme.json`
+
+The default catalog card sticks to only a few Blocks:
+
+- **Product Image** renders the featured image and (for this template) links through to the single product.
+- **Product Sale Badge**, nested in Product Image, displays once a product has a sale price.
+- **Product Title**, rendered through `core/post-title` in this context rather than a dedicated WooCommerce Block, links to the same product page.
+- **Product Price** shows both the current price and any sale price.
+- **Add to Cart Button** adds a product to the cart or links to the product page. Simple products add-to-cart from the card. Other product types send the visitor to the product page.
+
+:::tip
+Product page theming covers these Blocks in more depth, since they also appear in the Single Product template's summary area. This includes registered selectors and any supported style properties.
+:::
+
+The Product Collection Block does not register spacing support, and the gap between product cards comes from the inner `.wc-block-product-template` grid. To change that gap, use a Block stylesheet rather than `theme.json`:
+
+```css
+.wc-block-product-template.is-flex-container,
+.wc-block-product-template__responsive {
+ gap: 2rem;
+}
+```
+
+A shared text color or font across every card still styles through `theme.json` like any other Block:
+
+```json
+{
+ "styles": {
+ "blocks": {
+ "woocommerce/product-template": {
+ "typography": {
+ "fontFamily": "var(--wp--preset--font-family--body)"
+ }
+ }
+ }
+ }
+}
+```
+
+You can extend this to any of the other Blocks through targeting a registered Block name instead.
+
+## Sorting and filtering the catalog
+
+Your main concerns will be how many results a shopper is looking at and which order those results appear in. The subset of the catalog they're seeing is also important.
+
+WooCommerce splits these across three Blocks, two of which ship in the default template and one you add yourself. However, all can update the grid without a full page reload.
+
+### Counting and sorting results
+
+`woocommerce/product-results-count` and `woocommerce/catalog-sorting` each render through a classic WooCommerce function server-side, then layer Interactivity API behavior on top.
+
+**Product Results Count**'s render callback calls `woocommerce_result_count()`, which loads the overridable `loop/result-count.php` template. Copying this file into a theme's `woocommerce/loop/` directory still changes the text this Block displays, exactly as it would on a classic theme.
+
+The count itself follows the product grid settings (the `woocommerce_catalog_columns` and `woocommerce_catalog_rows` options). Because Block themes don't have a Customizer, you can use the `loop_shop_per_page` filter as an override.
+
+**Catalog Sorting**'s render callback calls `woocommerce_catalog_ordering()`, which builds the drop-down menu from the `woocommerce_catalog_orderby` filter and reads its default selection from the `woocommerce_default_catalog_orderby` filter or option. The base option set contains `menu_order`, `popularity`, `rating`, `date`, `price`, and `price-desc`. Search pages add `relevance` and remove `menu_order`. WooCommerce also removes `menu_order` on other catalog pages when it isn't the configured default, and removes `rating` on every page when product ratings are disabled.
+
+A plugin that already customizes the sorting drop-down menu on a classic theme needs no changes to keep working:
+
+```php
+add_filter( 'woocommerce_catalog_orderby', function( $options ) {
+ unset( $options['rating'] );
+ return $options;
+} );
+```
+
+However, the Blockified template will differ depending on what happens after you generate the PHP output. WooCommerce runs the rendered form through `WP_HTML_Tag_Processor` to add `data-wp-interactive`, `data-wp-on--submit`, and `data-wp-on--change` attributes. The form connects to the Interactivity API, so a sorting change updates the grid instead of submitting the form and reloading the page.
+
+The Sorting Block also exposes a `useLabel` attribute that toggles the **Sort by** text alongside the drop-down menu. Both Blocks support text color and font size through `theme.json`, as per the Global styles guide.
+
+Neither Block depends on the other for placement. The default template puts them in a flex Group sitting on the same row, but you can separate them, hide one, or move both elsewhere in the layout without affecting how either one queries or renders its data.
+
+### Adding the Product Filters Block
+
+:::note
+Several child filter Blocks save as an empty wrapper div and populate through the Interactivity API once the page loads. See WordPress' [Interactivity API reference guide](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/) or the WooCommerce post on [rendering interactive Blocks](https://developer.woocommerce.com/2025/10/30/understanding-the-interactivity-api-driven-future-for-woocommerce-blocks/) for more details.
+:::
+
+The Product Filters Block is not part of the default catalog template. On an inherited archive query, it can sit beside the Product Collection Block because WooCommerce connects it to the active catalog query. For a custom or non-inherited collection, nest Product Filters inside Product Collection so it receives that collection's query context.
+
+```html
+<!-- wp:woocommerce/product-filters -->
+<div class="wp-block-woocommerce-product-filters">
+ <!-- wp:woocommerce/product-filter-price -->
+ <div class="wp-block-woocommerce-product-filter-price">
+ <!-- wp:heading {"level":3} --><h3 class="wp-block-heading">Price</h3><!-- /wp:heading -->
+ <!-- wp:woocommerce/product-filter-price-slider -->
+ <div class="wp-block-woocommerce-product-filter-price-slider"></div>
+ <!-- /wp:woocommerce/product-filter-price-slider -->
+ </div>
+ <!-- /wp:woocommerce/product-filter-price -->
+
+ <!-- wp:woocommerce/product-filter-attribute {"attributeId":1} -->
+ …
+ <!-- /wp:woocommerce/product-filter-attribute -->
+
+ <!-- wp:woocommerce/product-filter-active -->
+ …
+ <!-- /wp:woocommerce/product-filter-active -->
+</div>
+<!-- /wp:woocommerce/product-filters -->
+```
+
+Each filter type is its own child Block, so you only add the ones a design calls for:
+
+| Filter Block | Filters by |
+| :---- | :---- |
+| Price Filter | A minimum and maximum price, shown as a slider with optional input fields. |
+| Attribute Filter | A single chosen attribute, such as color or size, as a list or as chips. |
+| Rating Filter | Customer rating. Only appears once at least one product carries a rating. |
+| Availability Filter | Stock status: in stock, out of stock, or on backorder. |
+| Taxonomy Filter | A taxonomy you choose when adding the Block: category, tag, or brand. |
+| Active Filters | Every filter currently applied, as removable chips. |
+
+Every filter type shares one Block `ancestor` restriction: each expects to sit inside `woocommerce/product-filters`, which is why the inserter won't offer the Price Filter Block as a top-level addition to an arbitrary template. This mirrors the `ancestor` restrictions for general product page theming.
+
+### Configuring the behavior of filters
+
+The Product Filters Block's **Overlay** setting controls whether filters remain on the page or open in an overlay. Its options are **Off**, **Mobile**, and **Always**, stored in the `overlayMode` attribute. When an overlay is enabled, **Overlay position** sets `overlayPosition` to the left or right. **Reload full page** is found in the Product Collection Block's Advanced settings and sets an attribute called `forcePageReload`, which is disabled by default.
+
+When `forcePageReload` is `false`, WooCommerce gives the Product Collection Block its own `data-wp-router-region` attribute, and Product Filters renders a separate router region. Both can use the WordPress Interactivity API's client-side navigation. Setting `forcePageReload` to `true` disables enhanced pagination and tells nested Product Filters to reload through Block context. For an inherited archive query where Product Filters is a sibling, WooCommerce provides the same value through `wp_interactivity_config()`. Reach for it if another Block or customization inside the collection depends on a traditional full-page reload.
+
+Product Filters declares the following context keys. When nested in a custom Product Collection, it receives the collection's query and `forcePageReload` setting through Block context. When it's a sibling of an inherited archive collection, it relies on the archive query and uses the global configuration described above for `forcePageReload`:
+
+```json
+"usesContext": [
+ "postId",
+ "query",
+ "queryId",
+ "collection",
+ "productCollectionLocation",
+ "forcePageReload"
+]
+```
+
+This also checks for an `isProductCollectionBlock` attribute that's nested inside `query`. The template's own Product Collection Block already includes this, but any product collection you create will need this to ensure you don't have to reload the page to display front-end changes.
+
+:::note
+The Attribute Filter, Price Filter, and Active Filters Blocks existed before Product Filters, as `woocommerce/attribute-filter`, `woocommerce/price-filter`, and `woocommerce/active-filters`. Those predate the Product Collection Block, so they don't appear in the Blockified Product Catalog template.
+:::
+
+## Pagination
+
+Pagination renders through the core Query Pagination Block as Product Collection registers itself as a compatible query source for it. Product Collection and Product Filters render separate router regions, although both can use client-side navigation. The `forcePageReload` attribute disables enhanced pagination for the collection and tells the associated filters to fall back to a full-page reload through the context or configuration described above.
+
+With the default setting, moving between pages updates the grid through client-side navigation, then moves focus to the first product link in the updated collection. This focus change can also move the shopper's scroll position. Enabling **Reload full page** falls back to standard link navigation for the pagination controls instead.
+
+The Query Pagination Block only appears once a collection spans more than one page, so testing pagination changes against a catalog with enough products matters more here than anywhere else on the template. A collection with fewer products than its Products per page setting renders no pagination Blocks. This can make a pagination override look broken in a near-empty development environment when it isn't.
+
+The following child Blocks build the default pagination control:
+
+- **Pagination Previous** links to the prior page and is hidden on the first page.
+- **Pagination Numbers** lists individual page numbers. The `midSize` attribute sets how many links show on either side of the current page before it displays ellipses.
+- **Pagination Next** links to the following page and hides on the last page similar to Pagination Previous.
+
+Each accepts its own label text and styles through the same core Block settings you'd use for a blog's post list, rather than anything WooCommerce-specific.
+
+If a shopper sees no matches on the page (such as a category with no products or a search with no hits), the template uses `woocommerce/product-collection-no-results`. The default catalog template points this at the `no-products-found-clear-filters` pattern.
+
+## Customizing category, tag, and brand archives
+
+Every taxonomy has a term-specific file that takes priority over the taxonomy's general file, which in turn falls back to `archive-product.html`. This applies identically to brands:
+
+```text
+your-theme/
+└── templates/
+ ├── archive-product.html
+ ├── taxonomy-product_cat-outerwear.html
+ └── taxonomy-product_brand-acme.html
+```
+
+This might suit a Stack instead of a Grid (where you need a different layout) or a featured banner above the results, without touching the design every other archive uses.
+
+Here, you can copy the general taxonomy template (or `archive-product.html` if the taxonomy has no dedicated file of its own) and rename it to match the term's slug. WordPress resolves the correct file based on the term being viewed.
+
+### What a term-level override can break
+
+WooCommerce's classic template fires a sequence of hooks around the product loop. These are preserved on the Blockified template through an internal compatibility layer that anchors each hook to a specific Block:
+
+| Block | Position | Hook |
+| :---: | :---: | :---: |
+| Product Collection | Before | `woocommerce_before_main_content` |
+| | After | `woocommerce_after_main_content` |
+| Product Template | Before | `woocommerce_before_shop_loop` |
+| | After | `woocommerce_after_shop_loop` |
+| Post Title | Before | `woocommerce_before_shop_loop_item_title` |
+| | After | `woocommerce_shop_loop_item_title`, `woocommerce_after_shop_loop_item_title` |
+| Term Description | Before | `woocommerce_archive_description` |
+| Query No Results | Before | `woocommerce_no_products_found` |
+| Each product item | Before | `woocommerce_before_shop_loop_item` |
+| | After | `woocommerce_after_shop_loop_item` |
+
+`woocommerce_before_shop_loop_item` and `woocommerce_after_shop_loop_item` anchor to each product's internal item context. It means they won't break if you remove a Block.
+
+For example, an extension that still adds content through `woocommerce_archive_description` is anchoring itself to the Term Description Block. A term-level override that drops a Block removes the anchor too, which can leave an extension's content with nowhere to render on that one archive even though it still appears everywhere else.
+
+:::tip
+Before removing the Product Template, Post Title, Term Description, or Query No Results Blocks from a term-level override, test the change against your current extensions. A missing hook anchor isn't always a visual change you'd catch by looking at the canvas.
+:::
+
+## Common customization examples
+
+The Blocks and settings covered so far combine into a handful of layouts that come up often. Each of the following can go directly into a template override and uses the concepts taught in this guide.
+
+### A single row for a category landing page
+
+
+
+A category or brand override sometimes needs a short, curated row rather than a full paginated catalog. Some attributes on the Product Collection Block can produce this without needing a second, differently-configured Block:
+
+```html
+<!-- wp:woocommerce/product-collection {
+ "query": {
+ "perPage": 4,
+ "pages": 1,
+ "inherit": false,
+ "taxQuery": {
+ "product_cat": [19]
+ },
+ "isProductCollectionBlock": true
+ },
+ "displayLayout": {
+ "type": "flex",
+ "columns": 4
+ },
+ "collection": "woocommerce/product-collection/by-category"
+} -->
+
+<div class="wp-block-woocommerce-product-collection">
+
+<!-- wp:woocommerce/product-template -->
+<!-- wp:woocommerce/product-image -->
+<!-- wp:woocommerce/product-sale-badge /-->
+<!-- /wp:woocommerce/product-image -->
+
+<!-- wp:post-title {"isLink":true,"__woocommerceNamespace":"woocommerce/product-title"} /-->
+
+<!-- wp:woocommerce/product-price /-->
+
+<!-- wp:woocommerce/product-button /-->
+<!-- /wp:woocommerce/product-template -->
+</div>
+
+<!-- /wp:woocommerce/product-collection -->
+```
+
+Here, `query.perPage` sets how many products load, while `query.pages` only displays a single page of results.
+
+To restrict the products in the category from displaying, you use the following interdependent attributes:
+
+- `taxQuery` filters by the category, for which you'll need the term ID (found as the `tag_ID` within the edit screen URL).
+- `inherit: false` unlinks the query from the typical display template.
+- `collection` identifies this as a built-in recipe rather than a custom query.
+
+Finally, `isProductCollectionBlock` ties the Block to the same pagination behavior and router region described earlier. This means it typically has to nest in `query` to have any effect.
+
+### Filters beside the grid instead of above it
+
+
+
+The default template puts sorting above the grid, but a sidebar layout is just as valid and doesn't need any new Blocks.
+
+For an inherited catalog archive, place Product Filters and Product Collection in separate columns as siblings. The collection's `query` defines the inheritance, while `displayLayout` sets the grid's column count:
+
+```html
+<!-- wp:columns {"align":"wide"} -->
+<div class="wp-block-columns alignwide">
+ <!-- wp:column {"width":"25%"} -->
+ <div class="wp-block-column" style="flex-basis:25%">
+ <!-- wp:woocommerce/product-filters -->…<!-- /wp:woocommerce/product-filters -->
+ </div>
+ <!-- /wp:column -->
+
+ <!-- wp:column {"width":"75%"} -->
+ <div class="wp-block-column" style="flex-basis:75%">
+ <!-- wp:woocommerce/product-collection {"queryId":0,"query":{"inherit":true,"isProductCollectionBlock":true},"displayLayout":{"type":"flex","columns":2}} -->…<!-- /wp:woocommerce/product-collection -->
+ </div>
+ <!-- /wp:column -->
+</div>
+<!-- /wp:columns -->
+```
+
+For a custom or non-inherited Product Collection, keep Product Filters inside that collection instead so the filter receives the correct query context.
+
+### Swapping the default catalog query for a curated collection
+
+
+
+Product Collection's Custom query type replaces its default template-driven behavior with a fixed set of results. As an example, you could use this for a landing page built around a single promotion:
+
+```html
+<!-- wp:woocommerce/product-collection {"query":{"inherit":false,"woocommerceOnSale":true,"perPage":8,"isProductCollectionBlock":true},"displayLayout":{"type":"flex","columns":4}} -->
+<div class="wp-block-woocommerce-product-collection alignwide">
+ <!-- wp:heading -->
+ <h2 class="wp-block-heading">On sale now</h2>
+ <!-- /wp:heading -->
+ <!-- wp:woocommerce/product-template -->
+ <!-- wp:woocommerce/product-image /-->
+ <!-- wp:post-title {"level":3,"isLink":true,"__woocommerceNamespace":"woocommerce/product-title"} /-->
+ <!-- wp:woocommerce/product-price /-->
+ <!-- /wp:woocommerce/product-template -->
+</div>
+<!-- /wp:woocommerce/product-collection -->
+```
+
+Setting `query.inherit` to `false` breaks the connection to the template or page the Block is on. Without it, you wouldn't see the fixed on-sale set of products.
+
+`woocommerceOnSale` is one of several filter attributes the Custom query type exposes. However, unlike the example of a single row of category-based products, you don't need a matching `collection` value.
diff --git a/docs/theming/block-theme-development/template-hierarchy.md b/docs/theming/block-theme-development/template-hierarchy.md
new file mode 100644
index 00000000000..fd7ffc8a99d
--- /dev/null
+++ b/docs/theming/block-theme-development/template-hierarchy.md
@@ -0,0 +1,239 @@
+---
+post_title: 'Block theme development: Template hierarchy reference'
+sidebar_label: Template hierarchy
+sidebar_position: 2
+---
+
+# Template hierarchy reference
+
+Every WooCommerce store page renders from a template. In a Block theme, WooCommerce registers a full set of these by default, built from HTML Block templates rather than PHP. As such, a store works immediately after activation without a theme providing anything.
+
+This reference maps every template and template part WooCommerce registers, when each one loads, and how a theme overrides or extends them. It builds directly on the other concepts in the [Block theme development](/docs/theming/block-theme-development/theming-woo-blocks/) section, so if any of the terms here feel unfamiliar, start there first.
+
+## Requirements
+
+Before you begin, make sure you have the following in place:
+
+- **A Block theme.** The template registrations and fallback behavior covered here apply to Block themes. Classic themes use a separate, PHP-based template system, covered in the [classic theme developer handbook](https://developer.woocommerce.com/docs/theming/theme-development/classic-theme-developer-handbook/).
+- **Familiarity with the Site Editor.** You should know how to open templates and view Block markup through the **Code editor**.
+- **Familiarity with WordPress's Block template hierarchy.** This guide assumes you already know how WordPress resolves a template file to a request; it focuses on what WooCommerce adds on top of that system.
+
+## The three layers behind every WooCommerce template
+
+WooCommerce uses three related template systems, but not every store page has a distinct file in all three. It's a common source of confusion if you don't understand how they work together.
+
+First, **Classic PHP templates** live under `plugins/woocommerce/templates/` as `.php` files and predate Block themes entirely. A classic theme overrides one by copying it into a `woocommerce/` directory within the theme.
+
+Some of these files exist purely to redirect to another. For example, `taxonomy-product-cat.php` is a single line:
+
+```php
+<?php
+wc_get_template( 'archive-product.php' );
+```
+
+If you go looking for a taxonomy template and find one of these, you've found the classic theme system. The two are unrelated and the classic template structure is [documented separately](https://developer.woocommerce.com/docs/theming/theme-development/template-structure/).
+
+For Block themes, WooCommerce keeps two sets of HTML templates. The **classic-compatibility set** lives in `plugins/woocommerce/templates/templates/`. Its product catalog, product search, product attribute, and single product templates render the corresponding classic PHP template through a single `woocommerce/legacy-template` Block:
+
+```html
+<!-- wp:woocommerce/legacy-template {"template":"single-product"} /-->
+```
+
+The Cart, Checkout, Order Confirmation, and Coming Soon templates in this set already use Block markup instead of the legacy wrapper.
+
+A **Blockified set** lives in `plugins/woocommerce/templates/templates/blockified/`. Its product-facing templates use individual WooCommerce and core WordPress Blocks, such as Product Image Gallery and Product Price, rather than the legacy wrapper.
+
+WooCommerce only registers its standard Block templates when `wp_is_block_theme()` returns true. Blockified templates are the default for Block themes unless the store's `wc_blocks_use_blockified_product_grid_block_as_template` option explicitly retains the compatibility set. Every template in this documentation refers to the Blockified set unless stated otherwise.
+
+:::tip
+If a template looks different to what you expect, open it through the Site Editor's **Code editor** view. A single `woocommerce/legacy-template` Block at the top of the file means you're looking at the classic compatibility version, not the Blockified one.
+:::
+
+## The core store templates
+
+WooCommerce registers 11 templates by default to cover every stage of the shopping experience.
+
+| Template | Slug | Notes |
+| :---- | :---- | :---- |
+| Single Product | `single-product` | Renders an individual product page. |
+| Product Catalog | `archive-product` | Renders the main shop page. Acts as the fallback for the four taxonomy templates below. |
+| Products by Category | `taxonomy-product_cat` | No dedicated file; falls back to Product Catalog. |
+| Products by Tag | `taxonomy-product_tag` | No dedicated file; falls back to Product Catalog. |
+| Products by Attribute | `taxonomy-product_attribute` | WooCommerce ships a generic file, but Product Catalog remains the default fallback until a theme or merchant customizes the attribute template. |
+| Products by Brand | `taxonomy-product_brand` | No dedicated file; falls back to Product Catalog. Brands were enabled by default starting in WooCommerce 9.6. |
+| Product Search Results | `product-search-results` | Renders search results scoped to products. |
+| Page: Coming soon | `coming-soon` | Registered regardless of store visibility; Launch Your Store determines when shoppers see it. |
+| Page: Cart | `page-cart` | Wraps the page a merchant assigns as the Cart page. |
+| Page: Checkout | `page-checkout` | Wraps the page a merchant assigns as the Checkout page. |
+| Order Confirmation | `order-confirmation` | Renders after a successful purchase. |
+
+Eight of these 11 exist as files you can open and copy into your theme's `/templates` folder to override:
+
+- Single Product
+- Product Catalog
+- Products by Attribute
+- Product Search Results
+- Coming soon
+- Cart
+- Checkout
+- Order Confirmation
+
+Products by Category, Products by Tag, and Products by Brand do not need dedicated files. WordPress recognizes those requests as taxonomy archives, and WooCommerce inserts Product Catalog as their fallback. The fallback is set directly in each template's registration class:
+
+```php
+class ProductBrandTemplate extends AbstractTemplateWithFallback {
+ const SLUG = 'taxonomy-product_brand';
+ public string $fallback_template = ProductCatalogTemplate::SLUG;
+ public bool $is_taxonomy_template = true;
+ // ...
+}
+```
+
+Products by Attribute is a special case. WooCommerce ships `taxonomy-product_attribute.html`, but its template class deliberately uses Product Catalog unless the active theme provides the attribute template or a merchant has customized it in the database. This prevents the plugin's generic attribute file from taking priority over a customized Product Catalog template.
+
+As a result, a theme overriding `archive-product.html` gets the design applied automatically to all four taxonomy archives until the theme or a merchant creates a more specific template.
+
+**Products by Brand** became part of WooCommerce core in 9.4 and was enabled by default starting in 9.6. Older stores can therefore differ depending on their WooCommerce version and whether the Brands feature was enabled.
+
+:::info
+The **Coming soon** template's file is `coming-soon.html`, not `page-coming-soon.html`. The "Page:" wording belongs to the template's title in the Site Editor, not its filename. WooCommerce registers the template even when Launch Your Store is inactive; store visibility determines when the template is used.
+:::
+
+## Cart, checkout, and order confirmation
+
+The **Cart** and **Checkout** templates behave differently from the browsing templates. They wrap the content a merchant assigns through the **Pages** screen rather than content WooCommerce queries directly.
+
+Both `page-cart.html` and `page-checkout.html` have a similar structure. The Cart template's main content is:
+
+```html
+<!-- wp:woocommerce/page-content-wrapper {"page":"cart"} -->
+<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
+<main class="wp-block-group">
+ <!-- wp:woocommerce/store-notices /-->
+ <!-- wp:post-title {"align":"wide","level":1} /-->
+ <!-- wp:post-content {"align":"wide"} /-->
+</main>
+<!-- /wp:group -->
+<!-- /wp:woocommerce/page-content-wrapper -->
+```
+
+The Checkout template uses the same wrapper with `{"page":"checkout"}`, but it omits the Post Title Block. In both templates, `core/post-content` renders the Cart or Checkout Block assigned to that page.
+
+:::info
+If you override either template, keep the Cart or Checkout Block inside the content rendered by `core/post-content` rather than hardcoding `woocommerce/cart` or `woocommerce/checkout` directly into the template. Bypassing the assigned page content this way desyncs the page editor from the frontend. Some WooCommerce functionality that checks for the presence of those Blocks on the assigned page could break.
+:::
+
+The **Order Confirmation** template does not wrap an assigned page and has no placeholder page. Its dedicated template renders data from the order-received endpoint through Order Confirmation Blocks for the status, summary, totals, downloads, addresses, additional fields, and additional information.
+
+Styling the Cart and Checkout Blocks (including the mobile checkout button and associated CSS containers) is covered in [Cart and Checkout blocks theming](https://developer.woocommerce.com/docs/theming/block-theme-development/cart-and-checkout/).
+
+## Template parts
+
+Template parts live in a theme's `/parts` folder, separate from full templates in `/templates`. WooCommerce registers several of these smaller, reusable sections that a template pulls into a Template Part Block rather than owning the markup directly.
+
+A template part only renders once something includes it, so registering one changes nothing on its own until a template references it:
+
+```html
+<!-- wp:template-part {"slug":"mini-cart","theme":"your-theme"} /-->
+```
+
+WooCommerce supplies seven template-part files:
+
+- **Mini-Cart** (`mini-cart`) supplies the contents of the Mini-Cart drawer, the panel that opens without navigating away from the current page when a shopper adds an item to cart.
+- **Checkout header** (`checkout-header`) supplies the header used specifically on the Checkout template (separate from the site's main header), so a merchant can strip distracting navigation away from the payment step.
+- **Coming soon social links** (`coming-soon-social-links`) supplies the reusable social-links section included by WooCommerce's Coming Soon patterns.
+- **Simple, External, Variable, and Grouped product `add-to-cart-with-options`** parts handle the add-to-cart form on the product page, one per product type. Their registry entries depend on product types registered with WooCommerce, not on whether the catalog currently contains products of each type. WooCommerce's four core product types normally make all four parts available.
+
+The four `add-to-cart-with-options` parts sit inside its own template part area, separate from the standard header and footer areas, and back the Add to Cart and Options Block. This lets a theme or merchant give each product type its own layout, such as a stepper for a simple product's quantity field, instead of one form that adapts its own contents internally.
+
+:::info
+The Add to Cart and Options Block was introduced in WooCommerce 10.0 and is still in Beta at the time of writing. As such, treat the underlying template parts as subject to change.
+:::
+
+A theme overrides any template part similarly to a template: by adding a matching file to its own `/parts` folder. WordPress also recognizes the older `/block-template-parts` folder name for backward compatibility, but new themes should use `/parts`.
+
+## Overriding templates and template parts
+
+WooCommerce's default templates are built to work for any store, regardless of catalog size, product mix, or industry. These generalities mean the defaults need tweaking often: a theme built around a particular grid, typography scale, or Block arrangement usually wants its own version of at least the Single Product and Product Catalog templates, even if it never touches the rest.
+
+Overriding a template lets you change the Block composition and layout while keeping WooCommerce's underlying logic (such as pricing, availability, and the 'add-to-cart' flow) intact.
+
+You override a WooCommerce template or template part by placing a file with the matching name in its own `/templates` or `/parts` folder:
+
+```text
+your-theme/
+├── templates/
+│ ├── single-product.html
+│ ├── archive-product.html
+│ └── page-cart.html
+└── parts/
+ └── mini-cart.html
+```
+
+There's no registration step required beyond placing the file. A merchant's database customization takes priority, followed by the active theme's file and then WooCommerce's default.
+
+Beyond a plain override, WordPress's template hierarchy supports two narrower levels of targeting:
+
+- **Product-specific templates.** `single-product-{slug}.html` (where `{slug}` is a specific product's slug) takes priority over the general `single-product.html` template whenever the product is viewed. This suits a flagship product or seasonal item that needs a different layout from the rest of the catalog.
+- **Term-specific templates.** `taxonomy-product_cat-{term}.html` renders only when a shopper views the specific category, falling back to `taxonomy-product_cat.html` if it exists, then to `archive-product.html` if neither more specific file does.
+
+The latter gives a theme three levels of specificity for any taxonomy-driven page without needing conditional logic to detect the category being displayed. WordPress resolves the correct file on its own, based on the request.
+
+### Visualizing the fallback chain
+
+Seeing both fallback chains side-by-side makes the specificity easier to follow than reading them as separate bullet points:
+
+| Product request | Taxonomy request |
+| :---- | :---- |
+| `single-product-{slug}.html` | `taxonomy-{taxonomy}-{term}.html` |
+| `single-product.html` | `taxonomy-{taxonomy}.html` |
+| WooCommerce's Single Product template | `archive-product.html` |
+| WordPress's broader singular fallbacks | WordPress's broader taxonomy and archive fallbacks |
+
+In a nutshell, build overrides with this fallback chain in mind rather than against it. A theme that wants one consistent design across every taxonomy archive only needs to touch `archive-product.html`; a theme that wants Products by Category to diverge from Products by Tag only needs to add the one file that should differ. The rest can inherit the parent.
+
+:::info
+Merchants can customize any of these templates from the Site Editor regardless of what a theme provides. These customizations save to the database rather than a file and take priority over both the theme's file and WooCommerce's default. As such, document any custom template you ship with enough context so that merchants understand what it's built around.
+:::
+
+## Checking your work
+
+Which template loads and the fallback that's applied depends on the WooCommerce version and theme in use. To confirm this rather than assume once you've built an override, you can use the `wp block template` commands.
+
+These are bundled with WP-CLI 3.0.0 by default and let you carry out checks without opening the Site Editor.
+
+:::note
+At the time this documentation was written, WP-CLI 3.0.0 was still in prerelease. Until the stable release is available, use a nightly build with `wp cli update --nightly` or install the command separately.
+:::
+
+```bash
+# List every template WooCommerce or another plugin registers
+# independent of theme overrides
+wp block template list --source=plugin
+
+# List the templates your theme is currently overriding
+wp block template list --source=theme
+
+# List registered template parts
+wp block template list --type=wp_template_part
+
+# Dump the Block markup for a given template to confirm
+# whether it's the Blockified version or the legacy-template wrapper
+wp block template get your-theme//single-product --field=content
+```
+
+`wp block template list --source=plugin` is the most direct way to inspect which templates a plugin registers at runtime. Registration is not the same as active selection: for example, Coming Soon remains registered even when Launch Your Store is inactive.
+
+A missing entry can reflect an older WooCommerce version, disabled functionality, the active theme type, or a source filter. Filtering by `--slug` narrows the list to one template at a time, which is faster than scrolling the full output while debugging a single override.
+
+### Filesystem and Site Editor checking
+
+Checking a theme's own file system is the simplest confirmation of all: `ls your-theme/templates` and `ls your-theme/parts` tell you exactly which overrides exist without loading WordPress.
+
+If you don't have shell access to the environment, the Site Editor covers the same ground:
+
+- **Open the template through Appearance > Editor > Templates**, select the template, and use the **Code editor** option to view Block markup. A single `woocommerce/legacy-template` Block at the top means you're looking at the classic-compatibility version rather than the Blockified one.
+- **Check the full Templates list** to see which core templates and parts are registered for the active theme. A missing Products by Brand entry can indicate an older WooCommerce version or a store where the Brands feature is unavailable.
+- **Look for a Customized label** next to a template's name in the list. It means a merchant has edited the template directly from the Site Editor and that saved version in the database is what's rendering rather than your theme's file or WooCommerce's default.
+
+These checks ultimately give you a reliable path from a bare Block theme to a fully customized WooCommerce store: knowing the relationship between template rendering for a given page and understanding its fallback chain, then overriding it.
diff --git a/docs/theming/block-theme-development/theming-woo-blocks.md b/docs/theming/block-theme-development/theming-woo-blocks.md
index cfd5b515fff..ea02b835e31 100644
--- a/docs/theming/block-theme-development/theming-woo-blocks.md
+++ b/docs/theming/block-theme-development/theming-woo-blocks.md
@@ -1,18 +1,14 @@
---
-post_title: Theming for Woo blocks
-sidebar_label: Theming for Woo blocks
-
+post_title: WooCommerce block theme development
---
-# Theming for Woo blocks
-
----
+# WooCommerce block theme development
-**Note:** We're assuming you have some previous knowledge about block theme development and some WordPress concepts. If you are completely new to block theme development, please check [Develop Your First Low-Code Block Theme](https://learn.wordpress.org/course/develop-your-first-low-code-block-theme/)
-to learn about block theme development, and explore
-the [Create Block Theme plugin](https://wordpress.org/plugins/create-block-theme/) tool when you're ready to create a new theme.
+:::info
+We're assuming you have some previous knowledge about block theme development and some WordPress concepts. If you are completely new to block theme development, please check [Develop Your First Low-Code Block Theme](https://learn.wordpress.org/course/develop-your-first-low-code-block-theme/) to learn about block theme development, and explore the [Create Block Theme plugin](https://wordpress.org/plugins/create-block-theme/) tool when you're ready to create a new theme.
----
+For a WooCommerce-specific starting point, [Purple](https://wordpress.org/themes/purple/) is a starter Block theme available in the WordPress.org theme directory. You can also [follow Purple's development in the WooCommerce monorepo](https://github.com/woocommerce/woocommerce/tree/trunk/themes/purple).
+:::
## General concepts
@@ -25,16 +21,19 @@ WooCommerce comes with several [block templates](https://github.com/woocommerce/
- Products by Category (`taxonomy-product_cat.html`)
- Products by Tag (`taxonomy-product_tag.html`)
- Products by Attribute (`taxonomy-product_attribute.html`)
+ - Products by Brand (`taxonomy-product_brand.html`)
- Product Search Results (`product-search-results.html`)
-- Page: Coming soon (`page-coming-soon.html`)
+- Page: Coming soon (`coming-soon.html`)
- Page: Cart (`page-cart.html`)
- Page: Checkout (`page-checkout.html`)
- Order Confirmation (`order-confirmation.html`)
+See the [WooCommerce block template hierarchy](/docs/theming/block-theme-development/template-hierarchy/) for the complete template list, precedence rules, and fallback chains.
+
Block themes can customize those templates in the following ways:
- It's possible to override the templates by creating a file with the same file name under the `/templates` folder. For example, if a block theme contains a `wp-content/themes/yourtheme/templates/single-product.html` template, it will take priority over the WooCommerce default Single Product template.
-- Products by Category, Products by Tag and Products by Attribute templates fall back to the Product Catalog template. In other words, if a theme provides an `archive-product.html` template but doesn't provide a `taxonomy-product_cat.html` template, the Products by Category template will use the `archive-product.html` template. Same for the Products by Tag and Products by Attribute templates.
+- Products by Category, Products by Tag, Products by Brand, and Products by Attribute templates fall back to the Product Catalog template. In other words, if a theme provides an `archive-product.html` template but doesn't provide a `taxonomy-product_cat.html` template, the Products by Category template will use the `archive-product.html` template. Products by Attribute only use the generic `taxonomy-product_attribute.html` template when the theme provides it or a merchant has customized it in the Site Editor; otherwise, they use `archive-product.html`.
- It's possible to create templates for specific products and taxonomies. For example, if the theme provides a template with file name of `single-product-cap.html`, that template will be used when rendering the product with slug `cap`. Similarly, themes can provide specific taxonomy templates: `taxonomy-product_cat-clothing.html` would be used in the product category with slug `clothing`.
- Always keep in mind users can make modifications to the templates provided by the theme via the Site Editor.
@@ -118,3 +117,5 @@ For example, let's imagine you are building a theme and would like to customize
|  |  |
You can find more [documentation on global styles](https://developer.wordpress.org/themes/global-settings-and-styles/styles/) in developer.wordpress.org. You can also find the [list of WooCommerce blocks and their names in the docs](/docs/block-development/reference/block-references).
+
+For WooCommerce-specific `theme.json` examples, block style variations, and Site Editor guidance, see [Global styles for WooCommerce](/docs/theming/block-theme-development/global-styles/).