How to hide a text and make it accessible by screen reader? - html

I have a simple text that gets updated on an action and I want that to be announced by the screen reader. But I don't want that text to be visible on the web page. I tried display: none and visibility: hidden, but seems like they are not accessible by the screen reader softwares. I found a way to make this work - that is by absolute positioning the element all the way with negative 999999 value which will make it off screen and hidden from the webpage. I am not really a fan of this solution. Is there a more elegant way to achieve this?
<span class="aria-invisible" aria-live="polite">5 selections have been made.</span>
.aria-invisible {
display: none; //either of these two attributes
visibility: hidden;
}

A better solution to the bootstrap "sr-only" class.
There are numerous problems with the Bootstrap "sr-only" class.
First of all you will see from this discussion that a negative margin can cause issues on VoiceOver.
Secondly you must account for words wrapping one per line as screen readers do not read line breaks
Finally clip has been deprecated.
To fix point 1 simply don't add a negative margin.
To fix point 2 add white-space: no-wrap to ensure words do not end up 'one per line' and cause words to get smushed together.
To fix point 3 we add clip-path: inset(50%) as this clips to a 0px square, we keep clip as at the moment this has great coverage, clip-path is used to future-proof your solution.
Please find below a much more robust class, as of yet I have not found a screen reader / browser combo that does not work as expected with this.
I have this class on a few different forums being tested, so far so good but if someone can find a problem with it please let me know as I will be submitting it everywhere.
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<p>visible text <span class="visually-hidden">hidden text</span></p>

I did encounter this problem in the past.
Bootstrap has this sweet class sr-only that actually hides the content on the webpage but is accessible by the screen readers. You can check this link
Moreover, if you are not using bootstrap, you can simply implement the class yourself in your code.
.aria-invisible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
<span class="aria-invisible">5 selections have been made. </span>
I hope this helps.

Using aria-label attributes is the way to do (example below)
Is there a more elegant way to achieve this?
Do not hide the element. Yes. I am not answering your question, but I am addressing the problem.
Screenreaders are only a subpart of assistive technologies used by a small part of people targeted by accessibility guidelines.
Imagine using a screen magnifier for instance where you do not have a global view on the full screen. Imagine having some cognitive disabilities which makes difficult for you to count or remember elements.
If you do consider that an information is important for blind people, then it's surely is for them AND for other people.
Now, instead of it being a long text, it can be a small counter with appropriate aria labelling:
<div role="status" aria-live="polite" aria-label="5 selections have been made.">
5 selections
</div>

I had the same problem with the text being out of position with the visually hidden class mentioned above. Some small changes to the class fixed this issue for me
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: auto;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}

Related

Read more Links do not have descriptive text

I have a page which contains a list of items. Each item contains a 'Read more' link that points to a different page. But when I run the lighthouse tool on that page, it complains that links do not have a descriptive text. Now I cannot change the Read more text here.
Read more
Read more
Read more
Is there any other way to resolve this?
I had the same problem.
Attribute aria-label does not works, lighthouse still display issue.
I fixed it by adding hidden detailed text inside link.
Read more<span class="screen-reader-text">Details</span>
<style>
.screen-reader-text {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
-webkit-clip-path: inset(50%);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute !important;
width: 1px;
word-wrap: normal !important;
word-break: normal;
}
</style>
Note about .screen-reader-text: this CSS grabbed from wordpress default Twenty Seventeen theme.
Yes, you can use aria-label in order to provide more descriptive text to Assistive Tech such as a screen reader.
Read more
Assistive tech will read the contents of the aria-label out instead of "Read More".
Bear in mind that the text you enter should be enough to know where the link will take you and not context dependent (the link text should make sense on its own) if possible.

Accessibility with unseen label

I have a couple things on my sight where it’s seen to normal sight users. But I have a hidden label that has css styling to hide it from sight users ie height 0 weight 0 line hight 0 etc. the issue is that when dragging a finger on iPhones neither options are accessible. I’m trying to find a way to make the visible content still say nothing but screen readers say something else. Thanks.
EDIT
I apologize I was very drunk when I wrote that.
I’m just going to ask a whole new question. Thanks for everyone who tried to help lol
Setting width/height to 0 isn't a correct way to hide something for sighted users while keeping it readable for screen reader users.
Entire questions and articles on the web are devoted to explain how exactly to do it the right way.
The basic technique is called visually hidden text.
If you are using a framework, you already have predefined CSS classes like .sr_only, .visually-hidden, etc.
Here's for example the one I use, taken from knacss with a minor change of my own:
.visually-hidden {
position: absolute !important;
border: 0 !important;
height: 1px !important;
/* width: 1px !important; /*Disabled because of JFW+firefox bug, all words are concatenated */
padding: 0 !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
left: -2px !important;
top: auto !important;
}

How to fix "name property of a focusable element must not be null"

