diff --git a/js/cell.js b/js/cell.js index d34eaca..50bc2d8 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,6 +41,7 @@ var proto = Cell.prototype; proto.create = function() { this.element.style.position = 'absolute'; this.element.setAttribute( 'aria-hidden', 'true' ); + _hideFocusables( this.element ); this.x = 0; this.shift = 0; this.element.style[ this.parent.originSide ] = 0; @@ -83,13 +86,29 @@ proto.renderPosition = function( x ) { this.parent.getPositionValue( adjustedX ) + ')'; }; + function _showFocusables( element ) { + element.querySelectorAll( FOCUSABLES_SELECTOR ).forEach( function( focusableEl ) { + focusableEl.removeAttribute('aria-hidden'); + focusableEl.setAttribute( 'tabindex', '0' ); + } ); + } + proto.select = function() { this.element.classList.add('is-selected'); + _showFocusables( this.element ); this.element.removeAttribute('aria-hidden'); }; + 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 ); this.element.setAttribute( 'aria-hidden', 'true' ); }; diff --git a/js/flickity.js b/js/flickity.js index 5fe52eb..25203c5 100644 --- a/js/flickity.js +++ b/js/flickity.js @@ -190,6 +190,22 @@ 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 + // 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'); @@ -823,7 +839,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; } 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();