avatarDaniel Berryhill

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

7116

Abstract

lass="hljs-attribute">id</span>=<span class="hljs-string">"btnSearch"</span> <span class="hljs-attribute">type</span>=<span class="hljs-string">"submit"</span>>Search</button></pre></div><p id="81e1">And here’s what the browser renders:</p><figure id="bdeb"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*6wdeQFryd0wx3HdhiHvaDQ.png"><figcaption></figcaption></figure><p id="8938">Redundant, right? You probably even winced at it. Fortunately, we don’t have to do this to have an accessible textbox.</p><p id="dbc2">JAWS check (on Chrome): When the textbox receives focus, it reads the following: “Search colon Edit. Type in text.”</p><p id="7170">So, we want to hide the label; but we also want the screen reader to read the same way.</p><p id="65bc">We can use a hidden label here because the context of the two controls sufficiently communicates the purpose of each. In this case, the context is the proximity of the textbox and the Search button to one another.</p><blockquote id="5683"><p><i>Guideline: Use hidden labels only if the context of the control is visually apparent</i></p></blockquote><h2 id="6eab">3. Use a “Screen Reader Only” Label</h2><p id="3c56">You can create a CSS class to take the <code><label></code> element out of the page visually but retain it for screen readers. Take a look at this CSS declaration:</p><div id="2e0c"><pre><span class="hljs-selector-class">.screenReaderOnly</span> { <span class="hljs-attribute">border</span>: <span class="hljs-number">0</span>; <span class="hljs-attribute">clip</span>: <span class="hljs-built_in">rect</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>); <span class="hljs-attribute">height</span>: <span class="hljs-number">1px</span>; <span class="hljs-attribute">margin</span>: -<span class="hljs-number">1px</span>; <span class="hljs-attribute">overflow</span>: hidden; <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>; <span class="hljs-attribute">position</span>: absolute; <span class="hljs-attribute">width</span>: <span class="hljs-number">1px</span>; }</pre></div><p id="9d04">This style will completely remove any element from view. Now, for any <code><label></code> element you want to hide, give it the <code>.screenReaderOnly</code> class and it will still be consumed by screen readers.</p><p id="dcf1">Look at this markup:</p><div id="1193"><pre><label <span class="hljs-keyword">for</span>=<span class="hljs-string">"txtSearch"</span> <span class="hljs-keyword">class</span>="<span class="hljs-symbol">screenReaderOnly</span>"><span class="hljs-symbol">Search: </span></<span class="hljs-symbol">label</span>> <<span class="hljs-symbol">input</span> <span class="hljs-symbol">id</span>="<span class="hljs-symbol">txtSearch</span>" <span class="hljs-symbol">type</span>="<span class="hljs-symbol">text</span>"/> <<span class="hljs-symbol">button</span> <span class="hljs-symbol">id</span>="<span class="hljs-symbol">btnSearch</span>" <span class="hljs-symbol">type</span>="<span class="hljs-symbol">submit</span>"><span class="hljs-symbol">Search</span></<span class="hljs-symbol">button</span>></pre></div><p id="63f7">With the CSS class properly implemented, the browser renders this:</p><figure id="8f6f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*Y6mlvMORR2zuTETF4yygyA.png"><figcaption></figcaption></figure><p id="a58a">JAWS check: “Search colon Edit. Type in text.”</p><p id="2e03">Nice! So, when the textbox receives focus, the screen reader will read the hidden label as if it were not hidden.</p><blockquote id="f5e8"><p><i>Side Notes:</i></p></blockquote><blockquote id="e563"><p><i>If your site uses the Bootstrap framework, this is already built-in. For Bootstrap 4 and earlier versions, you can use the <code>.sr-only</code> class and for Bootstrap 5, use the <code>.visually-hidden</code> class.</i></p></blockquote><blockquote id="1cb9"><p><i>If you want to do the reverse (make an element visually available but hide it from screen readers), add the following HTML attribute to the element: <code>aria-hidden="true"</code>. This is handy for decorative elements such as icons that don’t represent essential information.</i></p></blockquote><blockquote id="ce14"><p><i>Do not use <code>{display: none}</code> or <code>{visibility: hidden}</code> to hide labels because they will hide them from screen readers.</i></p></blockquote><h1 id="14dc">A Word About ARIA Attributes</h1><p id="efea"><b>ARIA attributes</b> have no visual effect on their elements; but they make a world of difference for those with screen readers. The attributes <code>aria-label</code> and <code>aria-labelledby</code> allow a user with a screen reader to consume input controls as if they had traditional labels.</p><p id="f7d8">As I said earlier, be careful with hidden labels — especially ARIA attribute labels. It’s important to understand their purpose. These attributes aren’t just an alternative to the <code>title</code> attribute.</p><p id="923e">As Attila Vágó stated in his article <a href="https://hmh.engineering/accessibility-is-hard-careful-with-them-aria-labels-5e1c6c669b61">Accessibility is Hard — Careful With Them ARIA Labels!</a>, “an <code>aria-label</code> does not mean it provides additional information. It does exactly the opposite. It overrides the meaning of your component entirely.”</p><p id="3e08">To demonstrate that, take a look at this markup where I use an explicit label and an <code>aria-label</code> attribute:</p><div id="e3d7"><pre><<span class="hljs-keyword">label</span> <span class="hljs-keyword">for</span>=<span class="hljs-string">"txtLastName"</span>>Last Name: </<span class="hljs-keyword">label</span>> <<span class="hljs-keyword">input</span> id=<span class="hljs-string">"txtLastName"</span> aria-<span class="hljs-keyword">label</span>=<span class="hljs-string">"Surname"</span> <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span> /></pre></div><p id="213b">JAWS Check: “Surname Edit. Type in text.”</p><p id="ecd6">Yikes! As you can see, the traditional label’s text (“Last Name: ”) is completely ignored and the <code>aria-label</code> overrides it.</p><blockquote id="4c1b"><p><i>Guideline: ARIA labels should only be used if traditional labels are not an option.</i></p></blockquote><h2 id="3f38">4. Use an Aria-label Attribute</h2><p id="41f4">Use the <code>aria-label</code> attribute to give the hidden label some text like you would a traditional label. You can enter any text as the value for <code>aria-label</code>, keeping in mind it should properly describe the control and be consistent with the text of other <code><label></code> elements on the site.</p><div id="3f7e"><pre><input <span class="hljs-attribute">id</span>=<span class="hljs-string">"txtSearch"</span> <span class="hljs-attribute">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attribute">aria-label</spa