Im using UI accessibility insights to test my react app on windows.
I get a lot of "name property of a focusable element must not be null" and a lot of "focusable sibling elements must not have the same name and localizedcontroltype" errors.
I have no clue how to fix these errors, and i can't find anything decent in google.
To solve the first one i tries adding the name ot id or aria-label attr to the buttons, but nothing changed. Any idea on how to get around this?
example of button that's failing the test:
<button
className={css.button}
onClick={() => setIsTipOpen(!isTipOpen)}
>
<Icon type='Girl'/>
</button>
Screen readers need to be able to announce something to their users. The problem with an icon in a button is there is no way to programatically determine the button text.
One way to fix this is to add an aria-label to the button element.
Another way to do this is visually hidden text. This is text that is not visible but still accessible for screen readers.
In the below example you will see there is no visible text (as it should be an icon inside) but a screen reader user would hear "button, A girl", just make sure the button text makes sense as an action (so I assume this button would be "gender female" or something).
Visually hidden text is preferable as for people who use text only browsers the text within the button is still displayed (as CSS is ignored in a text only browser) and support is 100% all the way back to IE6 (whereas aria-label support is not 100%).
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
.your-button-classes{
width: 100%;
height: 50px;
background-color: #333;
}
<button
class="your-button-classes"
onClick="someAction"
>
<Icon type='Girl'/>
<span class="visually-hidden">A girl</span>
</button>

How to make an HTML link displayed as an icon accessible?

I have some links that are displayed only as icons (don't have any text):
.icon-link {
background-image: url(...);
}
How do I make this link accessible for people not accessing the website visually (using screen readers)?
I see few approaches possible, but cannot find any resources on which one is actually right, or best supported:
Adding aria-label attribute on <a>
Adding title attribute on <a>
Adding text inside <a> and then hiding it visually with CSS
Short Answer
Use visually hidden text.
Longer answer
Adding a title offers very little in the way of accessibility. Here is an interesting article on the subject, it links out to further information.
So with that in mind that leaves option 1 and 3 as viable options, however the best compatibility is using visually hidden text.
You see support for aria-label is surprisingly low (scroll down the page to the aria-label section), whereas visually hidden text using the example below will cover browsers all the way back to IE6!
I answered about the most robust way to do visually hidden text (with explanations of why I do each item) in this stack overflow answer. I have copied the same below just for your reference.
For your use case just add a span within the link with the visually-hidden class.
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<p>visible text <span class="visually-hidden">hidden text</span></p>
Added Bonus of visually hidden text
As #QuentinC pointed out in the comments there is another great reason to use visually hidden text over the other methods.
If a user uses a browser that does not support CSS (there are still a few text only browsers that people use) then the text will be displayed.
Always reconsider using visually hidden text. Not because it is bad, but because it leads to false belief that the solution is accessible for everyone when it's only accessible to a small subset of the population.
Using hidden text won't help people not using screenreaders to know the action performed by the link when meaning of the image might be difficult. Screenreader users are a small part of the population targetted by accessibility rules.
Regarding the title attribute, it won't hurt anyone to improve accessibility if you inform standard mouse users of the action performed by the link. It will help them. If a title attribute is not always recommended, you might opt for any solution that would show the text when the element is focused with the mouse or with the keyboard.
You also must remember that not showing text will not help people using voice navigation or eye tracking device.
When using the title attribute, you must always consider using it conjointly with the aria-label attribute, and not replacing one with the other.
EDIT: simple example
.icon-link {
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='50px' width='120px'><text x='0' y='20' font-size='20'>🍕</text></svg>");
content: '';
background-repeat: no-repeat;
width: 20px;
height: 30px;
display: inline-block;
}
#pizza {
position: absolute;
display:none;
background:white;
color: black;
margin-left: 20px;
}
a:focus #pizza, a:hover #pizza {
display: block;
}
<div id="pizza">Pizza!</div>

How to accessibly hide table caption element?

