Commit 20aaca9e70 for woocommerce
commit 20aaca9e700851e836b1583bb3e97ed81b8f33f7
Author: Allison Levine <1689238+allilevine@users.noreply.github.com>
Date: Fri Feb 13 12:41:28 2026 -0500
Fix spacing issues in email editor product collection rendering (#63177)
* Fix spacing issues in email editor product collection rendering
- Add 32px margin between product items in single-column layout
- Add 32px margin between product image and title for visual hierarchy
- Apply block padding styles (padding-top/bottom) to product images
- Filter out empty margin-top values to prevent malformed CSS output
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): packages/php/email-editor
* Remove image-to-title margin-top spacing in product collection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use theme block gap for product collection spacing instead of hardcoded values
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use fixed spacing for inner product elements to match editor behavior
The editor does not apply blockGap between inner product elements
(image, title, price), so using a fixed 16px value (from the email
editor's base theme.json) ensures editor and preview stay consistent
regardless of the site theme's blockGap setting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Reduce inner block spacing to 8px to account for block renderer padding
Block renderers like Product Price (5px) and Product Button (12px)
add their own internal padding, which stacks on top of the margin.
Reducing from 16px to 8px brings the total visual gap closer to
what the editor shows.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/packages/php/email-editor/changelog/63177-fix-email-editor-product-collection-spacing b/packages/php/email-editor/changelog/63177-fix-email-editor-product-collection-spacing
new file mode 100644
index 0000000000..2f2c894d0b
--- /dev/null
+++ b/packages/php/email-editor/changelog/63177-fix-email-editor-product-collection-spacing
@@ -0,0 +1,4 @@
+Significance: minor
+Type: fix
+
+Fix spacing issues in email editor product collection rendering.
\ No newline at end of file
diff --git a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php
index 7f254fc38e..d0e45f400f 100644
--- a/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php
+++ b/packages/php/email-editor/src/Integrations/Core/Renderer/Blocks/class-abstract-block-renderer.php
@@ -65,7 +65,13 @@ abstract class Abstract_Block_Renderer implements Block_Renderer {
* @return string
*/
protected function add_spacer( $content, $email_attrs ): string {
- $gap_style = WP_Style_Engine::compile_css( array_intersect_key( $email_attrs, array_flip( array( 'margin-top' ) ) ), '' ) ?? '';
+ // Filter out empty margin-top values to prevent malformed CSS output.
+ $margin_top_attrs = array_intersect_key( $email_attrs, array_flip( array( 'margin-top' ) ) );
+ if ( isset( $margin_top_attrs['margin-top'] ) && '' === trim( $margin_top_attrs['margin-top'] ) ) {
+ $margin_top_attrs = array();
+ }
+
+ $gap_style = WP_Style_Engine::compile_css( $margin_top_attrs, '' ) ?? '';
$padding_style = WP_Style_Engine::compile_css( array_intersect_key( $email_attrs, array_flip( array( 'padding-left', 'padding-right' ) ) ), '' ) ?? '';
$table_attrs = array(
diff --git a/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-collection.php b/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-collection.php
index 838a057073..393165d8a4 100644
--- a/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-collection.php
+++ b/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-collection.php
@@ -15,6 +15,14 @@ use WP_Query;
* Renders a product collection block for email.
*/
class Product_Collection extends Abstract_Product_Block_Renderer {
+ /**
+ * Default spacing between inner product elements (image, title, price).
+ * This is a fixed value from the email editor's base theme.json, independent
+ * of the site theme's blockGap, because the editor does not apply blockGap
+ * between inner product elements.
+ */
+ private const INNER_BLOCK_SPACING = '8px';
+
/**
* Render the product collection block content for email.
*
@@ -98,14 +106,26 @@ class Product_Collection extends Abstract_Product_Block_Renderer {
// Limit columns to max 2 for email compatibility.
$columns = min( max( $columns, 1 ), 2 );
+ // Get the block gap from theme styles to match the editor spacing.
+ $theme_styles = $rendering_context->get_theme_styles();
+ $block_gap = $theme_styles['spacing']['blockGap'] ?? '16px';
+
if ( 1 === $columns ) {
// Single column layout - render products vertically.
$content = '';
+ $index = 0;
foreach ( $products as $product ) {
+ // For the first product, use the original email_attrs.
+ // For subsequent products, add margin-top for spacing between items.
+ $email_attrs = $inner_block['email_attrs'] ?? array();
+ if ( $index > 0 && ! isset( $email_attrs['margin-top'] ) ) {
+ $email_attrs['margin-top'] = $block_gap;
+ }
$content .= $this->add_spacer(
$this->render_product_content( $product, $inner_block, $collection_type ),
- $inner_block['email_attrs'] ?? array()
+ $email_attrs
);
+ ++$index;
}
return $content;
}
@@ -113,7 +133,7 @@ class Product_Collection extends Abstract_Product_Block_Renderer {
// Two-column layout using HTML tables for email compatibility.
// Wrap with add_spacer to match single-column spacing behavior.
return $this->add_spacer(
- $this->render_two_column_grid( $products, $inner_block, $collection_type, $rendering_context ),
+ $this->render_two_column_grid( $products, $inner_block, $collection_type, $rendering_context, $block_gap ),
$inner_block['email_attrs'] ?? array()
);
}
@@ -125,9 +145,10 @@ class Product_Collection extends Abstract_Product_Block_Renderer {
* @param array $inner_block Inner block data.
* @param string $collection_type Collection type identifier.
* @param Rendering_Context $rendering_context Rendering context.
+ * @param string $block_gap Block gap value from theme styles.
* @return string
*/
- private function render_two_column_grid( array $products, array $inner_block, string $collection_type, Rendering_Context $rendering_context ): string {
+ private function render_two_column_grid( array $products, array $inner_block, string $collection_type, Rendering_Context $rendering_context, string $block_gap = '16px' ): string {
$content = '';
// Calculate the cell width from the actual layout width.
@@ -170,7 +191,7 @@ class Product_Collection extends Abstract_Product_Block_Renderer {
// Add spacing between rows (except after the last row).
if ( $row_index < count( $product_chunks ) - 1 ) {
- $content .= '<tr><td colspan="2" style="height: 20px;"></td></tr>';
+ $content .= sprintf( '<tr><td colspan="2" style="height: %s;"></td></tr>', esc_attr( $block_gap ) );
}
}
@@ -195,13 +216,25 @@ class Product_Collection extends Abstract_Product_Block_Renderer {
return $content;
}
+ $inner_index = 0;
foreach ( $template_block['innerBlocks'] as $inner_block ) {
+ // Override the preprocessor-applied blockGap margin-top for inner blocks.
+ // The editor does not vary spacing between inner product elements
+ // (image, title, price) when blockGap changes, so we use a fixed value
+ // to keep editor and preview consistent.
+ $inner_block['email_attrs'] = $inner_block['email_attrs'] ?? array();
+ if ( 0 === $inner_index ) {
+ unset( $inner_block['email_attrs']['margin-top'] );
+ } else {
+ $inner_block['email_attrs']['margin-top'] = self::INNER_BLOCK_SPACING;
+ }
+
// Set cell width context for multi-column layouts.
if ( null !== $cell_width ) {
- $inner_block['email_attrs'] = $inner_block['email_attrs'] ?? array();
$inner_block['email_attrs']['width'] = $cell_width . 'px';
}
+ ++$inner_index;
switch ( $inner_block['blockName'] ) {
case 'woocommerce/product-price':
case 'woocommerce/product-button':
diff --git a/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-image.php b/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-image.php
index ac868e7a34..2d479c73e0 100644
--- a/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-image.php
+++ b/packages/php/email-editor/src/Integrations/WooCommerce/Renderer/Blocks/class-product-image.php
@@ -433,6 +433,12 @@ class Product_Image extends Abstract_Product_Block_Renderer {
'vertical-align' => 'top',
);
+ // Apply padding from block styles (e.g., padding-top, padding-bottom).
+ $padding_styles = Styles_Helper::get_block_styles( $parsed_block['attrs'] ?? array(), $rendering_context, array( 'spacing' ) );
+ if ( ! empty( $padding_styles['declarations'] ) ) {
+ $cell_styles = array_merge( $cell_styles, $padding_styles['declarations'] );
+ }
+
$align = $parsed_block['attrs']['align'] ?? 'left';
$cell_styles['text-align'] = $align;