Options

n>=<span class="hljs-string">"Search"</span> /> <button <span class="hljs-attribute">id</span>=<span class="hljs-string">"btnSearch"</span> <span class="hljs-attribute">type</span>=<span class="hljs-string">"submit"</span>>Search</button></pre></div><p id="d24c">JAWS check: “Search Edit. Type in text.”</p><h2 id="2352">5. Use an Aria-labelledby Attribute</h2><p id="b6b5">The <code>aria-labelledby</code> attribute is really handy when there is an element on the page that can serve as a label for your control. Add the <code>aria-labelledby</code> attribute to the control and set its value to the <code>id</code> attribute of the element serving as the label.</p><p id="5ef4">Look at this example:</p><div id="8e76"><pre><input <span class="hljs-attribute">id</span>=<span class="hljs-string">"txtSearch"</span> <span class="hljs-attribute">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attribute">aria-labelledby</span>=<span class="hljs-string">"btnSearch"</span> /> <button <span class="hljs-attribute">id</span>=<span class="hljs-string">"btnSearch"</span> <span class="hljs-attribute">type</span>=<span class="hljs-string">"submit"</span>>Search</button> </pre></div><p id="5e34">JAWS check: “Search Edit. Type in text.”</p><blockquote id="109a"><p><i>Side notes:</i></p></blockquote><blockquote id="5c65"><p><i>This attribute uses the British spelling of “labelled”, not the US spelling (“labeled”); so make sure you’ve spelled it with two “L”s.</i></p></blockquote><blockquote id="cabd"><p><i>If you have two elements you want to serve as the label for a control, put the <code>id</code> values of both as the value of <code>aria-labelledby</code>, separated by a space. You may need to use this method if you have input controls in a grid or table. Use the <code>id</code> attribute of the relevant <code><th></code> elements.</i></p></blockquote><h1 id="1f7e">How Not to Label Controls</h1><p id="97c6">We’ve covered ways to make your labels accessible to screen readers. Now let’s cover some ways that are <b>not</b> accessible. It’s not wrong to utilize any of these to add information about the controls; but they are not appropriate for labels.</p><h2 id="7f60">1. The Title Attribute is Not a Label</h2><p id="2fb4">You should avoid the <code>title</code> attribute as a label because screen readers do not always consume it, which makes the control inaccessible.</p><p id="9c15">However, you can use the <code>title</code> attribute as a means to provide additional, non-essential information.</p><h2 id="81b7">2. The Placeholder Attribute is Not a Label</h2><p id="69d9">The <code>placeholder</code> attribute, like the <code>title</code> attribute, is not a label and should never serve as one. Screen readers do not always consume the placeholder; and the placeholder disappears when a textbox receives input — so even sighted users will no longer be able to see the placeholder.</p><p id="38ff">Like the <code>title</code> attribute, the <code>placeholder</code> attribute can be used to provide non-essential information.</p><blockquote id="2d89"><p><i>Side notes:</i></p></blockquote><blockquote id="cec4"><p><i>I’m aware of the “floating label” placeholder. The problem is it’s rarely (if ever) accessible. Even Bootstrap’s floating label isn’t (as of v5.0.2). Screen readers will read it correctly, but the label violates color contrast guidelines (light gray text over a white background) once the control receives focus. What’s worse is they do this with the label’s opacity, which isn’t always caught by automated accessibility tools.</i></p></blockquote><blockquote id="8a4e"><p><i>As Adam Silver states in his article “<a href="https://readmedium.com/floating-labels-are-a-bad-idea-82edb64220f6">Floating labels are problematic</a>”, “People may skip the field [that uses a placeholder] thinking it’s completed already. When they submit, they will see an error which needs fixing. This is frustrating and time consuming.”</i></p></blockquote><h2 id="4d60">3. The Aria-describedby Attribute is Not a Label</h2><p id="7100">The <code>aria-describedby</code> attribute is mostly for instructions for the control. This can be for character limits or formatting requirements, like this example:</p><div id="c1b6"><pre><span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"txtEmployer"</span>></span>Employer: <span class="hljs-tag"></<span class="hljs-name">label</span>></span> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"txtEmployer"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">aria-describedby</span>=<span class="hljs-string">"descEmployer"</span> /></span> <span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"descEmployer"</span>></span>Limit to 30 characters<span class="hljs-tag"></<span class="hljs-name">span</span>></span></pre></div><p id="c05d">JAWS check: <i>“Employer colon Edit. Limit to 30 characters. Type in text.”</i></p><p id="cdf9">This attribute is necessary for custom or third-party controls if it reacts to keyboard or focus events differently than native HTML controls. In this case, provide the instructions on the page in an element with an <code>id</code> attribute (usually a <code>div</code> or <code>span</code>). Then, set the value of <code>aria-describedby</code>to the <code>id</code> of the element with the instructions.</p><h1 id="aeff">Final Thoughts</h1><p id="5d01">While this tutorial focuses mostly on screen readers, remember that WCAG standards help you accommodate users with varying disabilities. This includes those with dexterity challenges, cognitive challenges, and color blindness among others.</p><p id="2245">So, don’t make your controls tiny (dexterity), help the user avoid mistakes when they enter information into your forms (cognitive), and don’t use color as the only means to communicate information (color blindness).</p><p id="43b1">I’ll add a few links to free accessibility tools. These are helpful; but as you know, automated testing doesn’t catch everything.</p><h1 id="bcd6">Links</h1><ul><li><a href="https://www.w3.org/TR/WCAG22/">WCAG 2.2 Standards</a></li><li><a href="https://www.w3.org/TR/wcag-3.0/">WCAG 3.0 Standards</a> (unfinished working draft)</li><li><a href="https://www.tpgi.com/arc-platform/arc-toolkit/">TGPi ARC Toolkit</a> (browser extension)</li><li><a href="https://www.ssa.gov/accessibility/andi/help/install.html">ANDI</a> (bookmarklet, best used for US Section 508 compliance testing)</li><li><a href="https://accessibilityinsights.io/en/downloads/">Accessibility Insights</a> (browser extension)</li><li><a href="http://webaim.org/resources/contrastchecker">WebAIM Contrast Checker</a> (website for checking color contrast)</li><li><a href="https://www.w3.org/WAI/tutorials/">W3C WAI Tutorials</a></li></ul></article></body>

