Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A11y enhancements #3834

Merged
merged 3 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 9 additions & 4 deletions playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
</head>

<body>
<div class="swiper-container">
<section class="swiper-container">
<div class="swiper-scrollbar"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<button class="swiper-button-prev"></button>
<button class="swiper-button-next"></button>
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
Expand All @@ -26,7 +26,7 @@
<div class="swiper-slide">Slide 10</div>
</div>
<div class="swiper-pagination"></div>
</div>
</section>
<style>
body,
html {
Expand Down Expand Up @@ -75,6 +75,11 @@
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
a11y: {
containerMessage: 'Example content',
containerRoleDescriptionMessage: 'carousel',
itemRoleDescriptionMessage: 'slide',
},
});
</script>
</body>
Expand Down
72 changes: 67 additions & 5 deletions src/components/a11y/a11y.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import $ from '../../utils/dom';
import { bindModuleMethods } from '../../utils/utils';

const A11y = {
getRandomNumber(size = 16) {
const randomChar = () => Math.round(16 * Math.random()).toString(16);
return 'x'.repeat(size).replace(/x/g, randomChar);
},
makeElFocusable($el) {
$el.attr('tabIndex', '0');
return $el;
Expand All @@ -14,10 +18,26 @@ const A11y = {
$el.attr('role', role);
return $el;
},
addElRoleDescription($el, description) {
$el.attr('aria-role-description', description);
return $el;
},
addElControls($el, controls) {
$el.attr('aria-controls', controls);
return $el;
},
addElLabel($el, label) {
$el.attr('aria-label', label);
return $el;
},
addElId($el, id) {
$el.attr('id', id);
return $el;
},
addElLive($el, live) {
$el.attr('aria-live', live);
return $el;
},
disableEl($el) {
$el.attr('aria-disabled', true);
return $el;
Expand Down Expand Up @@ -111,11 +131,43 @@ const A11y = {
},
init() {
const swiper = this;
const params = swiper.params.a11y;

swiper.$el.append(swiper.a11y.liveRegion);

// Container
const $containerEl = swiper.$el;
if (params.containerRoleDescriptionMessage) {
swiper.a11y.addElRoleDescription($containerEl, params.containerRoleDescriptionMessage);
}
if (params.containerMessage) {
swiper.a11y.addElLabel($containerEl, params.containerMessage);
}

// Wrapper
const $wrapperEl = swiper.$wrapperEl;
const wrapperId = $wrapperEl.attr('id') || `swiper-wrapper-${swiper.a11y.getRandomNumber(16)}`;
let live;
swiper.a11y.addElId($wrapperEl, wrapperId);

if (swiper.params.autoplay && swiper.params.autoplay.enabled) {
live = 'off';
} else {
live = 'polite';
}
swiper.a11y.addElLive($wrapperEl, live);

// Slide
if (params.itemRoleDescriptionMessage) {
swiper.a11y.addElRoleDescription($(swiper.slides), params.itemRoleDescriptionMessage);
}
swiper.a11y.addElRole($(swiper.slides), 'group');
swiper.slides.each((slideEl) => {
const $slideEl = $(slideEl);
swiper.a11y.addElLabel($slideEl, `${$slideEl.index() + 1} / ${swiper.slides.length}`);
});

// Navigation
const params = swiper.params.a11y;
let $nextEl;
let $prevEl;
if (swiper.navigation && swiper.navigation.$nextEl) {
Expand All @@ -124,17 +176,24 @@ const A11y = {
if (swiper.navigation && swiper.navigation.$prevEl) {
$prevEl = swiper.navigation.$prevEl;
}

if ($nextEl) {
swiper.a11y.makeElFocusable($nextEl);
swiper.a11y.addElRole($nextEl, 'button');
if ($nextEl[0].tagName !== 'BUTTON') {
swiper.a11y.addElRole($nextEl, 'button');
$nextEl.on('keydown', swiper.a11y.onEnterKey);
}
swiper.a11y.addElLabel($nextEl, params.nextSlideMessage);
$nextEl.on('keydown', swiper.a11y.onEnterKey);
swiper.a11y.addElControls($nextEl, wrapperId);
}
if ($prevEl) {
swiper.a11y.makeElFocusable($prevEl);
swiper.a11y.addElRole($prevEl, 'button');
if ($prevEl[0].tagName !== 'BUTTON') {
swiper.a11y.addElRole($prevEl, 'button');
$prevEl.on('keydown', swiper.a11y.onEnterKey);
}
swiper.a11y.addElLabel($prevEl, params.prevSlideMessage);
$prevEl.on('keydown', swiper.a11y.onEnterKey);
swiper.a11y.addElControls($prevEl, wrapperId);
}

// Pagination
Expand Down Expand Up @@ -197,6 +256,9 @@ export default {
firstSlideMessage: 'This is the first slide',
lastSlideMessage: 'This is the last slide',
paginationBulletMessage: 'Go to slide {{index}}',
containerMessage: null,
containerRoleDescriptionMessage: null,
itemRoleDescriptionMessage: null,
},
},
create() {
Expand Down
21 changes: 21 additions & 0 deletions src/types/components/a11y.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,25 @@ export interface A11yOptions {
* @default 'swiper-notification'
*/
notificationClass?: string;

/**
* Message for screen readers for outer swiper container
*
* @default null
*/
containerMessage?: string;

/**
* Message for screen readers describing the role of outer swiper container
*
* @default null
*/
containerRoleDescriptionMessage?: string;

/**
* Message for screen readers describing the role of slide element
*
* @default null
*/
itemRoleDescriptionMessage?: string;
}