diff --git a/assets/component-facets.css b/assets/component-facets.css index ca766b95d91..20a3cffd7cf 100644 --- a/assets/component-facets.css +++ b/assets/component-facets.css @@ -300,8 +300,10 @@ } .facets-layout-grid { + --swatch-input--size: 2.4rem; + display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(3, minmax(0, 1fr)); text-align: center; padding: 2rem 2.4rem; gap: 3rem 1rem; @@ -326,7 +328,7 @@ align-items: flex-start; } -.facets-layout-grid .visual-display-parent { +.facets-layout-grid .facets__label { display: flex; flex-direction: column; gap: 0.8rem; @@ -340,7 +342,7 @@ cursor: pointer; } -.facet-checkbox { +.facets__label { padding: 1rem 2rem 1rem 0; flex-grow: 1; position: relative; @@ -361,7 +363,13 @@ } } -.facet-checkbox input[type='checkbox'] { +/* Disabled state */ +.facets__label.disabled { + opacity: 0.4; + pointer-events: none; +} + +.facets-layout-list input[type='checkbox'] { position: absolute; opacity: 1; width: 1.6rem; @@ -384,12 +392,6 @@ opacity: 0; } -.facets__visual-display-wrapper { - display: flex; - justify-content: center; - flex-shrink: 0; -} - .no-js .facet-checkbox input[type='checkbox'] { z-index: 0; } @@ -423,12 +425,6 @@ } } -.facet-checkbox--disabled, -.mobile-facets__label--disabled { - opacity: 0.4; - pointer-events: none; -} - .facets__price { display: flex; padding: 2rem; diff --git a/assets/component-product-variant-picker.css b/assets/component-product-variant-picker.css index 71cb55a6130..3f1ab35e6d9 100644 --- a/assets/component-product-variant-picker.css +++ b/assets/component-product-variant-picker.css @@ -145,6 +145,8 @@ variant-selects { } .product-form__input--swatch .swatch-input__input + .swatch-input__label { + --swatch-input--size: 4.4rem; + margin: 0.7rem 1.2rem 0.2rem 0; } diff --git a/assets/component-swatch-input.css b/assets/component-swatch-input.css index 149c51f6746..89c78f7955f 100644 --- a/assets/component-swatch-input.css +++ b/assets/component-swatch-input.css @@ -1,9 +1,9 @@ /* swatch-input lives in its own file for reusability of the swatch in other areas than the product form context */ .swatch-input__input + .swatch-input__label { - --swatch-input--size: 4.4rem; --swatch-input--border-radius: 50%; display: inline-block; + max-width: 100%; border-radius: var(--swatch-input--border-radius); cursor: pointer; outline-offset: 0.2rem; @@ -14,7 +14,8 @@ --swatch-input--border-radius: 0.2rem; } -.swatch-input__input + .swatch-input__label:hover { +.swatch-input__input + .swatch-input__label:hover, +.swatch-input__input:hover + .swatch-input__label { outline: 0.2rem solid rgba(var(--color-foreground), 0.4); } @@ -28,6 +29,11 @@ outline: none; } +/* Actually disabled */ +.swatch-input__input:disabled + .swatch-input__label { + pointer-events: none; +} + /* Focus visible */ .swatch-input__input:focus-visible + .swatch-input__label { box-shadow: 0 0 0 0.5rem rgb(var(--color-background)), 0 0 0 0.7rem rgba(var(--color-foreground), 0.55); diff --git a/assets/component-swatch.css b/assets/component-swatch.css index 75d91b50f0e..834416d6637 100644 --- a/assets/component-swatch.css +++ b/assets/component-swatch.css @@ -5,8 +5,10 @@ display: block; width: var(--swatch--size); + max-width: 100%; aspect-ratio: 1 / 1; background: var(--swatch--background); + background-position: var(--swatch-focal-point, initial); background-size: cover; background-origin: border-box; border: 0.1rem solid rgba(var(--color-foreground), 0.45); diff --git a/assets/component-visual-display.css b/assets/component-visual-display.css deleted file mode 100644 index f683b455d7e..00000000000 --- a/assets/component-visual-display.css +++ /dev/null @@ -1,90 +0,0 @@ -.visual-display { - --visual-display__size: min(2.4rem, 100%); - position: relative; - width: var(--visual-display__size); - max-width: 100%; - border: 0.1rem solid rgba(var(--color-foreground), 0.2); - aspect-ratio: 1/1; -} - -.visual-display.empty { - border-style: dashed; -} - -.visual-display--presentation-swatch { - --visual-display__size: min(2.4rem, 100%); - - border-radius: 100%; - overflow: hidden; -} - -.visual-display-parent .visual-display--presentation-swatch { - outline-offset: 0.2rem; -} - -/* Hover, active, and focus states */ -:is( - .visual-display-parent:hover .visual-display--presentation-swatch, - .visual-display-parent.active .visual-display--presentation-swatch, - .visual-display-parent:has(:focus-visible) .visual-display--presentation-swatch - ) { - outline-style: solid; -} - -/* Active state */ -.visual-display-parent.active .visual-display--presentation-swatch { - outline-width: 0.2rem; - outline-color: rgb(var(--color-foreground), 1); -} - -/* Hover state */ -.visual-display-parent:hover .visual-display--presentation-swatch { - outline-width: 0.2rem; - outline-color: rgb(var(--color-foreground), 0.4); -} - -/* Focus state */ -.visual-display-parent:has(:focus-visible) .visual-display--presentation-swatch { - outline-width: 0.2rem; - outline-color: rgb(var(--color-foreground), 0.4); - box-shadow: 0 0 0 0.6rem rgb(var(--color-background)), 0 0 0 0.8rem rgba(var(--color-foreground), 0.5), - 0 0 1.2rem 0.4rem rgba(var(--color-foreground), 0.3); -} - -/* Focus state for older browsers */ -@supports not selector(:has(a, b)) { - .visual-display-parent:focus-within .visual-display--presentation-swatch { - outline-offset: 0.2rem; - outline: 0.2rem solid rgb(var(--color-foreground), 0.4); - box-shadow: 0 0 0 0.6rem rgb(var(--color-background)), 0 0 0 0.8rem rgba(var(--color-foreground), 0.5), - 0 0 1.2rem 0.4rem rgba(var(--color-foreground), 0.3); - } -} - -.visual-display-parent.disabled { - opacity: 0.4; - pointer-events: none; -} - -/* Used to display the disabled dash */ -.visual-display-parent.disabled .visual-display::after { - display: block; - content: ''; - - /* 1.414 is not a magic number, it's the square root of 2, or the length of the diagonal */ - width: calc(var(--visual-display__size) * 1.414); - border-bottom: 0.1rem solid rgb(var(--color-background-contrast)); - transform: rotate(-45deg); - transform-origin: left; -} - -.visual-display .visual-display__child { - display: block; - height: 100%; - width: 100%; - forced-color-adjust: none; -} - -.visual-display--presentation-swatch .visual-display__image { - object-fit: cover; -} diff --git a/assets/global.js b/assets/global.js index c541c6fb82e..7145ddd6b73 100644 --- a/assets/global.js +++ b/assets/global.js @@ -1015,6 +1015,11 @@ class VariantSelects extends HTMLElement { selectedDropdownSwatchValue.style.setProperty('--swatch--background', 'unset'); selectedDropdownSwatchValue.classList.add('swatch--unavailable'); } + + selectedDropdownSwatchValue.style.setProperty( + '--swatch-focal-point', + target.selectedOptions[0].dataset.optionSwatchFocalPoint || 'unset' + ); } else if (tagName === 'INPUT' && target.type === 'radio') { const selectedSwatchValue = this.querySelector(`[data-selected-swatch-value="${name}"]`); if (selectedSwatchValue) selectedSwatchValue.innerHTML = value; diff --git a/snippets/facets.liquid b/snippets/facets.liquid index 88ad351eecb..7cde6a68a19 100644 --- a/snippets/facets.liquid +++ b/snippets/facets.liquid @@ -13,7 +13,8 @@ {% endcomment %} {{ 'component-show-more.css' | asset_url | stylesheet_tag }} -{{ 'component-visual-display.css' | asset_url | stylesheet_tag }} +{{ 'component-swatch-input.css' | asset_url | stylesheet_tag }} +{{ 'component-swatch.css' | asset_url | stylesheet_tag }} {%- liquid assign sort_by = results.sort_by | default: results.default_sort_by @@ -116,13 +117,11 @@ assign total_active_values = total_active_values | plus: filter.active_values.size case filter.presentation when 'swatch' - assign has_visual_display = true assign show_more_number = 15 assign visual_layout_class = 'facets-layout-grid facets-layout-grid--' | append: filter.presentation else - assign has_visual_display = false - assign visual_layout_class = 'facets-layout-list' assign show_more_number = 10 + assign visual_layout_class = 'facets-layout-list' endcase %} @@ -182,54 +181,79 @@ {%- endif -%}
{{ filter.label | escape }} + {%- liquid + assign sorted_values = filter.values + # Keep the selected values grouped together when operator is AND + if filter.operator == 'AND' + assign active_filter_values = filter.values | where: 'active', true + assign inactive_filter_values = filter.values | where: 'active', false + assign sorted_values = active_filter_values | concat: inactive_filter_values + endif + -%} {% comment %} No show more for no JS {% endcomment %} @@ -691,10 +738,8 @@ {% liquid case filter.presentation when 'swatch' - assign has_visual_display = true assign visual_layout_class = 'facets-layout-grid facets-layout-grid--' | append: filter.presentation else - assign has_visual_display = false assign visual_layout_class = 'facets-layout-list' endcase %} @@ -742,40 +787,66 @@ -%} {%- for value in sorted_values -%} {% liquid + assign input_id = 'Filter-' | append: filter.param_name | escape | append: '-mobile-' | append: forloop.index assign is_disabled = false if value.count == 0 and value.active == false assign is_disabled = true endif %} -
  • -
  • + {% if filter.presentation == 'swatch' %} +
    +
    + {% render 'swatch-input', + id: input_id, + type: 'checkbox', + name: value.param_name, + value: value.value, + product_form_id: 'FacetFiltersFormMobile', + swatch: value.swatch, + checked: value.active, + disabled: is_disabled %}
    - {% else %} + + {{ text_value }} +
    + {% else %} + + {{ text_value }} + + {% endif %}
  • {%- endfor -%} diff --git a/snippets/product-variant-options.liquid b/snippets/product-variant-options.liquid index d3dbaaa7eca..67def0ec294 100644 --- a/snippets/product-variant-options.liquid +++ b/snippets/product-variant-options.liquid @@ -46,9 +46,11 @@ endcase endfor + assign swatch_focal_point = null if value.swatch.image assign image_url = value.swatch.image | image_url: width: 50 assign swatch_value = 'url(' | append: image_url | append: ')' + assign swatch_focal_point = value.swatch.image.presentation.focal_point elsif value.swatch.color assign swatch_value = 'rgb(' | append: value.swatch.color.rgb | append: ')' else @@ -115,6 +117,9 @@ {% endif %} {% if swatch_value and picker_type == 'swatch_dropdown' %} data-option-swatch-value="{{ swatch_value }}" + {% if swatch_focal_point %} + data-option-swatch-focal-point="{{ swatch_focal_point }}" + {% endif %} {% endif %} > {% if option_disabled -%} diff --git a/snippets/swatch.liquid b/snippets/swatch.liquid index 11cc63d4d04..c052d965606 100644 --- a/snippets/swatch.liquid +++ b/snippets/swatch.liquid @@ -12,10 +12,11 @@ {% endcomment %} {%- liquid - assign swatch_value = nil + assign swatch_value = null if swatch.image assign image_url = swatch.image | image_url: width: 50 assign swatch_value = 'url(' | append: image_url | append: ')' + assign swatch_focal_point = swatch.image.presentation.focal_point elsif swatch.color assign swatch_value = 'rgb(' | append: swatch.color.rgb | append: ')' endif @@ -24,7 +25,7 @@ - {%- case type -%} - {%- when 'colors' -%} - {% liquid - assign size_limit = value.size | at_most: 4 - assign rotation = '0deg' - if size_limit == 2 - assign rotation = '45deg' - endif - - assign angle_increment = 360 | divided_by: size_limit - assign angle = 0 - %} - {%- capture conic_gradient -%} - {%- for color in value limit: size_limit -%} - {{ color }} {{ angle }}deg{%- assign angle = angle | plus: angle_increment %} {{ angle }}deg{%- unless forloop.last %}, {%- endunless -%} - {%- endfor -%} - {%- endcapture -%} -
    - {%- when 'image' -%} - {{ - value - | image_url: width: 300 - | image_tag: class: 'visual-display__child visual-display__image', alt: value.alt - }} - {%- else -%} -
    - {%- endcase -%} -