Commit 7d9d9a27119 for woocommerce

commit 7d9d9a27119636e03210a996a014d3c8aae8f4a8
Author: Tung Du <dinhtungdu@gmail.com>
Date:   Wed May 6 23:47:47 2026 +0700

    Update product filter color handling (#64586)

    * Remove hardcoded color fallbacks from product filters overlay

    * Fix product filters dark theme color fallbacks

    * fix: dynamic filter style

    * Compute inherited colors for product filters editor and frontend

    * Fix lint: dependency comment and PHP alignment

    Add missing "Internal dependencies" comment in set-styles.ts.
    Align equals signs in ProductFilterCheckboxList.php and
    ProductFilterChips.php to match surrounding assignment groups.

    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

    * Add deprecated handler for product-filters color fallback change

    The save function no longer outputs hardcoded #111/#fff fallbacks
    for --wc-product-filters-text-color and background-color CSS vars.
    Existing saved blocks with those values trigger "invalid content"
    in the editor. Add a v1 deprecated entry so WordPress can
    recognize and migrate old markup.

    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

    * Compute product filters color variables from attributes and global styles

    * Add changelog

    * Fix product filters style build

    * Use AssetDataRegistry for global styles colors in product filters editor

    The previous approach using getEditedEntityRecord only returned the user
    customization layer, which is empty when no explicit colors are set.
    Use wp_get_global_styles via AssetDataRegistry to get fully merged values.

    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

    * Fix CI lint/PHPStan failures and blockGap preset conversion bug

    - Fix prettier formatting in deprecated.tsx
    - Update PHPStan baseline pattern for ProductFilters (bool|string|null → string|false)
    - Fix blockGap preset notation not converted to CSS variable in editor
    - Extract presetToCssVariable to shared utility

    * Update plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/deprecated.tsx

    Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>

    * Use Interactivity API to compute product filters color fallbacks from DOM

    Replace wp_get_global_styles fallback with client-side color detection
    using getClosestColor via data-wp-init directive. Works for both block
    and classic themes by walking up the DOM tree for inherited colors.

    Also fixes blockGap preset conversion bug in editor and extracts
    presetToCssVariable to shared utility. Updates PHPStan baseline.

    * Fix getClosestColor transparency check for non-zero RGB values

    Check alpha channel directly instead of only matching rgba(0,0,0,0).
    Prevents treating rgba(255,255,255,0) as opaque white.

    * Skip setting editor color vars when global styles unavailable

    Only set --wc-product-filters-background-color and text-color vars
    when explicit attributes or global styles provide values. On classic
    themes where global styles are empty, vars stay unset and SCSS
    fallback chain (CanvasText/Canvas) handles it.

    * Fix CSS rendering regressions and resolve PHPStan error

    - Add trailing semicolon to CSS custom properties so block support
      styles concatenate correctly
    - Remove :where() from selected chip selector so defaults beat
      unselected custom color rules
    - Cast wp_json_encode to string and remove baseline entry instead
      of suppressing the error

    * Simplify initColors by removing redundant early return

    Store getPropertyValue results in variables to avoid duplicate calls.

    ---------

    Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
    Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>

diff --git a/plugins/woocommerce/changelog/update-product-filter-style-handling b/plugins/woocommerce/changelog/update-product-filter-style-handling
new file mode 100644
index 00000000000..88783d9973e
--- /dev/null
+++ b/plugins/woocommerce/changelog/update-product-filter-style-handling
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Update product filter color handling to use block and global styles.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/deprecated.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/deprecated.tsx
new file mode 100644
index 00000000000..3da47a4821f
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/deprecated.tsx
@@ -0,0 +1,59 @@
+/**
+ * External dependencies
+ */
+import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import { type BlockAttributes } from './types';
+import { getColorsFromBlockSupports } from './utils/get-colors-from-block-supports';
+import { presetToCssVariable } from './utils/preset-to-css-variable';
+
+function isObject< T extends Record< string, unknown >, U >(
+	term: T | U
+): term is NonNullable< T > {
+	return (
+		term !== null && term instanceof Object && term.constructor === Object
+	);
+}
+
+function objectHasProp< P extends PropertyKey >(
+	target: unknown,
+	property: P
+): target is { [ K in P ]: unknown } {
+	return isObject( target ) && property in target;
+}
+
+function getProductFiltersCssV1( attributes: BlockAttributes ) {
+	const colors = getColorsFromBlockSupports( attributes );
+	const styles: Record< string, string | undefined > = {
+		'--wc-product-filters-text-color': colors.textColor || '#111',
+		'--wc-product-filters-background-color':
+			colors.backgroundColor || '#fff',
+	};
+	if (
+		objectHasProp( attributes, 'style' ) &&
+		objectHasProp( attributes.style, 'spacing' ) &&
+		objectHasProp( attributes.style.spacing, 'blockGap' ) &&
+		typeof attributes.style.spacing.blockGap === 'string'
+	) {
+		styles[ '--wc-product-filter-block-spacing' ] = presetToCssVariable(
+			attributes.style.spacing.blockGap
+		);
+	}
+	return styles;
+}
+
+const v1 = {
+	save( { attributes }: { attributes: BlockAttributes } ) {
+		const blockProps = useBlockProps.save( {
+			className: 'wc-block-product-filters',
+			style: getProductFiltersCssV1( attributes ),
+		} );
+		const innerBlocksProps = useInnerBlocksProps.save( blockProps );
+		return <div { ...innerBlocksProps } />;
+	},
+};
+
+export default [ v1 ];
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/edit.tsx
index 56dcaeb2a83..0fe27fc2d5f 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/edit.tsx
@@ -7,6 +7,7 @@ import { __ } from '@wordpress/i18n';
 import { Icon, close } from '@wordpress/icons';
 import { useState } from '@wordpress/element';
 import { filterThreeLines } from '@woocommerce/icons';
+import { getSetting } from '@woocommerce/settings';
 import clsx from 'clsx';

 /**
@@ -14,7 +15,8 @@ import clsx from 'clsx';
  */
 import './editor.scss';
 import { type BlockAttributes } from './types';
-import { getProductFiltersCss } from './utils/get-product-filters-css';
+import { getColorsFromBlockSupports } from './utils/get-colors-from-block-supports';
+import { presetToCssVariable } from './utils/preset-to-css-variable';

 const TEMPLATE: InnerBlockTemplate[] = [
 	[
@@ -40,11 +42,33 @@ export const Edit = ( props: BlockEditProps< BlockAttributes > ) => {
 	const { attributes } = props;
 	const { isPreview } = attributes;
 	const [ isOpen, setIsOpen ] = useState( false );
+
+	const globalColors = getSetting< { background?: string; text?: string } >(
+		'globalStylesColors',
+		{}
+	);
+	const colors = getColorsFromBlockSupports( attributes );
+
+	const blockGap = (
+		attributes as unknown as Record<
+			string,
+			Record< string, Record< string, string > >
+		>
+	 )?.style?.spacing?.blockGap;
+
 	const blockProps = useBlockProps( {
 		className: clsx( 'wc-block-product-filters', {
 			'is-overlay-opened': isOpen,
 		} ),
-		style: getProductFiltersCss( attributes ),
+		style: {
+			'--wc-product-filters-background-color':
+				colors.backgroundColor || globalColors.background || undefined,
+			'--wc-product-filters-text-color':
+				colors.textColor || globalColors.text || undefined,
+			'--wc-product-filter-block-spacing': blockGap
+				? presetToCssVariable( blockGap )
+				: undefined,
+		},
 	} );

 	return (
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/frontend.ts
index f9c0a20e8d5..69585113643 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/frontend.ts
@@ -14,8 +14,9 @@ import type {
 	FilterOptionItem,
 	ProductFiltersContext,
 } from './types';
+import { getClosestColor } from './utils/get-closest-color';

-const { getContext, store, getServerContext, getConfig } = iAPI;
+const { getContext, getElement, store, getServerContext, getConfig } = iAPI;

 const BLOCK_NAME = 'woocommerce/product-filters';

@@ -254,6 +255,34 @@ const productFiltersStore = {
 		},
 	},
 	callbacks: {
+		initColors: () => {
+			const el = getElement();
+			if ( ! el.ref ) return;
+
+			const style = el.ref.style;
+			const hasBg = style.getPropertyValue(
+				'--wc-product-filters-background-color'
+			);
+			const hasFg = style.getPropertyValue(
+				'--wc-product-filters-text-color'
+			);
+
+			if ( ! hasBg ) {
+				const bg = getClosestColor( el.ref, 'backgroundColor' );
+				if ( bg ) {
+					style.setProperty(
+						'--wc-product-filters-background-color',
+						bg
+					);
+				}
+			}
+			if ( ! hasFg ) {
+				const fg = getClosestColor( el.ref, 'color' );
+				if ( fg ) {
+					style.setProperty( '--wc-product-filters-text-color', fg );
+				}
+			}
+		},
 		scrollLimit: () => {
 			const { isOverlayOpened } = getContext< ProductFiltersContext >();
 			if ( isOverlayOpened ) {
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/index.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/index.tsx
index bdf2b029e9f..bafd2cb2bb3 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/index.tsx
@@ -9,6 +9,7 @@ import { Icon } from '@wordpress/icons';
  * Internal dependencies
  */
 import metadata from './block.json';
+import deprecated from './deprecated';
 import { Edit } from './edit';
 import { Save } from './save';

@@ -16,4 +17,5 @@ registerBlockType( metadata, {
 	icon: <Icon icon={ filterThreeLines } />,
 	edit: Edit,
 	save: Save,
+	deprecated,
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/set-styles.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/set-styles.ts
deleted file mode 100644
index 7650d0bae6f..00000000000
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/set-styles.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * Recursively searches up the DOM tree to find the first non-transparent color of the specified type.
- *
- * @param element   - The DOM element to check for color.
- * @param colorType - Whether to check for color or background color.
- *
- * @return The computed RGB color string or null if not found.
- */
-function getClosestColor(
-	element: Element | null,
-	colorType: 'color' | 'backgroundColor'
-): string | null {
-	if ( ! element ) {
-		return null;
-	}
-	const computedColor = window.getComputedStyle( element )[ colorType ];
-
-	// Skip transparent or default "empty" colors.
-	if (
-		computedColor !== 'rgba(0, 0, 0, 0)' &&
-		computedColor !== 'transparent'
-	) {
-		// Extract RGB values from the color string.
-		const rgbValues = computedColor.match( /\d+/g );
-
-		if ( ! rgbValues || rgbValues.length < 3 ) {
-			return null;
-		}
-
-		const [ red, green, blue ] = rgbValues.slice( 0, 3 );
-		return `rgb(${ red }, ${ green }, ${ blue })`;
-	}
-
-	// If current element has transparent color, check parent element.
-	return getClosestColor( element.parentElement, colorType );
-}
-
-/**
- * Sets the appropriate styles for Product Filter selector pills to ensure
- * visibility in both light and dark themes.
- *
- * This function swaps the text and background colors for selected pills
- * to create better contrast in all theme environments.
- */
-function setStyles(): void {
-	/**
-	 * Get the background color of the body then set it as the background color
-	 * of the Product Filter Selector selected pills.
-	 *
-	 * We only set the background color, instead of the whole background. As
-	 * we only provide the option to customize the background color.
-	 */
-
-	// For simplicity, we only consider the background color of the first Product Filter Selector pills.
-	const pillsContainer = document.querySelector(
-		'.wc-block-product-filter-chips__items'
-	);
-
-	if ( ! pillsContainer ) {
-		return;
-	}
-
-	const style = document.createElement( 'style' );
-
-	const selectedPillColor =
-		getClosestColor( pillsContainer, 'backgroundColor' ) || '#fff';
-	const selectedPillBackgroundColor =
-		getClosestColor( pillsContainer, 'color' ) || '#000';
-
-	// We use :where here to reduce specificity so customized colors and theme CSS take priority.
-	style.appendChild(
-		document.createTextNode(
-			`:where(.wc-block-product-filter-chips__item)[aria-checked="true"] {
-				background-color: ${ selectedPillBackgroundColor };
-				color: ${ selectedPillColor };
-				border-color: ${ selectedPillBackgroundColor };
-			}
-
-			:where(.wc-block-product-filter-chips__item)[aria-checked="true"]:hover {
-				background-color: color-mix(in srgb,${ selectedPillBackgroundColor } 85%,transparent)
-			}`
-		)
-	);
-
-	document.head.appendChild( style );
-}
-
-export default setStyles;
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/style.scss
index 6198b20642d..21fb25e54c7 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/chips/style.scss
@@ -24,14 +24,6 @@
 		outline-offset: 1px;
 	}

-	&:hover {
-		background-color: color-mix(
-			in srgb,
-			var(--wp--preset--color--contrast, currentColor) 10%,
-			transparent
-		);
-	}
-
 	.has-chip-text-color & {
 		color: var(--wc-product-filter-chips-text);
 	}
@@ -44,9 +36,9 @@
 }

 .wc-block-product-filter-chips__item[aria-checked="true"] {
-	background: var(--wp--preset--color--contrast, currentColor);
-	color: var(--wp--preset--color--base, Canvas);
-	border-color: var(--wp--preset--color--contrast, currentColor);
+	background: var(--wc-product-filters-text-color, CanvasText);
+	color: var(--wc-product-filters-background-color, Canvas);
+	border-color: var(--wc-product-filters-text-color, CanvasText);

 	.has-selected-chip-text-color & {
 		color: var(--wc-product-filter-chips-selected-text);
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/save.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/save.tsx
index cf2b3896075..604c0d6d84a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/save.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/save.tsx
@@ -8,18 +8,10 @@ import clsx from 'clsx';
  * Internal dependencies
  */
 import './editor.scss';
-import { type BlockAttributes } from './types';
-import { getProductFiltersCss } from './utils/get-product-filters-css';

-export const Save = ( {
-	attributes,
-}: {
-	attributes: BlockAttributes;
-	style: Record< string, string >;
-} ): JSX.Element => {
+export const Save = (): JSX.Element => {
 	const blockProps = useBlockProps.save( {
 		className: clsx( 'wc-block-product-filters' ),
-		style: getProductFiltersCss( attributes ),
 	} );
 	const innerBlocksProps = useInnerBlocksProps.save( blockProps );
 	return <div { ...innerBlocksProps } />;
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/style.scss
index 97443bee5e7..855757c9567 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/style.scss
@@ -52,17 +52,11 @@
 		transition: none;
 		background-color: var(
 			--wc-product-filters-overlay-background-color,
-			var(
-				--wc-product-filters-background-color,
-				var(--wp--preset--color--base, #fff)
-			)
+			var(--wc-product-filters-background-color, Canvas)
 		);
 		color: var(
 			--wc-product-filters-overlay-text-color,
-			var(
-				--wc-product-filters-text-color,
-				var(--wp--preset--color--base, #111)
-			)
+			var(--wc-product-filters-text-color, CanvasText)
 		);
 		display: flex !important;
 		flex-direction: column;
@@ -109,7 +103,6 @@

 		.wc-block-product-filters__overlay-dialog {
 			transform: translateY(0);
-			color: var(--wc-product-filters-overlay-color, inherit);
 			transition: transform 500ms;
 		}
 	}
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/get-closest-color.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/get-closest-color.ts
new file mode 100644
index 00000000000..5ac0596a517
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/get-closest-color.ts
@@ -0,0 +1,22 @@
+export function getClosestColor(
+	element: Element | null,
+	colorType: 'color' | 'backgroundColor'
+): string | null {
+	if ( ! element ) {
+		return null;
+	}
+	const color = window.getComputedStyle( element )[ colorType ];
+	const isFullyTransparent =
+		color === 'transparent' ||
+		( color.startsWith( 'rgba(' ) &&
+			parseFloat( color.split( ',' )[ 3 ] ) === 0 );
+	if ( ! isFullyTransparent ) {
+		const matches = color.match( /\d+/g );
+		if ( ! matches || matches.length < 3 ) {
+			return null;
+		}
+		const [ r, g, b ] = matches.slice( 0, 3 );
+		return `rgb(${ r }, ${ g }, ${ b })`;
+	}
+	return getClosestColor( element.parentElement, colorType );
+}
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/get-product-filters-css.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/get-product-filters-css.ts
deleted file mode 100644
index e4d56ec7364..00000000000
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/get-product-filters-css.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Internal dependencies
- */
-import { getColorsFromBlockSupports } from './get-colors-from-block-supports';
-import type { BlockAttributes } from '../types';
-
-const isNull = < T >( term: T | null ): term is null => {
-	return term === null;
-};
-
-const isObject = < T extends Record< string, unknown >, U >(
-	term: T | U
-): term is NonNullable< T > => {
-	return (
-		! isNull( term ) &&
-		term instanceof Object &&
-		term.constructor === Object
-	);
-};
-
-const isString = < U >( term: string | U ): term is string => {
-	return typeof term === 'string';
-};
-
-function objectHasProp< P extends PropertyKey >(
-	target: unknown,
-	property: P
-): target is { [ K in P ]: unknown } {
-	// The `in` operator throws a `TypeError` for non-object values.
-	return isObject( target ) && property in target;
-}
-
-function presetToCssVariable( preset: string ) {
-	if ( ! preset.includes( ':' ) || ! preset.includes( '|' ) ) {
-		return preset;
-	}
-
-	return `var(--wp--${ preset
-		.replace( 'var:', '' )
-		.replaceAll( '|', '--' ) })`;
-}
-
-export function getProductFiltersCss( attributes: BlockAttributes ) {
-	const colors = getColorsFromBlockSupports( attributes );
-	const styles: Record< string, string | undefined > = {
-		'--wc-product-filters-text-color': colors.textColor || '#111',
-		'--wc-product-filters-background-color':
-			colors.backgroundColor || '#fff',
-	};
-	if (
-		objectHasProp( attributes, 'style' ) &&
-		objectHasProp( attributes.style, 'spacing' ) &&
-		objectHasProp( attributes.style.spacing, 'blockGap' ) &&
-		isString( attributes.style.spacing.blockGap )
-	) {
-		styles[ '--wc-product-filter-block-spacing' ] = presetToCssVariable(
-			attributes.style.spacing.blockGap
-		);
-	}
-	return styles;
-}
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/preset-to-css-variable.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/preset-to-css-variable.ts
new file mode 100644
index 00000000000..979dfc68d32
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/utils/preset-to-css-variable.ts
@@ -0,0 +1,8 @@
+export function presetToCssVariable( preset: string ): string {
+	if ( ! preset.includes( ':' ) || ! preset.includes( '|' ) ) {
+		return preset;
+	}
+	return `var(--wp--${ preset
+		.replace( 'var:', '' )
+		.replaceAll( '|', '--' ) })`;
+}
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index aa02c60038d..c48f3f61cae 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -53998,12 +53998,6 @@ parameters:
 			count: 1
 			path: src/Blocks/BlockTypes/ProductFilters.php

-		-
-			message: '#^Parameter \#1 \$extra_attributes of function get_block_wrapper_attributes expects array\<string, string\>, array\<string, bool\|string\|null\> given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Blocks/BlockTypes/ProductFilters.php
-
 		-
 			message: '#^Parameter \#1 \$str of function md5 expects string, string\|false given\.$#'
 			identifier: argument.type
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php
index 9f1827095c4..83a331f16e8 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilters.php
@@ -5,6 +5,7 @@ declare( strict_types = 1 );
 namespace Automattic\WooCommerce\Blocks\BlockTypes;

 use Automattic\WooCommerce\Blocks\Utils\BlocksSharedState;
+use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils;
 use Automattic\WooCommerce\Internal\ProductFilters\Params;

 /**
@@ -36,9 +37,10 @@ class ProductFilters extends AbstractBlock {
 	 *                           not in the post content on editor load.
 	 */
 	protected function enqueue_data( array $attributes = array() ) {
-		global $pagenow;
 		parent::enqueue_data( $attributes );

+		$this->asset_data_registry->add( 'globalStylesColors', wp_get_global_styles( array( 'color' ) ) );
+
 		BlocksSharedState::load_store_config( 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WooCommerce' );

 		// Classic themes do not support client-side navigation on product
@@ -99,23 +101,15 @@ class ProductFilters extends AbstractBlock {
 			'activeFilters' => $active_filters,
 		);

-		$classes = '';
-		$styles  = '';
-		$tags    = new \WP_HTML_Tag_Processor( $content );
-
-		if ( $tags->next_tag( array( 'class_name' => 'wc-block-product-filters' ) ) ) {
-			$classes = $tags->get_attribute( 'class' );
-			$styles  = $tags->get_attribute( 'style' );
-		}
-
 		$wrapper_attributes = array(
-			'class'                            => $classes,
+			'class'                            => 'wc-block-product-filters',
 			'data-wp-interactive'              => $this->get_full_block_name(),
+			'data-wp-init--colors'             => 'callbacks.initColors',
 			'data-wp-watch--scrolling'         => 'callbacks.scrollLimit',
 			'data-wp-on--keyup'                => 'actions.closeOverlayOnEscape',
-			'data-wp-context'                  => wp_json_encode( $interactivity_context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ),
+			'data-wp-context'                  => (string) wp_json_encode( $interactivity_context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ),
 			'data-wp-class--is-overlay-opened' => 'context.isOverlayOpened',
-			'style'                            => $styles,
+			'style'                            => $this->get_css_variables( $attributes ),
 		);

 		// TODO: Remove this conditional once the fix is released in WP. https://github.com/woocommerce/gutenberg/pull/4.
@@ -193,6 +187,33 @@ class ProductFilters extends AbstractBlock {
 		);
 	}

+	/**
+	 * Get CSS custom properties from block attributes.
+	 *
+	 * @param array $attributes Block attributes.
+	 * @return string CSS custom properties string.
+	 */
+	private function get_css_variables( $attributes ) {
+		$styles = array();
+
+		$bg = StyleAttributesUtils::get_background_color_class_and_style( $attributes );
+		if ( ! empty( $bg['value'] ) ) {
+			$styles[] = sprintf( '--wc-product-filters-background-color: %s', $bg['value'] );
+		}
+
+		$text = StyleAttributesUtils::get_text_color_class_and_style( $attributes );
+		if ( ! empty( $text['value'] ) ) {
+			$styles[] = sprintf( '--wc-product-filters-text-color: %s', $text['value'] );
+		}
+
+		$block_gap = $attributes['style']['spacing']['blockGap'] ?? '';
+		if ( $block_gap ) {
+			$styles[] = sprintf( '--wc-product-filter-block-spacing: %s', StyleAttributesUtils::get_spacing_value( $block_gap ) );
+		}
+
+		return $styles ? implode( ';', $styles ) . ';' : '';
+	}
+
 	/**
 	 * Generate a unique navigation ID for the block.
 	 *