<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__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 : 40px !default;
$swatch__option-height--image : 40px !default;
$swatch__option-margin : 0 $spacer 0 0 !default;
$swatch__option-margin--catalog : 0 !default;
$swatch__option-padding : 0 0 1px 0 !default;
$swatch__option-padding--catalog : $spacer !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;
&--catalog {
width: 100%;
min-height: $swatch__container-min-height--catalog;
.swatch {
margin: $swatch__margin--catalog;
&__option-container {
padding: $swatch__option-padding--catalog;
margin: $swatch__option-margin--catalog;
&: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;
min-width: $swatch__option-width--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');
const 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