Is there any way to accessibly hide a table caption without breaking how screen readers interpret the rest of the table? Hiding a <caption> with typically recommended styles for hiding an element visually breaks the behavior of VoiceOver, causing it to skip the last row in the table when reading through linearly using the "next" keystroke. (It is possible to force VoiceOver into the last row by explicitly navigating down a column, but that requires the user to know to do this.)
I recognize this may be a bug in VoiceOver itself, but if there's a clean workaround, that would be ideal since WCAG requires accessibility with actually available assistive technology.
Here's a minimalist example to demonstrate:
Update: The style rules below are the standard rules used in the Magento framework to visually hide elements while leaving them accessible to screen readers. The key rule causing the VoiceOver behavior is the position: absolute; however, if this is simply removed, the layout flow is impacted.
caption {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
<table>
<caption>Table of Fruits</caption>
<thead>
<tr>
<th>Fruit</th>
<th>Color</th>
</tr>
</thead>
<tbody>
<tr>
<td>Apple</td>
<td>Red</td>
</tr>
<tr>
<td>Pear</td>
<td>Green</td>
</tr>
</tbody>
</table>
<p>Voiceover will jump straight from "Red" in prior table to this paragraph, skipping the last row.</p>
Well... I see that you are using a caption tag just for accessibility, which means that you don't want to show it visually; I suggest simply not using it and instead use aria-label in your table tag, which will make it accessible for screen readers.
<table aria-label="Table of fruits"> ... </table>
Read the first paragraph of this page to get an idea about aria-label usage.
A Few Discrepancies
<th> Needs <tr> as a Parent to be Valid
The OP (Original Post) code didn't have a <tr> in the <thead> which could be the reason why the last <tr> is being skipped. Invalid HTML has a tendency to confuse applications such as VoiceOver.
Three Methods
Disclaimer: Not Tested - Caveat Emptor
The following demo has three <table>s with identical HTML markup, CSS rules, and text content. Each <caption> has a different .class that employ a specific method of hiding content:
.clipped - Assuming that clipping content needs a length: clip: rect(0, 0, 0, 0); looks dubious. Some other properties and values looked to be ad-hoc as well so try replacing caption {...} rule set with:
.clipped {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
.transparent - This is simply assigning a transparent color to text. Height is still there (which VoiceOver requires), but it can be adjusted if needed. opacity: 0 is also an option, but there are certain situations in which opacity: 0 is considered the same as visibility: hidden (which VoiceOver ignores).
.transparent {
color: rgba(0, 0, 0, 0);
}
.collapsed - This collapses an element's content but retains its height so VoiceOver might recognize it.
.collapsed {
visibility: collapse;
}
Demo
table {
border: 1px solid #000;
table-layout: fixed;
border-collapse: collapse;
min-width: 200px;
}
th,
td {
width: 50%;
border: 1px solid #000;
}
.clipped {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
.transparent {
color: rgba(0, 0, 0, 0);
}
.collapsed {
visibility: collapse;
}
<table>
<caption class='clipped'>CAPTION</caption>
<thead><tr><th>TH</th><th>TH</th></tr></thead>
<tbody><tr><td>TD</td><td>TD</td></tr>
<tr><td>TD</td><td>TD</td></tr></tbody>
</table>
<table>
<caption class='transparent'>CAPTION</caption>
<thead><tr><th>TH</th><th>TH</th></tr></thead>
<tbody><tr><td>TD</td><td>TD</td></tr>
<tr><td>TD</td><td>TD</td></tr></tbody>
</table>
<table>
<caption class='collapsed'>CAPTION</caption>
<thead><tr><th>TH</th><th>TH</th></tr></thead>
<tbody><tr><td>TD</td><td>TD</td></tr>
<tr><td>TD</td><td>TD</td></tr></tbody>
</table>
<p>The <abbr title="Original Post"><b>OP</b></abbr> code didn't have a <code><tr></code> in the <code><thead></code> which could be the reason why the last <code><tr></code> is being skipped.</p>
You do not want to hide <caption> visually.
A bit late to the party but I feel an urge to highlight the importance of avoiding to treat disabled users differently. That simply means to prefer solutions that generally work for all users out-of-the-box. Try not reinvent the wheel or over-complicating with screen-reader-only solutions, just leave it as it is and provide the same content to all users. In this specific case I'd either make caption visible for all, or for no one. Why not to show caption to all users? If the table content is so complex that it needs to be described to a screen reader then you might ask yourself whether it's time to optimize the actual content for all users, or describing it to all users, not only screen readers. Because when you make something accessible for a screen-reader-only then likely you made it inaccessible for many other users. Hence you didn't make it accessible.
The worst here is the assumption that only screen-reader users will require some solution. But there are no screen-reader users. Such assumptions about the users should never be made. Accessibility is not screen-readers. There are so many other disabilities, use-cases and assistive technology. Many screen reader users want to share the content and if they "see" (or hear) something that their friend cannot access, it will look weird. Bear in mind also that many screen reader users are not blind. They might be using zooming and will also find confusing the fact that screen reader is reading the content which appears not to exist on the page.
Yes, there are always some exceptions, such would be "skip links" and similar, but all such practices are common and something users are familiar to. Those are usually well thought through for all user groups. Hence "skip links" would become visible when focused etc.
Since position: absolute; is what causes the problem, a pragmatic solution is to skip it and use margin-top: -1px; instead. Tested and verified i Chrome + voiceover.
.clipped {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
I'm a little late to this discussion, but there is a solution that hasn't been mentioned yet.
You can use the summary attribute on the table element. The summary attribute will not affect your layout, but will be read by the screen reader.
If you use the caption element or aria-label attribute, they will override the summary attribute. So just use summary by itself.
<table summary="Table of Fruits">