5 Ways to Make Labels Accessible for Input Controls — and 3 Ways Not to

If you’re a web developer and you’re confused about accessibility standards, this article is for you.

Photo by Marcus Aurelius from Pexels

The current WCAG (Web Content Accessibility Guidelines) 2.2 standards are written by principle, not by control. So, instead of a section on textboxes or buttons, there are sections on the “Perceivable” and “Operable” principles.

I’ll use my experience as a developer and an accessibility tester to help you get your labels accessible for screen readers.

Quick disclaimer: There is more to getting your labels accessible than what’s included here, such as color contrast, size, positioning, consistent formatting, and predictability. However, this article will focus on labels that screen readers can consume properly.

See Understanding Success Criterion 3.3.2: Labels or Instructions for referencing the guidelines provided in this article.

Photo by Kelly Sikkema on Unsplash

To test my examples, I’ll use Freedom Scientific’s JAWS (Job Access With Speech) screen reader on Chrome. JAWS is the most popular screen reader out there. It isn’t free; but there are other screen readers that are, such as NVDA or Apple VoiceOver (native to Mac OS).

Side note: Unfortunately, screen readers do not always interpret markup the same way. In fact, they may not read the same page on a different browser the same way. Your best bet is to use native HTML controls whenever possible.

An input control (textbox, radio button, checkbox, etc.) without a label is not accessible. There are no exceptions. But you do have a few choices for how you label them.

