<div class="swatch ">
<div class="swatch__wrapper" aria-label="Color" tabindex="0" aria-activedescendant="opt-color-red-3" aria-required="true" role="listbox" aria-invalid="false">
<div class="swatch__option-container " id="opt-color-orange-1" aria-label="Orange" tabindex="0" role="option" aria-selected="false">
<div class="swatch__option swatch__option--image" style="background-image: url(../../images/product/product_48x72.jpg)">
</div>
</div>
<div class="swatch__option-container " id="opt-color-green-2" aria-label="Green" tabindex="0" role="option" aria-selected="false">
<div class="swatch__option swatch__option--image" style="background-image: url(../../images/product/product_48x72.jpg)">
</div>
</div>
<div class="swatch__option-container selected" id="opt-color-red-3" aria-label="Red" tabindex="0" role="option" aria-selected="true">
<div class="swatch__option swatch__option--image" style="background-image: url(../../images/product/product_48x72.jpg)">
</div>
</div>
<div class="swatch__option-container " id="opt-color-white-4" aria-label="White" tabindex="0" role="option" aria-selected="false">
<div class="swatch__option swatch__option--image" style="background-image: url(../../images/product/product_48x72.jpg)">
</div>
</div>
</div>
</div>
<script src="/components/raw/swatch/swatch.js"></script>
<div class="swatch {{ class }}">
{{#if heading}}
<{{{ heading.tag }}}
id="{{ heading.id }}"
class="swatch__title {{ heading.class }}"
>
{{ heading.text }}
</{{{ heading.tag }}}>
{{/if}}
<div
class="swatch__wrapper"
{{{ wrapper.attributes }}}
>
{{#each options }}
<div
class="swatch__option-container {{ this.class }}"
id="{{ this.id }}"
{{{ this.attributes }}}
{{#if this.headingId}}
aria-describedby="{{ this.headingId }}"
{{/if}}
>
<div
class="swatch__option {{ this.option.class}}"
{{{ this.option.attributes }}}
>
{{ this.text }}
</div>
</div>
{{/each }}
</div>
</div>
{{#if script}}
<script src="{{static 'swatch.js' }}"></script>
{{/if}}
{
"script": true,
"heading": false,
"wrapper": {
"attributes": "aria-label=\"Color\" tabindex=\"0\" aria-activedescendant=\"opt-color-red-3\" aria-required=\"true\" role=\"listbox\" aria-invalid=\"false\""
},
"options": [
{
"id": "opt-color-orange-1",
"attributes": "aria-label=\"Orange\" tabindex=\"0\" role=\"option\" aria-selected=\"false\"",
"option": {
"class": "swatch__option--image",
"attributes": "style=\"background-image: url(../../images/product/product_48x72.jpg)\""
}
},
{
"id": "opt-color-green-2",
"attributes": "aria-label=\"Green\" tabindex=\"0\" role=\"option\" aria-selected=\"false\"",
"option": {
"class": "swatch__option--image",
"attributes": "style=\"background-image: url(../../images/product/product_48x72.jpg)\""
}
},
{
"id": "opt-color-red-3",
"class": "selected",
"attributes": "aria-label=\"Red\" tabindex=\"0\" role=\"option\" aria-selected=\"true\"",
"option": {
"class": "swatch__option--image",
"attributes": "style=\"background-image: url(../../images/product/product_48x72.jpg)\""
}
},
{
"id": "opt-color-white-4",
"attributes": "aria-label=\"White\" tabindex=\"0\" role=\"option\" aria-selected=\"false\"",
"option": {
"class": "swatch__option--image",
"attributes": "style=\"background-image: url(../../images/product/product_48x72.jpg)\""
}
}
]
}
$swatch__margin : 0 !default;
$swatch__margin--catalog : 0 0 $spacer 0 !default;
$swatch__transition : $transition-base !default;
$swatch__wrapper-justify : flex-start !default;
$swatch__container-min-height : 100px !default;
$swatch__container-min-height--catalog: 56px !default;
$swatch__container-margin-bottom : $spacer--25 !default;
$swatch__title-margin : 0 0 $spacer !default;
$swatch__title-font-size : $font-size-base !default;
$swatch__title-color : $color-secondary !default;
$swatch__option-width : 40px !default;
$swatch__option-height : 40px !default;
$swatch__option-width--catalog : 30px !default;
$swatch__option-height--catalog : 30px !default;
$swatch__option-width--image : 48px !default;
$swatch__option-height--image : 48px !default;
$swatch__option-margin : 0 $spacer 0 0 !default;
$swatch__option-padding : 0 0 1px 0 !default;
$swatch__option-background : $white !default;
$swatch__option-color : $color-secondary !default;
$swatch__option-border : 2px solid $white !default;
$swatch__option-border-width : 0 0 2px 0 !default;
$swatch__option-border--white : 2px $border-style-base $gray-lighter !default;
$swatch__option-border--white-catalog : $border-width-base $border-style-base $gray-light !default;
$swatch__option-border-color--active : $color-primary !default;
$swatch__option-border--size : $border-width-base $border-style-base $gray-light !default;
@import 'swatch-variables';
.swatch {
display: flex;
flex-wrap: wrap;
margin: $swatch__margin;
&[class*="size"] {
.swatch__option {
border: $swatch__option-border--size;
}
}
&__container {
// magento container for loading
position: relative;
min-height: $swatch__container-min-height;
margin-bottom: $swatch__container-margin-bottom;
&--catalog {
width: 100%;
min-height: $swatch__container-min-height--catalog;
.swatch {
margin: $swatch__margin--catalog;
&__option-container {
padding: $spacer;
margin: 0;
&:hover,
&:focus,
&.selected {
.swatch__option--white {
border: $swatch__option-border--white-catalog;
}
}
}
&__option {
min-width: $swatch__option-width--catalog;
min-height: $swatch__option-height--catalog;
&--white {
border: $swatch__option-border--white-catalog;
}
}
}
}
}
&__wrapper {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: $swatch__wrapper-justify;
}
&__option-container {
box-sizing: border-box;
border: $swatch__option-border;
border-width: $swatch__option-border-width;
margin: $swatch__option-margin;
padding: $swatch__option-padding;
transition: $swatch__transition;
cursor: pointer;
&:hover,
&:focus,
&.selected {
outline: none;
border-color: $swatch__option-border-color--active;
.swatch__option--white {
border: 0;
}
}
&.disabled {
opacity: 0.5;
cursor: not-allowed;
}
&:last-child {
margin-right: 0;
}
}
&__selected-option {
display: none;
}
&__title {
flex: 0 0 100%;
margin: $swatch__title-margin;
font-size: $swatch__title-font-size;
color: $swatch__title-color;
}
&__option {
display: flex;
justify-content: center;
align-items: center;
min-width: $swatch__option-width;
min-height: $swatch__option-height;
background-color: $swatch__option-background;
color: $swatch__option-color;
&--image {
min-height: $swatch__option-height--image;
background-size: cover;
background-position: top center;
}
&--white {
border: $swatch__option-border--white;
}
}
&__input {
@include visually-hidden;
}
}
'use strict';
(function() { // eslint-disable-line
const swatchWrapper = document.querySelector('.swatch__wrapper'),
swatchOptions = swatchWrapper.querySelectorAll('.swatch__option-container');
swatchOptions.forEach(option => {
option.addEventListener('click', () => {
swatchOptions.forEach(option => {
option.classList.remove('selected');
option.setAttribute('aria-selected', 'false');
})
option.classList.add('selected');
swatchWrapper.setAttribute('aria-activedescendant', option.id);
option.setAttribute('aria-selected', 'true');
})
})
})();
Swatches require following aria structure to be accessible for assistive technology and keyboard:
Swatches wrapper .swatch__wrapper
should have following aria attributes:
role="listbox"
to easy list swatch’s optionsaria-activedescendant
with value of selected swatch optiontabindex="0"
to make element focusablearia-required
with value true
or false
depending if swatch option is requiredaria-invalid
with value true
if required field is not filled (if none of options is selected) or with the value false
if required field is field (option is selected)Swatch option .swatch__option
should have following aria attributes:
role="option"
to show options of swatchtabindex="0"
to make element focusablearia-label
with value of the name/label of swatch option if the swatch option is not a text but graphic element - icon or image (color option - ex: “Green”)aria-describedby
with value of the listbox label/heading if existaria-selected
with value true
if the option was selected