From 8d2249a67f79397cbcf8c79ea04c05821a3eb539 Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Wed, 15 Dec 2021 19:35:01 -0500 Subject: [PATCH 1/7] handle carousel's aria-live attribute --- js/flickity.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/flickity.js b/js/flickity.js index 5fe52eb..100f9e2 100644 --- a/js/flickity.js +++ b/js/flickity.js @@ -190,6 +190,15 @@ proto.activate = function() { this.element.tabIndex = 0; // listen for key presses this.element.addEventListener( 'keydown', this ); + // handle aria-live + if ( this.options.autoPlay ) { + // https://www.w3.org/TR/wai-aria-practices/examples/carousel/carousel-1.html + // Very importantly, if automatic rotation is turned on, the live region is disabled. If it were not, the page would be come unusable as announcements of the continuously changing content constantly interrupt anything else the user is reading. + this.element.setAttribute('aria-live', 'off') + } else { + // When automatic rotation is turned off, the carousel slide content is included in a live region. This makes it easier for screen reader users to scan through the carousel slides. When screen reader users activate the next or previous slide button , the new slide content is announced, giving users immediate feedback that helps them determine whether or not to interact with the content. + this.element.setAttribute('aria-live', 'assertive') + } } this.emitEvent('activate'); From 501d35259fd699978f8d3d0b6f2d50fd718ed1ce Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Thu, 16 Dec 2021 17:45:01 -0500 Subject: [PATCH 2/7] try hiding and showing focusable elements --- js/cell.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/js/cell.js b/js/cell.js index d34eaca..09c625d 100644 --- a/js/cell.js +++ b/js/cell.js @@ -1,3 +1,6 @@ + + + // Flickity.Cell ( function( window, factory ) { // universal module definition @@ -83,13 +86,31 @@ proto.renderPosition = function( x ) { this.parent.getPositionValue( adjustedX ) + ')'; }; +proto.showFocusables = function() { + console.log("running show", this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]')) + this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ + focusableEl.removeAttribute("aria-hidden") + focusableEl.setAttribute("tabindex", "0"); + }) +}; + proto.select = function() { this.element.classList.add('is-selected'); + this.showFocusables(); this.element.removeAttribute('aria-hidden'); }; +proto.hideFocusables = function() { + console.log("running hide", this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]')) + this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ + focusableEl.setAttribute("aria-hidden", "true"); + focusableEl.setAttribute("tabindex", "-1"); + }); +} + proto.unselect = function() { this.element.classList.remove('is-selected'); + this.hideFocusables(this.element); this.element.setAttribute( 'aria-hidden', 'true' ); }; From 8ea92047603358a232cc35a1c1522bfe01157bb6 Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Mon, 20 Dec 2021 13:52:43 -0500 Subject: [PATCH 3/7] make prev/next buttons more descriptive --- js/prev-next-button.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/prev-next-button.js b/js/prev-next-button.js index 5695942..a20e837 100644 --- a/js/prev-next-button.js +++ b/js/prev-next-button.js @@ -58,7 +58,7 @@ PrevNextButton.prototype._create = function() { // init as disabled this.disable(); - element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' ); + element.setAttribute( 'aria-label', this.isPrevious ? 'Previous Slide' : 'Next Slide' ); // create arrow var svg = this.createSVG(); From 65733d1b40025ceb8606c6a89a7ad8596d1fd81c Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Mon, 20 Dec 2021 14:40:13 -0500 Subject: [PATCH 4/7] initialize cells with focusables hidden --- js/cell.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/js/cell.js b/js/cell.js index 09c625d..8a22a3c 100644 --- a/js/cell.js +++ b/js/cell.js @@ -41,7 +41,11 @@ var proto = Cell.prototype; proto.create = function() { this.element.style.position = 'absolute'; - this.element.setAttribute( 'aria-hidden', 'true' ); + this.element.setAttribute('aria-hidden', 'true'); + this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ + focusableEl.setAttribute( 'aria-hidden', 'true' ); + focusableEl.setAttribute("tabindex", "-1"); + }); this.x = 0; this.shift = 0; this.element.style[ this.parent.originSide ] = 0; @@ -89,7 +93,7 @@ proto.renderPosition = function( x ) { proto.showFocusables = function() { console.log("running show", this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]')) this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ - focusableEl.removeAttribute("aria-hidden") + focusableEl.removeAttribute("aria-hidden"); focusableEl.setAttribute("tabindex", "0"); }) }; @@ -103,14 +107,14 @@ proto.select = function() { proto.hideFocusables = function() { console.log("running hide", this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]')) this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ - focusableEl.setAttribute("aria-hidden", "true"); + focusableEl.setAttribute( 'aria-hidden', 'true' ); focusableEl.setAttribute("tabindex", "-1"); }); } proto.unselect = function() { this.element.classList.remove('is-selected'); - this.hideFocusables(this.element); + this.hideFocusables(); this.element.setAttribute( 'aria-hidden', 'true' ); }; From eef681b221f35385b5feea8a3c500618f4d139fc Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Tue, 21 Dec 2021 12:31:31 -0500 Subject: [PATCH 5/7] allow keyboard navigation of slides even when focused on an element inside of a cell --- js/cell.js | 3 --- js/flickity.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/js/cell.js b/js/cell.js index 8a22a3c..d316f69 100644 --- a/js/cell.js +++ b/js/cell.js @@ -1,6 +1,3 @@ - - - // Flickity.Cell ( function( window, factory ) { // universal module definition diff --git a/js/flickity.js b/js/flickity.js index 100f9e2..3a5a9fc 100644 --- a/js/flickity.js +++ b/js/flickity.js @@ -832,7 +832,7 @@ proto.watchCSS = function() { proto.onkeydown = function( event ) { // only work if element is in focus var isNotFocused = document.activeElement && document.activeElement != this.element; - if ( !this.options.accessibility || isNotFocused ) { + if ( !this.options.accessibility && isNotFocused ) { return; } From c9047dee4c9aad15ad0f44f45bdbbe7ade552324 Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Tue, 21 Dec 2021 13:18:26 -0500 Subject: [PATCH 6/7] refactor functions to handle focusable elements inside a cell --- js/cell.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/js/cell.js b/js/cell.js index d316f69..005ea21 100644 --- a/js/cell.js +++ b/js/cell.js @@ -27,6 +27,8 @@ 'use strict'; +var FOCUSABLES_SELECTOR = 'a[href], button, input, textarea, select, details,[tabindex]' + function Cell( elem, parent ) { this.element = elem; this.parent = parent; @@ -39,10 +41,7 @@ var proto = Cell.prototype; proto.create = function() { this.element.style.position = 'absolute'; this.element.setAttribute('aria-hidden', 'true'); - this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ - focusableEl.setAttribute( 'aria-hidden', 'true' ); - focusableEl.setAttribute("tabindex", "-1"); - }); + _hideFocusables(this.element); this.x = 0; this.shift = 0; this.element.style[ this.parent.originSide ] = 0; @@ -87,31 +86,29 @@ proto.renderPosition = function( x ) { this.parent.getPositionValue( adjustedX ) + ')'; }; -proto.showFocusables = function() { - console.log("running show", this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]')) - this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ - focusableEl.removeAttribute("aria-hidden"); - focusableEl.setAttribute("tabindex", "0"); + function _showFocusables(element) { + element.querySelectorAll(FOCUSABLES_SELECTOR).forEach(focusableEl=>{ + focusableEl.removeAttribute('aria-hidden'); + focusableEl.setAttribute('tabindex', '0'); }) }; proto.select = function() { this.element.classList.add('is-selected'); - this.showFocusables(); + _showFocusables(this.element); this.element.removeAttribute('aria-hidden'); }; -proto.hideFocusables = function() { - console.log("running hide", this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]')) - this.element.querySelectorAll('a[href], button, input, textarea, select, details,[tabindex]').forEach(focusableEl=>{ - focusableEl.setAttribute( 'aria-hidden', 'true' ); - focusableEl.setAttribute("tabindex", "-1"); + function _hideFocusables(element) { + element.querySelectorAll(FOCUSABLES_SELECTOR).forEach(focusableEl=>{ + focusableEl.setAttribute('aria-hidden', 'true'); + focusableEl.setAttribute('tabindex', '-1'); }); } proto.unselect = function() { this.element.classList.remove('is-selected'); - this.hideFocusables(); + _hideFocusables(this.element); this.element.setAttribute( 'aria-hidden', 'true' ); }; From b3be83c38352a7d1d2cebc51d66af4377485e6fb Mon Sep 17 00:00:00 2001 From: Stephen Marsh Date: Tue, 21 Dec 2021 23:29:58 -0500 Subject: [PATCH 7/7] lint js --- js/cell.js | 32 ++++++++++++++++---------------- js/flickity.js | 15 +++++++++++---- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/js/cell.js b/js/cell.js index 005ea21..50bc2d8 100644 --- a/js/cell.js +++ b/js/cell.js @@ -27,7 +27,7 @@ 'use strict'; -var FOCUSABLES_SELECTOR = 'a[href], button, input, textarea, select, details,[tabindex]' +var FOCUSABLES_SELECTOR = 'a[href], button, input, textarea, select, details,[tabindex]'; function Cell( elem, parent ) { this.element = elem; @@ -40,8 +40,8 @@ var proto = Cell.prototype; proto.create = function() { this.element.style.position = 'absolute'; - this.element.setAttribute('aria-hidden', 'true'); - _hideFocusables(this.element); + this.element.setAttribute( 'aria-hidden', 'true' ); + _hideFocusables( this.element ); this.x = 0; this.shift = 0; this.element.style[ this.parent.originSide ] = 0; @@ -86,29 +86,29 @@ proto.renderPosition = function( x ) { this.parent.getPositionValue( adjustedX ) + ')'; }; - function _showFocusables(element) { - element.querySelectorAll(FOCUSABLES_SELECTOR).forEach(focusableEl=>{ + function _showFocusables( element ) { + element.querySelectorAll( FOCUSABLES_SELECTOR ).forEach( function( focusableEl ) { focusableEl.removeAttribute('aria-hidden'); - focusableEl.setAttribute('tabindex', '0'); - }) -}; + focusableEl.setAttribute( 'tabindex', '0' ); + } ); + } proto.select = function() { this.element.classList.add('is-selected'); - _showFocusables(this.element); + _showFocusables( this.element ); this.element.removeAttribute('aria-hidden'); }; - function _hideFocusables(element) { - element.querySelectorAll(FOCUSABLES_SELECTOR).forEach(focusableEl=>{ - focusableEl.setAttribute('aria-hidden', 'true'); - focusableEl.setAttribute('tabindex', '-1'); - }); -} + function _hideFocusables( element ) { + element.querySelectorAll( FOCUSABLES_SELECTOR ).forEach( function( focusableEl ) { + focusableEl.setAttribute( 'aria-hidden', 'true' ); + focusableEl.setAttribute( 'tabindex', '-1' ); + } ); + } proto.unselect = function() { this.element.classList.remove('is-selected'); - _hideFocusables(this.element); + _hideFocusables( this.element ); this.element.setAttribute( 'aria-hidden', 'true' ); }; diff --git a/js/flickity.js b/js/flickity.js index 3a5a9fc..25203c5 100644 --- a/js/flickity.js +++ b/js/flickity.js @@ -193,11 +193,18 @@ proto.activate = function() { // handle aria-live if ( this.options.autoPlay ) { // https://www.w3.org/TR/wai-aria-practices/examples/carousel/carousel-1.html - // Very importantly, if automatic rotation is turned on, the live region is disabled. If it were not, the page would be come unusable as announcements of the continuously changing content constantly interrupt anything else the user is reading. - this.element.setAttribute('aria-live', 'off') + // Very importantly, if automatic rotation is turned on, the live region is + // disabled. If it were not, the page would be come unusable as announcements + // of the continuously changing content constantly interrupt anything else the + // user is reading. + this.element.setAttribute( 'aria-live', 'off' ); } else { - // When automatic rotation is turned off, the carousel slide content is included in a live region. This makes it easier for screen reader users to scan through the carousel slides. When screen reader users activate the next or previous slide button , the new slide content is announced, giving users immediate feedback that helps them determine whether or not to interact with the content. - this.element.setAttribute('aria-live', 'assertive') + // When automatic rotation is turned off, the carousel slide content is included + // in a live region. This makes it easier for screen reader users to scan through + // the carousel slides. When screen reader users activate the next or previous + // button, the new slide content is announced, giving users immediate feedback that + // helps them determine whether or not to interact with the content. + this.element.setAttribute( 'aria-live', 'assertive' ); } }