Commit 2ac4ec89d2 for woocommerce

commit 2ac4ec89d270321e8d24061fd9234e1f3d39057d
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Fri May 30 07:32:33 2025 -0600

    [Experimental] Use regular form in Add to Cart with Options when iAPI can't be used in variable products (#58313)

    * Use regular form in Add to Cart with Options when iAPI can't be used in variable products

    * Add changelog file

    * Use regular radio controls instead of div with a role attribute

    * Remove Product Collection specific directive

    * Update tests

    * Add tests explanatory comment

    * Use class names instead of generic radio input selector

    * Use the attribute slug to support attribute with spaces and special characters

    * Get variationId from the state instead of the context

diff --git a/plugins/woocommerce/changelog/fix-55699-add-to-cart-with-options-legacy-mode-variable-products b/plugins/woocommerce/changelog/fix-55699-add-to-cart-with-options-legacy-mode-variable-products
new file mode 100644
index 0000000000..9a2f910a83
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-55699-add-to-cart-with-options-legacy-mode-variable-products
@@ -0,0 +1,5 @@
+Significance: patch
+Type: update
+Comment: Use regular form in Add to Cart with Options when iAPI can't be used in variable products
+
+
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts
index 11b279c3b7..19cb87e96a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts
@@ -86,12 +86,8 @@ const getMatchedVariation = (
 			( [ attributeName, attributeValue ] ) => {
 				const attributeMatched = selectedAttributes.some(
 					( variationAttribute ) => {
-						const formattedVariationAttribute =
-							'attribute_' +
-							variationAttribute.attribute.toLowerCase();
-
 						const isSameAttribute =
-							formattedVariationAttribute === attributeName;
+							variationAttribute.attribute === attributeName;
 						if ( ! isSameAttribute ) {
 							return false;
 						}
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/frontend.ts
index afc154cf8e..336720ffed 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/frontend.ts
@@ -1,8 +1,8 @@
 /**
  * External dependencies
  */
-import type { ChangeEvent, KeyboardEvent } from 'react';
-import { store, getContext, getElement } from '@wordpress/interactivity';
+import type { ChangeEvent } from 'react';
+import { store, getContext } from '@wordpress/interactivity';
 import type { CartVariationItem } from '@woocommerce/types';

 /**
@@ -28,10 +28,6 @@ type Context = {
 	options: Option[];
 };

-type PillsContext = Context & {
-	focused?: string;
-};
-
 // Set selected pill styles for proper contrast.
 setStyles();

@@ -54,7 +50,7 @@ function setAttribute( name: string, value: string | null ) {
 }

 function setDefaultSelectedAttribute() {
-	const context = getContext< PillsContext >();
+	const context = getContext< Context >();
 	setAttribute( context.name, context.selectedValue );
 }

@@ -94,12 +90,8 @@ const isAttributeValueValid = ( {
 	return availableVariations.some( ( availableVariation ) => {
 		// Skip variations that don't match the current attribute value.
 		if (
-			availableVariation.attributes[
-				'attribute_' + attributeName.toLowerCase()
-			] !== attributeValue &&
-			availableVariation.attributes[
-				'attribute_' + attributeName.toLowerCase()
-			] !== '' // "" is used for "any".
+			availableVariation.attributes[ attributeName ] !== attributeValue &&
+			availableVariation.attributes[ attributeName ] !== '' // "" is used for "any".
 		) {
 			return false;
 		}
@@ -109,7 +101,7 @@ const isAttributeValueValid = ( {
 			( selectedAttribute ) => {
 				const availableVariationAttributeValue =
 					availableVariation.attributes[
-						'attribute_' + selectedAttribute.attribute.toLowerCase()
+						selectedAttribute.attribute
 					];
 				// If the current available variation matches the selected
 				// value, count it.
@@ -124,8 +116,7 @@ const isAttributeValueValid = ( {
 				// selection.
 				if ( availableVariationAttributeValue === '' ) {
 					if (
-						selectedAttribute.attribute.toLowerCase() !==
-							attributeName.toLowerCase() ||
+						selectedAttribute.attribute !== attributeName ||
 						attributeValue === selectedAttribute.value
 					) {
 						return true;
@@ -139,16 +130,16 @@ const isAttributeValueValid = ( {
 	} );
 };

-const { state, actions } = store(
+const { state } = store(
 	'woocommerce/add-to-cart-with-options-variation-selector-attribute-options__pills',
 	{
 		state: {
 			get isPillSelected() {
-				const { selectedValue, option } = getContext< PillsContext >();
+				const { selectedValue, option } = getContext< Context >();
 				return selectedValue === option.value;
 			},
 			get isPillDisabled() {
-				const { name, option } = getContext< PillsContext >();
+				const { name, option } = getContext< Context >();
 				const { selectedAttributes, availableVariations } =
 					getContext< AddToCartWithOptionsStoreContext >(
 						'woocommerce/add-to-cart-with-options'
@@ -161,31 +152,8 @@ const { state, actions } = store(
 					availableVariations,
 				} );
 			},
-			get pillTabIndex() {
-				const { selectedValue, focused, option, options } =
-					getContext< PillsContext >();
-
-				if ( state.isPillDisabled ) {
-					return -1;
-				}
-
-				// Allow the first pill to be focused when no option is selected.
-				if (
-					! selectedValue &&
-					! focused &&
-					options[ 0 ]?.value === option.value
-				) {
-					return 0;
-				}
-
-				if ( state.isPillSelected || focused === option.value ) {
-					return 0;
-				}
-
-				return -1;
-			},
 			get index() {
-				const context = getContext< PillsContext >();
+				const context = getContext< Context >();
 				return context.options.findIndex(
 					( option ) => option.value === context.option.value
 				);
@@ -196,125 +164,17 @@ const { state, actions } = store(
 				if ( state.isPillDisabled ) {
 					return;
 				}
-				const context = getContext< PillsContext >();
+				const context = getContext< Context >();
 				if ( context.selectedValue === context.option.value ) {
 					context.selectedValue = '';
 				} else {
 					context.selectedValue = context.option.value;
 				}
-				context.focused = context.option.value;
 				setAttribute( context.name, context.selectedValue );
 			},
-			handleKeyDown( event: KeyboardEvent< HTMLElement > ) {
-				let keyWasProcessed = false;
-
-				switch ( event.key ) {
-					case ' ':
-						keyWasProcessed = true;
-						actions.toggleSelected();
-						break;
-
-					case 'Up':
-					case 'ArrowUp':
-					case 'Left':
-					case 'ArrowLeft': {
-						keyWasProcessed = true;
-						const context = getContext< PillsContext >();
-						const { selectedAttributes, availableVariations } =
-							getContext< AddToCartWithOptionsStoreContext >(
-								'woocommerce/add-to-cart-with-options'
-							);
-						const { index } = state;
-						if ( index <= 0 ) {
-							return;
-						}
-
-						for ( let i = index - 1; i >= 0; i-- ) {
-							if (
-								isAttributeValueValid( {
-									attributeName: context.name,
-									attributeValue: context.options[ i ].value,
-									selectedAttributes,
-									availableVariations,
-								} )
-							) {
-								context.selectedValue =
-									context.options[ i ].value;
-								context.focused = context.selectedValue;
-
-								setAttribute(
-									context.name,
-									context.selectedValue
-								);
-
-								return;
-							}
-						}
-						break;
-					}
-
-					case 'Down':
-					case 'ArrowDown':
-					case 'Right':
-					case 'ArrowRight': {
-						keyWasProcessed = true;
-						const context = getContext< PillsContext >();
-						const { selectedAttributes, availableVariations } =
-							getContext< AddToCartWithOptionsStoreContext >(
-								'woocommerce/add-to-cart-with-options'
-							);
-						const { index } = state;
-						if ( index >= context.options.length - 1 ) {
-							return;
-						}
-
-						for (
-							let i = index + 1;
-							i < context.options.length;
-							i++
-						) {
-							if (
-								isAttributeValueValid( {
-									attributeName: context.name,
-									attributeValue: context.options[ i ].value,
-									selectedAttributes,
-									availableVariations,
-								} )
-							) {
-								context.selectedValue =
-									context.options[ i ].value;
-								context.focused = context.selectedValue;
-
-								setAttribute(
-									context.name,
-									context.selectedValue
-								);
-
-								return;
-							}
-						}
-						break;
-					}
-					default:
-						break;
-				}
-
-				if ( keyWasProcessed ) {
-					event.stopPropagation();
-					event.preventDefault();
-				}
-			},
 		},
 		callbacks: {
 			setDefaultSelectedAttribute,
-			watchSelected() {
-				const { focused } = getContext< PillsContext >();
-
-				if ( state.pillTabIndex === 0 && focused ) {
-					const { ref } = getElement();
-					ref?.focus();
-				}
-			},
 		},
 	},
 	{ lock: true }
@@ -325,7 +185,7 @@ store(
 	{
 		state: {
 			get isOptionDisabled() {
-				const { name, option } = getContext< PillsContext >();
+				const { name, option } = getContext< Context >();

 				if ( option.value === '' ) {
 					return false;
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/set-styles.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/set-styles.ts
index d434cf3533..21aa740f88 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/set-styles.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/set-styles.ts
@@ -70,7 +70,7 @@ function setStyles(): void {
 	// We use :where here to reduce specificity so customized colors and theme CSS take priority.
 	style.appendChild(
 		document.createTextNode(
-			`:where(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill)[aria-checked="true"] {
+			`:where(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill):has(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input:checked) {
 				background-color: ${ selectedPillBackgroundColor };
 				color: ${ selectedPillColor };
 				border-color: ${ selectedPillBackgroundColor };
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss
index eeaae640de..4efd3ccf02 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss
@@ -24,12 +24,12 @@
 		);
 	}

-	&:focus {
-		outline-color: var(--wp--preset--color--accent-4);
-		outline-offset: 2px;
+	&:has(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input:focus) {
+		outline: 1px solid var(--wp--preset--color--accent-4);
+		outline-offset: 1px;
 	}

-	&[aria-checked="true"] {
+	&:has(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input:checked) {
 		&:hover {
 			background-color: color-mix(
 				in srgb,
@@ -39,15 +39,19 @@
 		}
 	}

-	&[aria-checked="false"] {
-		border-color: currentColor;
-	}
-
-	&[aria-disabled="true"] {
+	&:has(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input:disabled) {
 		border-color: var(--wc-subtext);
 		color: var(--wc-subtext);
 		text-decoration: line-through;
 	}
+
+	// We can't make it display: none, otherwise it's not focusable.
+	.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input {
+		height: 0;
+		width: 0;
+		opacity: 0;
+		position: absolute;
+	}
 }

 .wc-block-add-to-cart-with-options-variation-selector-attribute-options__dropdown {
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
index 7a0c2578ba..51875ee987 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
@@ -128,18 +128,11 @@ test.describe( 'Add to Cart + Options Block', () => {

 		await page.goto( '/hoodie' );

-		const logoNoOption = page.getByRole( 'radio', {
-			name: 'No',
-			exact: true,
-		} );
-		const colorBlueOption = page.getByRole( 'radio', {
-			name: 'Blue',
-			exact: true,
-		} );
-		const colorGreenOption = page.getByRole( 'radio', {
-			name: 'Green',
-			exact: true,
-		} );
+		// The radio input is visually hidden and, thus, not clickable. That's
+		// why we need to select the <label> instead.
+		const logoNoOption = page.locator( 'label:has-text("No")' );
+		const colorBlueOption = page.locator( 'label:has-text("Blue")' );
+		const colorGreenOption = page.locator( 'label:has-text("Green")' );
 		const addToCartButton = page.getByText( 'Add to cart' ).first();

 		await logoNoOption.click();
@@ -172,14 +165,10 @@ test.describe( 'Add to Cart + Options Block', () => {

 		await page.goto( '/hoodie' );

-		const logoYesOption = page.getByRole( 'radio', {
-			name: 'Yes',
-			exact: true,
-		} );
-		const colorGreenOption = page.getByRole( 'radio', {
-			name: 'Green',
-			exact: true,
-		} );
+		// The radio input is visually hidden and, thus, not clickable. That's
+		// why we need to select the <label> instead.
+		const logoYesOption = page.locator( 'label:has-text("Yes")' );
+		const colorGreenOption = page.locator( 'label:has-text("Green")' );

 		await expect( colorGreenOption ).toBeEnabled();

diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
index 06aad4bb09..8d49c52641 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/AddToCartWithOptions.php
@@ -293,10 +293,11 @@ class AddToCartWithOptions extends AbstractBlock {
 			remove_filter( 'render_block_context', array( $this, 'set_is_descendant_of_add_to_cart_with_options_context' ) );

 			$wrapper_attributes = array(
-				'class'               => $classes,
-				'style'               => esc_attr( $classes_and_styles['styles'] ),
-				'data-wp-interactive' => 'woocommerce/add-to-cart-with-options',
-				'data-wp-context'     => wp_json_encode(
+				'class'                     => $classes,
+				'style'                     => esc_attr( $classes_and_styles['styles'] ),
+				'data-wp-interactive'       => 'woocommerce/add-to-cart-with-options',
+				'data-wp-class--is-invalid' => '!state.isFormValid',
+				'data-wp-context'           => wp_json_encode(
 					$context,
 					JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
 				),
@@ -326,12 +327,15 @@ class AddToCartWithOptions extends AbstractBlock {
 					$hidden_input = '<input type="hidden" name="add-to-cart" value="' . $product->get_id() . '" />';
 				} elseif ( ProductType::GROUPED === $product_type ) {
 					$hidden_input = '<input type="hidden" name="add-to-cart" value="' . $product->get_id() . '" />';
+				} elseif ( ProductType::VARIABLE === $product_type ) {
+					$hidden_input  = '<input type="hidden" name="add-to-cart" value="' . $product->get_id() . '" />';
+					$hidden_input .= '<input type="hidden" name="product_id" value="' . $product->get_id() . '" />';
+					$hidden_input .= '<input type="hidden" name="variation_id" data-wp-interactive="woocommerce/add-to-cart-with-options" data-wp-bind--value="state.variationId" />';
 				}
 			} else {
 				// Otherwise, we use the Interactivity API.
 				$form_attributes = array(
-					'data-wp-on--submit'        => 'actions.handleSubmit',
-					'data-wp-class--is-invalid' => '!state.isFormValid',
+					'data-wp-on--submit' => 'actions.handleSubmit',
 				);
 			}

diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php
index 0de2c15c04..8b7a1d2d57 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php
@@ -50,9 +50,9 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 			return '';
 		}

-		$attribute_name = $block->context['woocommerce/attributeName'];
+		$attribute_slug = wc_variation_attribute_name( $block->context['woocommerce/attributeName'] );

-		if ( isset( $attribute_name ) ) {
+		if ( isset( $attribute_slug ) ) {

 			$attributes = $this->parse_attributes( $attributes );

@@ -138,30 +138,32 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 	 */
 	protected function render_pills( $attributes, $content, $block ) {
 		$attribute_id    = $block->context['woocommerce/attributeId'];
-		$attribute_name  = $block->context['woocommerce/attributeName'];
+		$attribute_slug  = wc_variation_attribute_name( $block->context['woocommerce/attributeName'] );
 		$attribute_terms = $block->context['woocommerce/attributeTerms'];

 		$pills = '';
 		foreach ( $attribute_terms as $attribute_term ) {
-			$pills .= sprintf(
-				'<div %s>%s</div>',
+			$input = sprintf(
+				'<input type="radio" %s/>',
 				$this->get_normalized_attributes(
 					array(
-						'role'                        => 'radio',
-						'class'                       => 'wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill',
-						'data-wp-bind--tabindex'      => 'state.pillTabIndex',
-						'data-wp-bind--aria-checked'  => 'state.isPillSelected',
-						'data-wp-bind--aria-disabled' => 'state.isPillDisabled',
-						'data-wp-watch'               => 'callbacks.watchSelected',
-						'data-wp-on--click'           => 'actions.toggleSelected',
-						'data-wp-on--keydown'         => 'actions.handleKeyDown',
-						'data-wp-context'             => array(
+						'class'                  => 'wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input',
+						'name'                   => $attribute_slug,
+						'value'                  => $attribute_term['value'],
+						'data-wp-bind--checked'  => 'state.isPillSelected',
+						'data-wp-bind--disabled' => 'state.isPillDisabled',
+						'data-wp-watch'          => 'callbacks.watchSelected',
+						'data-wp-on--click'      => 'actions.toggleSelected',
+						'data-wp-on--keydown'    => 'actions.handleKeyDown',
+						'data-wp-context'        => array(
 							'option' => $attribute_term,
 						),
 					),
 				),
 				$attribute_term['label']
 			);
+
+			$pills .= '<label class="wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill">' . $input . $attribute_term['label'] . '</label>';
 		}

 		return sprintf(
@@ -174,7 +176,7 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 					'aria-labeledby'      => $attribute_id . '_label',
 					'data-wp-interactive' => $this->get_full_block_name() . '__pills',
 					'data-wp-context'     => array(
-						'name'          => $attribute_name,
+						'name'          => $attribute_slug,
 						'options'       => $attribute_terms,
 						'selectedValue' => $this->get_default_selected_attribute( $attribute_terms ),
 						'focused'       => '',
@@ -196,7 +198,7 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 	 */
 	protected function render_dropdown( $attributes, $content, $block ) {
 		$attribute_id    = $block->context['woocommerce/attributeId'];
-		$attribute_name  = $block->context['woocommerce/attributeName'];
+		$attribute_slug  = wc_variation_attribute_name( $block->context['woocommerce/attributeName'] );
 		$attribute_terms = $block->context['woocommerce/attributeTerms'];
 		$default_option  = array(
 			'label'      => esc_html__( 'Choose an option', 'woocommerce' ),
@@ -219,7 +221,7 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 						'data-wp-bind--disabled' => 'state.isOptionDisabled',
 						'data-wp-context'        => array(
 							'option'  => $attribute_term,
-							'name'    => $attribute_name,
+							'name'    => $attribute_slug,
 							'options' => $attribute_terms,
 						),
 					),
@@ -236,12 +238,13 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 					'id'                  => $attribute_id,
 					'data-wp-interactive' => $this->get_full_block_name() . '__dropdown',
 					'data-wp-context'     => array(
-						'name'          => $attribute_name,
+						'name'          => $attribute_slug,
 						'options'       => $attribute_terms,
 						'selectedValue' => $this->get_default_selected_attribute( $attribute_terms ),
 					),
 					'data-wp-init'        => 'callbacks.setDefaultSelectedAttribute',
 					'data-wp-on--change'  => 'actions.handleChange',
+					'name'                => $attribute_slug,
 				),
 			),
 			$options,
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorItemTemplate.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorItemTemplate.php
index 39ac42fdcf..b460eac7db 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorItemTemplate.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorItemTemplate.php
@@ -48,15 +48,14 @@ class VariationSelectorItemTemplate extends AbstractBlock {
 	/**
 	 * Get product row HTML.
 	 *
-	 * @param string   $product_attribute_name Product Attribute Name.
+	 * @param string   $attribute_name Product Attribute Name.
 	 * @param array    $product_attribute_terms Product Attribute Terms.
 	 * @param WP_Block $block The Block.
 	 * @return string Row HTML
 	 */
-	private function get_product_row( $product_attribute_name, $product_attribute_terms, $block ): string {
+	private function get_product_row( $attribute_name, $product_attribute_terms, $block ): string {
 		global $product;

-		$attribute_name     = $product_attribute_name;
 		$attribute_terms    = $this->get_terms( $attribute_name, $product_attribute_terms );
 		$product_variations = $product->get_available_variations();

@@ -66,8 +65,8 @@ class VariationSelectorItemTemplate extends AbstractBlock {
 			function ( $term ) use ( $product_variations, $attribute_name, $attribute_terms ) {
 				foreach ( $product_variations as $product_variation ) {
 					if (
-						$term['value'] === $product_variation['attributes'][ 'attribute_' . strtolower( $attribute_name ) ] ||
-						'' === $product_variation['attributes'][ 'attribute_' . strtolower( $attribute_name ) ]
+						$term['value'] === $product_variation['attributes'][ wc_variation_attribute_name( $attribute_name ) ] ||
+						'' === $product_variation['attributes'][ wc_variation_attribute_name( $attribute_name ) ]
 					) {
 						return true;
 					}
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php
index a84d0d2beb..50c44292e8 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductButton.php
@@ -220,7 +220,7 @@ class ProductButton extends AbstractBlock {
 		';

 		$button_directives = $is_descendant_of_add_to_cart_form ? '' : 'data-wp-on--click="actions.addCartItem"';
-		$anchor_directive  = 'data-wp-on--click="woocommerce/product-collection::actions.viewProduct"';
+		$anchor_directive  = $is_descendant_of_add_to_cart_form ? '' : 'data-wp-on--click="woocommerce/product-collection::actions.viewProduct"';

 		$span_button_directives = '
 			data-wp-text="state.addToCartText"