Commit ec46f8ab944 for woocommerce

commit ec46f8ab94493fb70c619916dfda0982027d56e1
Author: Ann <annchichi@users.noreply.github.com>
Date:   Fri Jul 3 18:32:11 2026 +0800

    Fix email order item alignment (#66141)

    * Fix email order item alignment

    * Add changelog entry for email order item alignment

    * Move styles to CSS

    * Update fulfillment emails

    * Middle vertical align order item titles in email

    * Fix email thumbnail width when image_size is a named size

    The order and fulfillment email item templates computed the thumbnail
    cell width by running absint() directly on image_size. When image_size
    is a registered size name (e.g. "woocommerce_thumbnail") rather than an
    array, absint() returns 0, collapsing the cell to 24px.

    Normalize image_size through wc_get_image_size() before computing the
    width so both named sizes and [width, height] arrays resolve correctly.
    Guard the returned value since the woocommerce_get_image_size_{name}
    filter lets third parties alter it, so 'width' is not guaranteed.

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

    ---------

    Co-authored-by:  Ján Mikláš <neosinner@gmail.com>
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-63238-email-order-item-alignment b/plugins/woocommerce/changelog/fix-63238-email-order-item-alignment
new file mode 100644
index 00000000000..d0cdc9500d9
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-63238-email-order-item-alignment
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Align email order item names consistently after product thumbnails.
diff --git a/plugins/woocommerce/src/Internal/EmailEditor/WooContentProcessor.php b/plugins/woocommerce/src/Internal/EmailEditor/WooContentProcessor.php
index 0b83263238f..8e35380aef0 100644
--- a/plugins/woocommerce/src/Internal/EmailEditor/WooContentProcessor.php
+++ b/plugins/woocommerce/src/Internal/EmailEditor/WooContentProcessor.php
@@ -135,7 +135,7 @@ class WooContentProcessor {
 			.order-item-data td {
 				border: 0;
 				padding: 0;
-				vertical-align: top;
+				vertical-align: middle;
 			}
 			.order-item-data img {
 				border-radius: 4px;
diff --git a/plugins/woocommerce/templates/emails/email-fulfillment-items.php b/plugins/woocommerce/templates/emails/email-fulfillment-items.php
index 460374dd7c1..6598e82cf8f 100644
--- a/plugins/woocommerce/templates/emails/email-fulfillment-items.php
+++ b/plugins/woocommerce/templates/emails/email-fulfillment-items.php
@@ -12,7 +12,7 @@
  *
  * @see     https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates\Emails
- * @version 10.8.0
+ * @version 11.0.0
  */

 use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -62,6 +62,10 @@ foreach ( $items as $item_id => $item ) :
 					<?php
 					// Show title/image etc.
 					if ( $show_image ) {
+						$image_dimensions = wc_get_image_size( $image_size );
+						$image_width      = is_array( $image_dimensions ) && isset( $image_dimensions['width'] ) ? absint( $image_dimensions['width'] ) : 48;
+						$thumbnail_width  = $image_width + 24;
+
 						/**
 						 * Email Order Item Thumbnail hook.
 						 *
@@ -69,7 +73,7 @@ foreach ( $items as $item_id => $item ) :
 						 * @param WC_Order_Item_Product $item  The item being displayed.
 						 * @since 2.1.0
 						 */
-						echo '<td>' . wp_kses_post( apply_filters( 'woocommerce_order_item_thumbnail', $image, $item->item ) ) . '</td>';
+						echo '<td class="email-order-item-thumbnail" style="width: ' . esc_attr( $thumbnail_width ) . 'px;">' . wp_kses_post( apply_filters( 'woocommerce_order_item_thumbnail', $image, $item->item ) ) . '</td>';
 					}
 					?>
 					<td>
diff --git a/plugins/woocommerce/templates/emails/email-order-items.php b/plugins/woocommerce/templates/emails/email-order-items.php
index 209f1ccc125..518c0a29688 100644
--- a/plugins/woocommerce/templates/emails/email-order-items.php
+++ b/plugins/woocommerce/templates/emails/email-order-items.php
@@ -12,7 +12,7 @@
  *
  * @see     https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates\Emails
- * @version 10.8.0
+ * @version 11.0.0
  */

 use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -31,6 +31,13 @@ foreach ( $items as $item_id => $item ) :
 	$purchase_note = '';
 	$image         = '';

+	/**
+	 * Email Order Item Visibility hook.
+	 *
+	 * @param bool                  $visible Whether the order item is visible.
+	 * @param WC_Order_Item_Product $item    The item being displayed.
+	 * @since 2.1.0
+	 */
 	if ( ! apply_filters( 'woocommerce_order_item_visible', true, $item ) ) {
 		continue;
 	}
@@ -41,8 +48,17 @@ foreach ( $items as $item_id => $item ) :
 		$image         = $product->get_image( $image_size );
 	}

+	/**
+	 * Email Order Item Class hook.
+	 *
+	 * @param string                $class The order item row class.
+	 * @param WC_Order_Item_Product $item  The item being displayed.
+	 * @param WC_Order              $order The order object.
+	 * @since 2.1.0
+	 */
+	$order_item_class = apply_filters( 'woocommerce_order_item_class', 'order_item', $item, $order );
 	?>
-	<tr class="<?php echo esc_attr( apply_filters( 'woocommerce_order_item_class', 'order_item', $item, $order ) ); ?>">
+	<tr class="<?php echo esc_attr( $order_item_class ); ?>">
 		<td class="td font-family text-align-left" style="vertical-align: <?php echo $block_email_editor_enabled ? 'top' : 'middle'; ?>; word-wrap:break-word;">
 			<?php if ( $email_improvements_enabled ) { ?>
 				<table class="order-item-data" role="presentation">
@@ -50,6 +66,10 @@ foreach ( $items as $item_id => $item ) :
 						<?php
 						// Show title/image etc.
 						if ( $show_image ) {
+							$image_dimensions = wc_get_image_size( $image_size );
+							$image_width      = is_array( $image_dimensions ) && isset( $image_dimensions['width'] ) ? absint( $image_dimensions['width'] ) : 48;
+							$thumbnail_width  = $image_width + 24;
+
 							/**
 							 * Email Order Item Thumbnail hook.
 							 *
@@ -57,7 +77,7 @@ foreach ( $items as $item_id => $item ) :
 							 * @param WC_Order_Item_Product $item  The item being displayed.
 							 * @since 2.1.0
 							 */
-							echo '<td style="vertical-align: top;">' . wp_kses_post( apply_filters( 'woocommerce_order_item_thumbnail', $image, $item ) ) . '</td>';
+							echo '<td class="email-order-item-thumbnail" style="width: ' . esc_attr( $thumbnail_width ) . 'px;">' . wp_kses_post( apply_filters( 'woocommerce_order_item_thumbnail', $image, $item ) ) . '</td>';
 						}
 						?>
 						<td>
diff --git a/plugins/woocommerce/templates/emails/email-styles.php b/plugins/woocommerce/templates/emails/email-styles.php
index 81e033b6471..325e48fc0ad 100644
--- a/plugins/woocommerce/templates/emails/email-styles.php
+++ b/plugins/woocommerce/templates/emails/email-styles.php
@@ -12,7 +12,7 @@
  *
  * @see     https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates\Emails
- * @version 10.8.0
+ * @version 11.0.0
  */

 use Automattic\WooCommerce\Internal\Email\EmailFont;
@@ -274,7 +274,7 @@ body {
 #body_content .order-item-data td {
 	border: 0 !important;
 	padding: 0 !important;
-	vertical-align: top;
+	vertical-align: middle;
 }

 #body_content .email-order-details .order-totals td,
@@ -388,6 +388,14 @@ body {
 	font-family: <?php echo $safe_font_family; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
 }

+.order-item-data {
+	width: 100%;
+}
+
+.order-item-data h3 {
+	margin: 0;
+}
+
 .link {
 	color: <?php echo esc_attr( $link_color ); ?>;
 }
diff --git a/plugins/woocommerce/tests/php/includes/emails/class-wc-email-order-items-test.php b/plugins/woocommerce/tests/php/includes/emails/class-wc-email-order-items-test.php
new file mode 100644
index 00000000000..1d2d518c143
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/emails/class-wc-email-order-items-test.php
@@ -0,0 +1,54 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Tests for `email-order-items.php` template.
+ *
+ * @covers `email-order-items.php` template
+ */
+class WC_Email_Order_Items_Test extends \WC_Unit_Test_Case {
+
+	/**
+	 * Order IDs created during tests.
+	 *
+	 * @var int[]
+	 */
+	private array $order_ids = array();
+
+	/**
+	 * Tear down after each test.
+	 */
+	public function tearDown(): void {
+		foreach ( $this->order_ids as $order_id ) {
+			WC_Helper_Order::delete_order( $order_id );
+		}
+		$this->order_ids = array();
+
+		update_option( 'woocommerce_feature_email_improvements_enabled', 'no' );
+
+		parent::tearDown();
+	}
+
+	/**
+	 * @testdox Order item image and text columns use fixed alignment with email improvements enabled.
+	 */
+	public function test_order_item_image_and_text_columns_use_fixed_alignment_with_email_improvements(): void {
+		update_option( 'woocommerce_feature_email_improvements_enabled', 'yes' );
+
+		$order             = WC_Helper_Order::create_order();
+		$this->order_ids[] = $order->get_id();
+
+		$content = wc_get_email_order_items( $order );
+
+		$this->assertStringContainsString(
+			'<table class="order-item-data" role="presentation">',
+			$content,
+			'Order item data table should fill the product column so rows share the same layout.'
+		);
+		$this->assertStringContainsString(
+			'<td class="email-order-item-thumbnail" style="width: 72px;">',
+			$content,
+			'Thumbnail column should reserve the image width plus the email improvements image gap.'
+		);
+	}
+}