There are two types of accessible labels for input controls: traditional and hidden labels. These aren’t conventional names; it’s just what I call them. There are two traditional labels and three hidden labels.

These labels are in order with the first being the most recommended method. ARIA attributes are last because they should be a last resort for labeling input controls. I’ll explain when we get there.

Traditional Labels

A traditional label is a visible <label> element programmatically assigned to an input control. These labels are easy to implement and, unless you add some weird CSS and/or JavaScript, will provide accessible labels every time.

Traditional labels can be assigned explicitly or implicitly.

1. Use an Explicit Label

To explicitly assign a label to an input control, use the for attribute on the <label> element and set its value to the id attribute of the control. For textboxes, the markup looks like this:

<label for="txtLastName">Last Name: </label>
<input id="txtLastName" type="text"/>

The markup renders on the browser like this:

This is the cleanest, most reliable way to label your controls. JAWS reads the textbox like this: “Last Name colon Edit. Type in text.”

Side note: Notice I used a colon (:) after the text “Last Name”. It doesn’t matter whether you use one or not. Just make sure your labels are consistent throughout (they either all have a colon or they all don’t).

2. Use an Implicit Label

To implicitly label an input control, wrap the control in a <label> element like this (markup and rendering):

<label>Last Name: <input id="txtLastName" type="text"/></label>

