Commit 7c8a860caff for woocommerce
commit 7c8a860caff9c3759230a80884068aebeaa6d080
Author: Tung Du <dinhtungdu@gmail.com>
Date: Wed May 13 16:39:17 2026 +0700
Add color swatch to product filter checkbox list (#64685)
* Add color swatch to product filter checkbox list for visual attributes
Display circular color swatches next to checkbox labels when the attribute
type is wc-visual. Supports server-rendered and Interactivity API paths,
with an empty-state diagonal line for terms without a color value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add changelog for color swatch in product filter checkbox list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): woocommerce
* Add color swatch to product filter checkbox list editor preview
Extend editor rendering to show color swatches for visual attributes:
- Register color term meta with show_in_rest for visual attribute taxonomies
- Pass visual attribute term colors via AssetDataRegistry to the editor
- Detect visual attributes in attribute-filter edit and add color to items
- Render color swatch in checkbox-list edit component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove changelog files — feature is behind feature flag
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert attribute filter files to trunk — color support already merged via #64694
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove outline style from checked color swatches
The checkbox check mark is sufficient to indicate selected state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix minor style
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/edit.tsx
index b9ce18e7bf8..9d62a022f74 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/edit.tsx
@@ -122,6 +122,26 @@ const CheckboxListEdit = ( props: EditProps ): JSX.Element => {
/>
</span>
<span className="wc-block-product-filter-checkbox-list__text-wrapper">
+ { item.color !== undefined && (
+ <span
+ className={ clsx(
+ 'wc-block-product-filter-checkbox-list__color-swatch',
+ {
+ 'is-empty':
+ ! item.color,
+ }
+ ) }
+ style={
+ item.color
+ ? {
+ backgroundColor:
+ item.color,
+ }
+ : undefined
+ }
+ aria-hidden="true"
+ />
+ ) }
<span className="wc-block-product-filter-checkbox-list__text">
{ typeof item.label === 'string'
? decodeHtmlEntities(
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/frontend.ts
index 204204e329d..f247eefc4d4 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/frontend.ts
@@ -8,7 +8,7 @@ import { store, getContext } from '@wordpress/interactivity';
*/
import type { SelectableItem } from '../../../../types/type-defs/selectable-items';
-type ItemWithIndex = SelectableItem & { index?: number };
+type ItemWithIndex = SelectableItem & { index?: number; color?: string };
type CheckboxListContext = {
storeNamespace: string;
@@ -24,6 +24,8 @@ type CheckboxListStore = {
state: {
itemHidden: boolean;
ratingStyle: string;
+ colorSwatchStyle: string;
+ isColorSwatchEmpty: boolean;
};
actions: {
showAll: () => void;
@@ -55,6 +57,17 @@ const { state }: CheckboxListStore = store< CheckboxListStore >(
if ( ! item ) return '';
return `width: ${ Number( item.value ) * 20 }%`;
},
+ get colorSwatchStyle(): string {
+ const { storeNamespace } = getContext< CheckboxListContext >();
+ const item = getParentItem( storeNamespace );
+ if ( ! item?.color ) return '';
+ return `background-color: ${ item.color }`;
+ },
+ get isColorSwatchEmpty(): boolean {
+ const { storeNamespace } = getContext< CheckboxListContext >();
+ const item = getParentItem( storeNamespace );
+ return ! item?.color;
+ },
},
actions: {
showAll() {
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/style.scss
index 9212da868ad..aebd84a8656 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/checkbox-list/style.scss
@@ -123,6 +123,26 @@ input[type="checkbox"].wc-block-product-filter-checkbox-list__input:checked {
white-space: nowrap;
}
+.wc-block-product-filter-checkbox-list__color-swatch {
+ border-radius: 50%;
+ border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
+ display: inline-block;
+ flex-shrink: 0;
+ height: 1em;
+ margin-right: 0.25em;
+ width: 1em;
+
+ &.is-empty {
+ background: linear-gradient(
+ to bottom right,
+ transparent calc(50% - 0.5px),
+ color-mix(in srgb, currentColor 20%, transparent) calc(50% - 0.5px),
+ color-mix(in srgb, currentColor 20%, transparent) calc(50% + 0.5px),
+ transparent calc(50% + 0.5px)
+ );
+ }
+}
+
.wc-block-product-filter-checkbox-list__stars {
display: inline-block;
height: 1.55em;
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 2da9a67c986..e08e1623879 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
@@ -117,6 +117,7 @@
border-radius: 50%;
border: 1px solid transparent;
outline: none;
+ color: inherit;
&:hover {
border-color: color-mix(in srgb, currentColor 40%, transparent);
@@ -143,9 +144,9 @@
background: linear-gradient(
to top left,
transparent calc(50% - 0.5px),
- color-mix(in srgb, currentColor 40%, transparent)
+ color-mix(in srgb, currentColor 20%, transparent)
calc(50% - 0.5px),
- color-mix(in srgb, currentColor 40%, transparent)
+ color-mix(in srgb, currentColor 20%, transparent)
calc(50% + 0.5px),
transparent calc(50% + 0.5px)
);
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterCheckboxList.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterCheckboxList.php
index f77a66cf8b9..24387b3e96f 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterCheckboxList.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterCheckboxList.php
@@ -82,6 +82,7 @@ final class ProductFilterCheckboxList extends AbstractBlock {
$hidden_count = max( 0, count( $items ) - count( $visible_items ) );
$first_item = reset( $items );
$show_counts = is_array( $first_item ) && array_key_exists( 'count', $first_item );
+ $has_colors = is_array( $first_item ) && array_key_exists( 'color', $first_item );
ob_start();
?>
@@ -137,6 +138,15 @@ final class ProductFilterCheckboxList extends AbstractBlock {
<?php echo $stars_svg; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</span>
<?php else : ?>
+ <?php if ( $has_colors ) : ?>
+ <span
+ class="wc-block-product-filter-checkbox-list__color-swatch<?php echo empty( $item['color'] ) ? ' is-empty' : ''; ?>"
+ <?php if ( ! empty( $item['color'] ) ) : ?>
+ style="background-color: <?php echo esc_attr( $item['color'] ); ?>"
+ <?php endif; ?>
+ aria-hidden="true"
+ ></span>
+ <?php endif; ?>
<span class="wc-block-product-filter-checkbox-list__text">
<?php echo esc_html( $item['label'] ); ?>
</span>
@@ -185,6 +195,14 @@ final class ProductFilterCheckboxList extends AbstractBlock {
<?php echo $stars_svg; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</span>
<?php else : ?>
+ <?php if ( $has_colors ) : ?>
+ <span
+ class="wc-block-product-filter-checkbox-list__color-swatch"
+ data-wp-class--is-empty="woocommerce/product-filter-checkbox-list::state.isColorSwatchEmpty"
+ data-wp-bind--style="woocommerce/product-filter-checkbox-list::state.colorSwatchStyle"
+ aria-hidden="true"
+ ></span>
+ <?php endif; ?>
<span
class="wc-block-product-filter-checkbox-list__text"
data-wp-text="context.item.label"