<div class="swatch ">
<span id="option-label-color-1" class="swatch__title ">
Label
</span>
<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" aria-describedby="option-label-color-1">
<div class="swatch__option " style="background-color: #ffa500">
</div>
</div>
<div class="swatch__option-container " id="opt-color-green-2" aria-label="Green" tabindex="0" role="option" aria-selected="false" aria-describedby="option-label-color-1">
<div class="swatch__option " style="background-color: #00ff00">
</div>
</div>
<div class="swatch__option-container selected" id="opt-color-red-3" aria-label="Red" tabindex="0" role="option" aria-selected="true" aria-describedby="option-label-color-1">
<div class="swatch__option " style="background-color: #ff0000">
</div>
</div>
<div class="swatch__option-container " id="opt-color-black-4" aria-label="White" tabindex="0" role="option" aria-selected="false" aria-describedby="option-label-color-1">
<div class="swatch__option " style="background-color: #000000">
</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": {
"id": "option-label-color-1",
"tag": "span",
"text": "Label"
},
"wrapper": {
"attributes": "aria-label=\"Color\" tabindex=\"0\" aria-activedescendant=\"opt-color-red-3\" aria-required=\"true\" role=\"listbox\" aria-invalid=\"false\""
},
"options": [
{
"headingId": "option-label-color-1",
"id": "opt-color-orange-1",
"attributes": "aria-label=\"Orange\" tabindex=\"0\" role=\"option\" aria-selected=\"false\"",
"option": {
"attributes": "style=\"background-color: #ffa500\""
}
},
{
"headingId": "option-label-color-1",
"id": "opt-color-green-2",
"attributes": "aria-label=\"Green\" tabindex=\"0\" role=\"option\" aria-selected=\"false\"",
"option": {
"attributes": "style=\"background-color: #00ff00\""
}
},
{
"headingId": "option-label-color-1",
"id": "opt-color-red-3",
"class": "selected",
"attributes": "aria-label=\"Red\" tabindex=\"0\" role=\"option\" aria-selected=\"true\"",
"option": {
"attributes": "style=\"background-color: #ff0000\""
}
},
{
"headingId": "option-label-color-1",
"id": "opt-color-black-4",
"attributes": "aria-label=\"White\" tabindex=\"0\" role=\"option\" aria-selected=\"false\"",
"option": {
"attributes": "style=\"background-color: #000000\""
}
}
]
}
$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