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

Prevent using dynamic accessible names for navigation btns #650

Open
wants to merge 3 commits into
base: master-2.0
Choose a base branch
from
Open
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
25 changes: 14 additions & 11 deletions content/carousels/examples/carousel.js
Expand Up @@ -85,9 +85,9 @@ var myCarousel = (function() {
// Add Play/Pause button if the slider is animated

if (settings.startAnimated) {
li.innerHTML = '<button data-stop=true><span class="visuallyhidden">Stop Animation </span></button>';
li.innerHTML = '<button data-action="stop" aria-label="Automatic animation" aria-pressed="false"><span>▶</span></button>';
} else {
li.innerHTML = '<button data-start=true><span class="visuallyhidden">Start Animation </span></button>';
li.innerHTML = '<button data-action="start" aria-label="Automatic animation" aria-pressed="true"><span>■</span></button>';
}

slidenav.appendChild(li);
Expand All @@ -97,10 +97,9 @@ var myCarousel = (function() {

// Add button for each slide if slidenav = true
for (var i = slides.length - 1; i >= 0; i--) {
var klass = (i===0) ? 'class="current" ' : '';
var kurrent = (i===0) ? ' <span class="visuallyhidden">(Current Slide)</span>' : '';
var activeCurrent = (i===0) ? 'aria-current="true" ' : '';

li.innerHTML = '<button '+ klass +'data-slide="' + i + '"><span class="visuallyhidden">News</span> ' + (i+1) + kurrent + '</button>';
li.innerHTML = '<button '+ activeCurrent +'data-slide="' + i + '"><span class="visuallyhidden">News</span> ' + (i+1) + '</button>';
slidenav.appendChild(li);
}
}
Expand Down Expand Up @@ -133,7 +132,7 @@ var myCarousel = (function() {
slides[0].parentNode.addEventListener('transitionend', function (event) {
var slide = event.target;
removeClass(slide, 'in-transition');
if (hasClass(slide, 'current')) {
if (slide.getAttribute('aria-current') === 'true') {
// Also, if the global setFocus variable is set
// and the transition ended on the current slide,
// set the focus on this slide.
Expand Down Expand Up @@ -236,11 +235,11 @@ var myCarousel = (function() {
if(settings.slidenav) {
var buttons = carousel.querySelectorAll('.slidenav button[data-slide]');
for (var j = buttons.length - 1; j >= 0; j--) {
buttons[j].className = '';
buttons[j].setAttribute("aria-current", "false");
buttons[j].innerHTML = '<span class="visuallyhidden">News</span> ' + (j+1);
}
buttons[new_current].className = "current";
buttons[new_current].innerHTML = '<span class="visuallyhidden">News</span> ' + (new_current+1) + ' <span class="visuallyhidden">(Current Slide)</span>';
buttons[new_current].setAttribute("aria-current", "true");
buttons[new_current].innerHTML = '<span class="visuallyhidden">News</span> ' + (new_current+1);
}

// Set the global index to the new current value
Expand Down Expand Up @@ -297,7 +296,9 @@ var myCarousel = (function() {
settings.animate = false;
animationSuspended = false;
var _this = carousel.querySelector('[data-stop], [data-start]');
_this.innerHTML = '<span class="visuallyhidden">Start Animation </span>▶';
_this.innerHTML = '<span aria-hidden="true">▶</span>';
_this.setAttribute('aria-label', "Automatic animation");
_this.setAttribute("aria-pressed", "false");
_this.removeAttribute('data-stop');
_this.setAttribute('data-start', 'true');
}
Expand All @@ -310,7 +311,9 @@ var myCarousel = (function() {
nextSlide();
}, 5000);
var _this = carousel.querySelector('[data-stop], [data-start]');
_this.innerHTML = '<span class="visuallyhidden">Stop Animation </span>■';
_this.innerHTML = '<span aria-hidden="true">■</span>';
_this.setAttribute('aria-label', "Automatic animation");
_this.setAttribute("aria-pressed", "true");
_this.setAttribute('data-stop', 'true');
_this.removeAttribute('data-start');
}
Expand Down
7 changes: 3 additions & 4 deletions content/carousels/functionality.md
Expand Up @@ -118,7 +118,7 @@ if (announceItem) {

Display buttons for each item in the carousel and highlight the current item. This allows users to get an overview of the carousel content, where they are in the sequence and will enable them to navigate directly to any item.

The list with buttons in the example below is added using JavaScript, with a number on the button that corresponds to the carousel item. The buttons are numbered matching the corresponding carousel items. The button for the active carousel item is highlighted both visually, and by using text that is visually hidden (for screen readers).
The list with buttons in the example below is added using JavaScript, with a number on the button that corresponds to the carousel item. The buttons are numbered matching the corresponding carousel items. The button for the active carousel item is marked as active using `aria-current="true"`, because <a href="https://adrianroselli.com/2020/12/be-careful-with-dynamic-accessible-names.html">assistive technologies such as screen readers have trouble with dynamic accessible names</a>. As a consequence, changing the programmatic button label from, e.g. "News 1" to "News 1 (Current Slide)" is way less robust than using an ARIA attribute than is meant to convey interface state in the first place (like the beforementioned `aria-current`).

{% include ednote.html note="The following paragraph was show in a side column before." %} See the [carousel styling](/tutorials/carousels/styling/) page for more information on how to highlight the active carousel item in an accessible way.

Expand All @@ -129,9 +129,8 @@ The list with buttons in the example below is added using JavaScript, with a num
~~~html
<ul class="slidenav">
<li>
<button class="current" data-slide="0">
<button class="current" data-slide="0" aria-current="true">
<span class="visuallyhidden">News</span> 1
<span class="visuallyhidden">(Current Slide)</span>
</button>
</li>
<li>
Expand Down Expand Up @@ -168,7 +167,7 @@ The list with buttons in the example below is added using JavaScript, with a num
color: #fff;
}

.slidenav button.current {
.slidenav button[aria-current="true"] {
border-radius: .5em;
background-color: #fff;
color: #333;
Expand Down
4 changes: 3 additions & 1 deletion content/carousels/styling.md
Expand Up @@ -50,6 +50,8 @@ Since the navigation buttons added to the carousel are typically small, it is im

In the following example, a filled square is used for buttons associated with items currently not shown. The button for the item shown has rounded corners and inverted colors. When users hover over these buttons using a mouse or focus them using a keyboard, their border is dotted.

The state of the button is conveyed by `aria-current="true"`. Using a CSS attribute selector, it is possible to address the button in its active state.

{::nomarkdown}
{% include box.html type="start" title="Example" class="example" %}
{:/}
Expand All @@ -68,7 +70,7 @@ In the following example, a filled square is used for buttons associated with it
color: #fff;
}

.slidenav button.current {
.slidenav button[aria-current="true"] {
border-radius: .5em;
background-color: #fff;
color: #333;
Expand Down
27 changes: 15 additions & 12 deletions content/carousels/working-example.md
Expand Up @@ -328,9 +328,9 @@ var myCarousel = (function() {
var li = document.createElement('li');

if (settings.startAnimated) {
li.innerHTML = '<button data-action="stop"><span class="visuallyhidden">Stop Animation </span></button>';
li.innerHTML = '<button data-action="stop" aria-label="Automatic animation" aria-pressed="false"><span>▶</span></button>';
} else {
li.innerHTML = '<button data-action="start"><span class="visuallyhidden">Start Animation </span></button>';
li.innerHTML = '<button data-action="start" aria-label="Automatic animation" aria-pressed="true"><span>■</span></button>';
}

slidenav.appendChild(li);
Expand All @@ -339,10 +339,9 @@ var myCarousel = (function() {
if (settings.slidenav) {
forEachElement(slides, function(el, i){
var li = document.createElement('li');
var klass = (i===0) ? 'class="current" ' : '';
var kurrent = (i===0) ? ' <span class="visuallyhidden">(Current Item)</span>' : '';
var activeCurrent = (i===0) ? 'aria-current="true" ' : '';

li.innerHTML = '<button '+ klass +'data-slide="' + i + '"><span class="visuallyhidden">News</span> ' + (i+1) + kurrent + '</button>';
li.innerHTML = '<button '+ activeCurrent +'data-slide="' + i + '"><span class="visuallyhidden">News</span> ' + (i+1) + '</button>';
slidenav.appendChild(li);
});
}
Expand Down Expand Up @@ -374,7 +373,7 @@ var myCarousel = (function() {
slides[0].parentNode.addEventListener('transitionend', function (event) {
var slide = event.target;
removeClass(slide, 'in-transition');
if (hasClass(slide, 'current')) {
if (slide.getAttribute('aria-current') === 'true') {
if(setFocus) {
slide.setAttribute('tabindex', '-1');
slide.focus();
Expand Down Expand Up @@ -448,11 +447,11 @@ var myCarousel = (function() {
if(settings.slidenav) {
var buttons = carousel.querySelectorAll('.slidenav button[data-slide]');
for (var j = buttons.length - 1; j >= 0; j--) {
buttons[j].className = '';
buttons[j].innerHTML = '<span class="visuallyhidden">News</span> ' + (j+1);
buttons[j].setAttribute("aria-current", "false");
buttons[j].innerHTML = '<span class="visuallyhidden">News</span> ' + (j+1);
}
buttons[new_current].className = "current";
buttons[new_current].innerHTML = '<span class="visuallyhidden">News</span> ' + (new_current+1) + ' <span class="visuallyhidden">(Current Item)</span>';
buttons[new_current].setAttribute("aria-current", "true");
buttons[new_current].innerHTML = '<span class="visuallyhidden">News</span> ' + (new_current+1);
}

index = new_current;
Expand Down Expand Up @@ -496,7 +495,9 @@ var myCarousel = (function() {
settings.animate = false;
animationSuspended = false;
_this = carousel.querySelector('[data-action]');
_this.innerHTML = '<span class="visuallyhidden">Start Animation </span>▶';
_this.innerHTML = '<span aria-hidden="true">▶</span>';
_this.setAttribute('aria-label', "Automatic animation");
_this.setAttribute("aria-pressed", "false");
_this.setAttribute('data-action', 'start');
}

Expand All @@ -505,7 +506,9 @@ var myCarousel = (function() {
animationSuspended = false;
timer = setTimeout(nextSlide, 5000);
_this = carousel.querySelector('[data-action]');
_this.innerHTML = '<span class="visuallyhidden">Stop Animation </span>■';
_this.innerHTML = '<span aria-hidden="true">■</span>';
_this.setAttribute('aria-label', "Automatic animation");
_this.setAttribute("aria-pressed", "true");
_this.setAttribute('data-action', 'stop');
}

Expand Down