JAWS also reads this textbox as “Last Name colon Edit. Type in text.”

Both implicit and explicit labels are accessible and render the same way; however, I recommend an explicit label whenever possible. Some screen readers can provide inconsistent results when implicit labels are used and it can prevent headaches if you ever do any automated testing.

Side Note: Click the label of a control to easily check you’ve labeled it properly. If the corresponding input control receives focus, you’ve made the association correctly. Unfortunately, this little trick doesn’t work on hidden labels.

Hidden Labels

A hidden label is… hidden. This can be achieved by visually hiding a <label> element or with ARIA attributes. You have to take great care with hidden labels as it’s easier to make a mistake.

So, why would you use a hidden label? Because sometimes a traditional label doesn’t make sense. Take a look at this markup:

<label for="txtSearch">Search:</label>
<input id="txtSearch" type="text"/>
<button id="btnSearch" type="submit">Search</button>

And here’s what the browser renders:

Redundant, right? You probably even winced at it. Fortunately, we don’t have to do this to have an accessible textbox.

JAWS check (on Chrome): When the textbox receives focus, it reads the following: “Search colon Edit. Type in text.”

So, we want to hide the label; but we also want the screen reader to read the same way.

We can use a hidden label here because the context of the two controls sufficiently communicates the purpose of each. In this case, the context is the proximity of the textbox and the Search button to one another.

Guideline: Use hidden labels only if the context of the control is visually apparent

3. Use a “Screen Reader Only” Label

You can create a CSS class to take the <label> element out of the page visually but retain it for screen readers. Take a look at this CSS declaration:

.screenReaderOnly {
 border: 0;
 clip: rect(0 0 0 0);
 height: 1px;
 margin: -1px;
 overflow: hidden;
 padding: 0;
 position: absolute;
 width: 1px;
 }

This style will completely remove any element from view. Now, for any <label> element you want to hide, give it the .screenReaderOnly class and it will still be consumed by screen readers.

Look at this markup:

<label for="txtSearch" class="screenReaderOnly">Search: </label>
<input id="txtSearch" type="text"/>
<button id="btnSearch" type="submit">Search</button>

With the CSS class properly implemented, the browser renders this:

JAWS check: “Search colon Edit. Type in text.”

Nice! So, when the textbox receives focus, the screen reader will read the hidden label as if it were not hidden.

Side Notes:

If your site uses the Bootstrap framework, this is already built-in. For Bootstrap 4 and earlier versions, you can use the .sr-only class and for Bootstrap 5, use the .visually-hidden class.

If you want to do the reverse (make an element visually available but hide it from screen readers), add the following HTML attribute to the element: aria-hidden="true". This is handy for decorative elements such as icons that don’t represent essential information.

Do not use {display: none} or {visibility: hidden} to hide labels because they will hide them from screen readers.

A Word About ARIA Attributes

ARIA attributes have no visual effect on their elements; but they make a world of difference for those with screen readers. The attributes aria-label and aria-labelledby allow a user with a screen reader to consume input controls as if they had traditional labels.

As I said earlier, be careful with hidden labels — especially ARIA attribute labels. It’s important to understand their purpose. These attributes aren’t just an alternative to the title attribute.

As Attila Vágó stated in his article Accessibility is Hard — Careful With Them ARIA Labels!, “an aria-label does not mean it provides additional information. It does exactly the opposite. It overrides the meaning of your component entirely.”

To demonstrate that, take a look at this markup where I use an explicit label and an aria-label attribute:

<label for="txtLastName">Last Name: </label>
<input id="txtLastName" aria-label="Surname" type="text" />

JAWS Check: “Surname Edit. Type in text.”

