I am designing a website for people with visual impairments with focus on mobile usage. For now I have some simple buttons like "play", "stop" and voice over reads what I have put between <button></button> tags. However, I want to make it as precise as possible because older people might be using it as well and include something like "press this button to play recording" but then it becomes really long and the button just looks awful. Is there a way to include this information in a button without having to display this text on a website? I can't find the answer and it's the first time I am doing a project with focus on blind people.
<button>Play</button>
I would like to have a button like this, but so the voice over reads a hidden tag like "press this button to play recording". How can I do that?
You can use aria-describedby for this:
.help-text {
color: gray;
font-size: 0.75rem;
}
<button
id="play-button"
aria-describedby="play-button-explanation"
>
Play
</button>
<span
id="play-button-explanation"
class="help-text"
>
Press this button to play recording.
</span>
When I use VoiceOver on this button, I get:
Play, button. Press this button to play recording. You are currently on a button ...
This would work even if the element wasn't being displayed, but I think it's good to have that context for people who can read it, too.
If you are not intending to display the label text on the screen, you can use aria-label
<button
id="play-button"
aria-label="Press this button to play recording"
>
Play
</button>
Related
On our (Vue.js v.2) webpage we have a section where you can swipe between cards, or by pressing "left" and "right" button. This swiping section is written with vanilla JS, and no fancy library. In order to have the adjacent visible when swiping, I need to have all of them visible in the DOM tree, but hiding them from the viewer. I have made it so all the unfocused cards are aria-hidden, and it works great when using ChromeVox. The problem is that when using VoiceOver, and I click the "right" button, and immediately tab down to the card, it will read out the card to the left, and also get trapped there since the card is aria-hidden. Code-wise the change from aria-visible to aria-hidden happens as soon as the button is pressed, but it seems like VoiceOver has already decided that when I am focusing on the "right" button, that the next element should be card 5 (for instance). If I wait around 1 or 2 seconds after clicking the "right" button, it will change the "next" element, and focus on the correct one (card 6) when I tab down. Is there any way to get around this, so it will focus only on the aria-visible element? Maybe a way to "force refresh" VoiceOver's stack of elements to read next? Maybe if I remove the message being read out when clicking the button, it will refresh immediately? I still haven't found of doing either of those things. I created a low quality flowchart to illustrate the problem better. What I want is for it to behave like ChromeVox.
I have tried several methods of getting this to work now, and it feels like it's a bug with VoiceOver. Some of the (desperate) attempts I've tried: setting tabindex=-1, role=presentation, changing the ID of "right" button dynamically as I navigate between cards, creating an empty div with a dynamic ID below the button, using aria-flowto, dynamically setting aria-describedby on the "next" element, and different variations between these and some other stuff I can't remember.
Eventually I found a solution that kinda works. I'm not very happy about it, but it's better than nothing. What I did was to make the title inside the card aria-hidden, and creating a currentHeader variable in store. I created an sr-only & aria-visible title above the swiping section, where the v-html points to the currentHeader variable. This way, the "next" element for the "right" button will always be the same element, but content will change after I click the button. It's not a perfect solution, and for some reason it makes VoiceOver "halt" when trying to go to the next element immediately after clicking the button, but at least the user won't read the wrong text and get trapped. Here's a pseudocode illustration of how I did it if my explaination was confusing:
// old solution // old swiping-section
<button id="left" /> <div v-for="element in elements" />
<button id="right" /> <h3 v-html="element.title" />
<swiping-section /> <p v-html="element.desc" />
</div>
// new solution // new swiping section
<button id="left" /> <div v-for="element in elements" />
<button id="right" /> <h3 aria-hidden="true" "v-html="element.title" />
<h3 class="sr-only" v-html="currentHeader" /> <p v-html="element.desc" />
<swiping-section /> </div>
If anyone finds a better way to do it, please post your solution.
Disclaimer: I understand that it is not valid HTML. I am trying to understand why is it not allowed?
W3C suggests that an interactive element like button or a mustn't contain another interactive element.
I could find a lot of resources mentioning this rule and some workarounds, also some resources related to how this impacts accessibility and screenreaders, but almost all of those resources talk about the fact that this is a requirement but do not explain why.
https://adrianroselli.com/2016/12/be-wary-of-nesting-roles.html
https://codepen.io/vloux/pen/wXGyOv
Nesting <a> inside <button> doesn't work in Firefox
https://github.com/dequelabs/axe-core/issues/601
I wasn't really able to find an explanation for why is it not allowed? does it lead to any usability problems?
This is a related question:
Why should interactive element not be used within an anchor?
The accepted answer is satisfactory but is not enough to make this rule a requirement. The described situation can be avoided using proper event handling.
Also, if nested interactive content is invalid, how are we supposed to have something like this:
A card which is clickable as a whole, and also has a clickable secondary CTA inside it.
I know a workaround would be to have a primary and secondary CTA inside the card, but shouldn't the above be allowed as well?
Here is a fiddle:
https://jsfiddle.net/t9qbwas5/2/
<button type="button" class="card">
The card itself is the primary CTA.
<br/>
<br/>
some important content to read through.
some important content to read through.
some important content to read through.
<div class="cta">
Secondary CTA
</div>
</button>
.cta {
padding: 4px;
background: #00a9a9;
color: white;
width: 80px;
margin: auto;
margin-top: 8px;
margin-bottom: 8px;
}
.card {
width: 200px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
In the above example, I am achieving this by using a clickable div inside the button, but that is not semantic (?) and also functionality wise, it is an interactive element inside another one. I am trying to understand that even if I use this workaround, is it fundamentally wrong to have nested interactive elements? is it a bad design/usability practice?
The answer is actually quite simple in principle. When you click on the interactive element inside another interactive element which function should you trigger?
In your example if I click on Secondary CTA should it fire the function for secondary CTA or should it fire the function for the card?
The fiddle below should demonstrate the problem, tab into the first button and press enter, then tab into the CTA and press Enter.
Obviously you could work around this but I think it demonstates the point.
$('.card').on('click', function(){
console.log("card");
});
$('.cta').on('click', function(){
console.log("cta");
});
.cta {
padding: 4px;
background: #00a9a9;
color: white;
width: 80px;
margin: auto;
margin-top: 8px;
margin-bottom: 8px;
}
.card {
width: 200px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" class="card">
The card itself is the primary CTA.
<br/>
<br/>
some important content to read through.
some important content to read through.
some important content to read through.
<div class="cta" tabindex="0">
Secondary CTA
</div>
</button>
This principle then continues through to Screen Readers and other Augmentative and alternative communication (AAC) devices.
Should they take into account the parent element when describing the child element? Should they allow the use of Space to activate if you nest a checkbox within a <button>, should Enter then affect only the button or both?
A card which is clickable as a whole, and also has a clickable secondary CTA inside it.
Although visually imaginable and technically possible, it's not accessible for assistive technologies, like screenreaders
Let's make a simple example:
<button>
Click for action 1
<button>Click for action 2</button>
</button>
The accessible name for the first <button> would be "Click for action1 Click for action 2". And if you define an aria-label="Click for action 1", then the inner button element would not be read at all.
If you really want to make a whole element clickable, you can perfectly use javascript and still be accessible
<div class="outer">
<button type="button" class="card">
The card itself is the primary CTA.
</button>
<br/>
<br/>
some important content to read through.
some important content to read through.
some important content to read through.
<button class="cta">
Secondary CTA
</button>
</div>
<script>
$(".outer").on("click", function() {$(".card").click()});
</script>
<style>
.outer {cursor: pointer}
</style>
With this example, you will correctly have two buttons rendered to screenreaders, the first one "The card itselfis the primary CTA" and the second one "Secondary CTA", while a mouse click on the whole card would lead the same action as the first button.
It's hard to answer "why" questions, because there are many factors to be considered and ultimately the reason is that the specification specifies it, but I'll give it a try.
When this behavior was spec'ed, this design style was not very common. A link was normally either a single image or a small portion of text. Take a look at this link to an article from the year 2000:
]
Only the title and the image are interactive. The rest is simple text.
Even today, this is not so common. Take also look at the Microsoft 365 pricing page:
Note how the card itself is not interactive, only the things inside it. You can see the primary CTA "Buy now" in the form of a button and the secondary CTAs in the form of hyperlinks.
Now, about your example: Is that card really a button? It might be subjective, but for me that's not a button. A button normally appears with a color scheme contrasting with the surrounding page. I would make the card a <div> and the secondary CTA a <button>.
However it might be confusing to users, as the card doesn't seem much interactive to me. Consider adding cursor: pointer to the <div> (beyond all the things necessary for accessibility)`.
I noted you tagged accessibility. I think this is not a great idea for people using screen readers, and I think most screen readers would have problems interpreting a button inside a button (if the browser accepted that at all).
I would use the "Microsoft 365 pricing page approach" instead. It's simpler and works well with HTML.
One important problem is related to event-capturing; if you click on a interactive element nested inside another interactive element(e.g. a select inside a clickable button) there would be an interference here and two case might happen depends on the browser;
case 1 is that both element will raise that event (e.g. click) event
case 2 is that parent element will capture the event and the nested element won't raise that event
in fact both cases will result in non-deterministic behavior;
This is not limited to click events actually, but click event is more tangible; Also the screen reader will fail to parse the markup; keyboard-interaction won't work as expected; try it in the snippet below:
del.addEventListener('click', function(){
console.log('deleting ...')
})
save.addEventListener('click', function(){
console.log('saving ...')
})
sel.addEventListener('change', function(){
console.log('changing to', sel.value)
})
<div id='del'>
delete
<button id='save'> save
<select id='sel'>
<option>foo</option>
<option>bar</option>
<select>
<input name='a' type='radio' />
<input name='a' type='radio' />
<input name='a' type='radio' />
</button>
</div>
I am trying to implement the notification component that will show the list of the items and will be opened by clicking on the notification icon on the fixed navigation bar on the top. I don't think it's a menu bar. Because the menu provides the actions that can be performed and it can also have a sub-menu.
https://www.w3.org/TR/wai-aria-practices/#menu
Can anyone let me know what should be the aria-role of such kind of components?
Below is the code sample. I will open the template dynamically by clicking on the notification icon button:-
<button aria-label="notifications">
<mat-icon class="mr-md">notifications</mat-icon>
</button>
<!-- Notification template -->
<div class="notifications__item">
Notifications
<li *ngFor="let notification of notifications" class="notifications__item">
<mat-icon class="notifications__icon material-icons-round">
{{ notification.icon }}
</mat-icon>
<div class="notifications__content">
<div [ngClass]="{ 'notifications__warn': notification?.type }">
<span>{{ notification.title }}</span>
</div>
<div>{{ notification.description }}</div>
</div>
<small class="notifications__caption">
{{ notification.duration }}
</small>
</li>
</div>
There are still a lot of things to consider that your example doesn't cover, so this isn't a complete answer, it is just pointing you to the relevant WAI-ARIA depending on what route you take.
The button
The first thing to consider is the button. You need to tell screen reader users what state it is currently in. For this we use aria-expanded to indicate whether the item it controls is currently opened or closed. (aria-expanded="true" for open, aria-expanded="false" for closed.)
At the same time we want to indicate what item this button controls (as the notification list isn't 'owned' by the element - for example if it was an <li> with a nested <ul> in a menu then the list would be 'owned' by it).
For this we would use aria-controls or aria-owns and point it to the ID of the element it controls. For the difference between them see this stack overflow post as a good explanation, in this example I would say it is aria-controls but yet again depends on your implementation and positioning in the DOM.
With regards to the button itself and where it sits in your menu, this is still considered navigation so it should sit within your <nav> element. However if this sits outside of your navigation along with say a 'help' and 'account' section you may consider those items part of a toolbar. (yet again I would say it doesn't apply here but something to look at)
Also it doesn't appear to be applicable here but if you include any links etc. within the 'popup' / modal that shows the notification list (i.e. a 'view all notifications' link), you should consider aria-haspopup="true"
The notification list
Right so we have a button pointed to the container (don't forget to give the container the relevant ID for aria-owns or aria-controls). Next what about the container itself?
Well in this example it appears that the container should be treated like a modal.
So for this reason you need to consider:-
trapping focus in the modal,
close with Escape,
returning focus to the button that activated it on close,
providing a close button that is accessible by keyboard,
a title for the modal (even if it is visually hidden)
What I would recommend is add some of the accessibility features above, try it with a screen reader and keyboard and see if it is easy to use. Once you have decided on your pattern ask some more questions on specific use case issues as the above is general guidance.
A few things to consider based on your markup
Additional things to consider from your example:-
use aria-hidden="true" on your icons, they don't add anything for screen readers (assuming your notification.title is descriptive).
For the notification title consider making it a relevant heading (<h2> - <h6> depending on position in document.
Don't forget to add some visually-hidden text that describes the warning level (I can see you have some form of colouring / distinction in [ngClass]="{ 'notifications__warn': notification?.type }" - expose the same info to screen readers.)
You currently have a <li> within a <div> - maybe change the outer <div> into an <ul> so it is semantically correct (<div class="notifications__item"> into <ul class="notifications__item">)
I hope the above is useful to set you on the right track, a lot to read but after reading the linked articles you should be able to make a better decision on what pattern you are using (as I didn't even mention making this a sub item within your menu) and can then ask some more questions on specific details you don't yet understand.
final thoughts / tips
test with a screen reader - this is the biggest tip I can give on working out how WAI-ARIA works and interacts with things.
Also if you are ever in doubt as to whether a WAI-ARIA attributre is applicable it is better to not include it.
Incorrect use or WAI-ARIA is actually worse than not including it at all so make sure you understand when to use an attribute reasonably well before implementing it. If I am ever unsure (as it still happens to me!) I tend to look at 2 or 3 examples of it in use and see if my pattern fits the examples I looked at.
I have this span element:
<span>Select the <strong>START</strong> <span class="icon-start></span> button.</span>
That is just a line that says "Select the START button" with a small icon after the word START.
Currently, Microsoft Narrator (in scan mode) reads "select the start" and then pauses at the icon and waits for the user to tell it to continue. I'm trying to make the Narrator read the entire line with zero interruptions or need for the user to tell it to continue.
So far here's what I've tried:
Add aria-hidden="true", tabindex="-1", and role="presentation to the icon's <span>
Wrapped the span in a <div> and gave it aria-hidden="true", tabindex="-1", and role="presentation
Added a role="heading" to the outer span -which works to make the Narrator reading everything uninterrupted, but he announces "Heading level 1" at first. If there's a way to prevent him from saying "heading" then that could work too.
Is there a role or aria attribute that tells the screen-reader to continue reading with no stopping?
It sounds like the screen reader is failing to parse your icon span — that span should have aria-hidden=“true”.
As a note, avoid making text items headings unless they are really meant to be headings. As a best practice, an accessible webpage should only have one <h1> element that essentially serves as a page title. And other headings below that single <h1> should be an <h2> and any headings below those should be <h3> etc. So, I'd advise you to be thoughtful about the heading level you choose if you were to go that route.
I solved this problem like this:
<span aria-label="Select the START button">
<span aria-hidden="true">
Select the <strong>START</strong> <span class="icon-start"></span> button.
</span>
</span>
This will announce the correct text and hide the presentation content from screen readers.
It sounds like Narrator has a similar behavior as VoiceOver on iOS devices. If you have an element that contains nested elements, VoiceOver often stops at each element even though you want to treat it as one element. You can typically work around this for iOS using an undocumented role="text". I don't know if it will work for Narrator too.
You'd have something like this:
<span role="text">Select the <strong>START</strong> <span aria-hidden="true" class="icon-start></span> button.</span>
Note that I also included aria-hidden on the icon, which #garret wisely recommended, so that it won't be read. You might not need aria-hidden when using role="text" but since that role is undocumented and technically not supported, you'd have to test it.
How do I change or add any code in order to tap and hold to show the pop up, currently once I press the button the pop up window will appear, I want to change it to tap and hold.
<div id="ScenePop1">
1
<div class="popup1">
<h2>Scene 1</h2>
<button id="store1">Store</button>
<button id="del1">Delete</button>
<a class="close1" href="#close"></a>
</div>
</div>
my code here: http://jsfiddle.net/oajt5p28/
This link come to elegant solution with the combination of jquery, jmobile and simpledialog2: http://www.raymondcamden.com/2012/05/23/Context-Menu-Example-with-jQuery-Mobile.
Or use could use html5 dialog element, and catch taphold event following this way:
How to detect a long touch pressure with javascript for android and iphone?