Yikes! As you can see, the traditional label’s text (“Last Name: ”) is completely ignored and the aria-label overrides it.

Guideline: ARIA labels should only be used if traditional labels are not an option.

4. Use an Aria-label Attribute

Use the aria-label attribute to give the hidden label some text like you would a traditional label. You can enter any text as the value for aria-label, keeping in mind it should properly describe the control and be consistent with the text of other <label> elements on the site.

<input id="txtSearch" type="text" aria-label="Search" />
<button id="btnSearch" type="submit">Search</button>

JAWS check: “Search Edit. Type in text.”

5. Use an Aria-labelledby Attribute

The aria-labelledby attribute is really handy when there is an element on the page that can serve as a label for your control. Add the aria-labelledby attribute to the control and set its value to the id attribute of the element serving as the label.

Look at this example:

<input id="txtSearch" type="text" aria-labelledby="btnSearch" />
<button id="btnSearch" type="submit">Search</button>

JAWS check: “Search Edit. Type in text.”

Side notes:

This attribute uses the British spelling of “labelled”, not the US spelling (“labeled”); so make sure you’ve spelled it with two “L”s.

If you have two elements you want to serve as the label for a control, put the id values of both as the value of aria-labelledby, separated by a space. You may need to use this method if you have input controls in a grid or table. Use the id attribute of the relevant <th> elements.

How Not to Label Controls

We’ve covered ways to make your labels accessible to screen readers. Now let’s cover some ways that are not accessible. It’s not wrong to utilize any of these to add information about the controls; but they are not appropriate for labels.

1. The Title Attribute is Not a Label

You should avoid the title attribute as a label because screen readers do not always consume it, which makes the control inaccessible.

However, you can use the title attribute as a means to provide additional, non-essential information.

2. The Placeholder Attribute is Not a Label

The placeholder attribute, like the title attribute, is not a label and should never serve as one. Screen readers do not always consume the placeholder; and the placeholder disappears when a textbox receives input — so even sighted users will no longer be able to see the placeholder.

Like the title attribute, the placeholder attribute can be used to provide non-essential information.

Side notes:

I’m aware of the “floating label” placeholder. The problem is it’s rarely (if ever) accessible. Even Bootstrap’s floating label isn’t (as of v5.0.2). Screen readers will read it correctly, but the label violates color contrast guidelines (light gray text over a white background) once the control receives focus. What’s worse is they do this with the label’s opacity, which isn’t always caught by automated accessibility tools.

As Adam Silver states in his article “Floating labels are problematic”, “People may skip the field [that uses a placeholder] thinking it’s completed already. When they submit, they will see an error which needs fixing. This is frustrating and time consuming.”

3. The Aria-describedby Attribute is Not a Label

The aria-describedby attribute is mostly for instructions for the control. This can be for character limits or formatting requirements, like this example:

<label for="txtEmployer">Employer: </label>
<input id="txtEmployer" type="text" aria-describedby="descEmployer" />
<span id="descEmployer">Limit to 30 characters</span>

JAWS check: “Employer colon Edit. Limit to 30 characters. Type in text.”

This attribute is necessary for custom or third-party controls if it reacts to keyboard or focus events differently than native HTML controls. In this case, provide the instructions on the page in an element with an id attribute (usually a div or span). Then, set the value of aria-describedbyto the id of the element with the instructions.

Final Thoughts

While this tutorial focuses mostly on screen readers, remember that WCAG standards help you accommodate users with varying disabilities. This includes those with dexterity challenges, cognitive challenges, and color blindness among others.

So, don’t make your controls tiny (dexterity), help the user avoid mistakes when they enter information into your forms (cognitive), and don’t use color as the only means to communicate information (color blindness).

I’ll add a few links to free accessibility tools. These are helpful; but as you know, automated testing doesn’t catch everything.

Links

Accessibility
WCAG
Web Accessibility
Inclusive Design
A11y
Recommended from ReadMedium