Headings and labels need to describe the content they are identifying in the content. This means when you have a H1 is should describe the page as a whole. Labels need to describe the data they are collecting. Seems simple. And usually it is. Let’s look at some examples of when it isn’t as clear.
Headings
I like to remind folks that when it comes to headings, they are intended to be used in a specific order. However, WCAG does not enforce this as a requirement for accessibility. This means we start with a page description as H1. Normally, we want to reinforce that description as part of the page’s title too. This gives the user the ability to review their tabs to find what they are looking for and confirms it when they read the page.
Once the page is described, we then look to the H2 and ask where does this content break into different areas? You probably have primary navigation, a sidebar, main, and footer. Perhaps you have a few dialogs for cookies, email, and comparing items. Each of these is a subsection of the page and should have the H2. The main content may have several, like this blog does. If the content can be further divided, continue adding the next lower heading.
Note: HTML spec lays out 6 levels, however browser can support more than that. This varies by browser, but most have at least 9 levels but can be in the thousands. However, browsers only apply default values for font size and display on the first 6. If you decide to use additional heading beyond that, you must define their appearance. Additionally, most screen readers only support 6. So it is not recommended to go beyond 6.
One place I don’t recommend using a heading is in a product card. Screen reader users have the ability to view headings in a list and jump to each. If you have dozens of headings on the screen, navigating this can be quite cumbersome. Make the product a link and only a link. Screen reader users can also bring up a list of links to navigate, but the list of headings is more for “skimming” a site so each product should not be included.
Labels
If it is an interactive control it needs to have a label that describes what is being collected. Period. End of sentence. However, we want to talk about nuance.
Native Elements
When using an input, select, or textarea control with native HTML, provide it an ID and the <label> element with a for attribute pointing at the ID.
<div class="container">
<label for="name">Full Name</label>
<input type="text" id="name" />
</div>
<div class="container">
<label for="message">Message</label>
<textarea id="message"></textarea>
</div>
<div class="container">
<label for="state">State</label>
<select id="state">
<option value="AL">Alabama</option>
[...]
</select>
</div>
One other thing we need to consider is that each label needs to have a unique ID to the page. When using native elements like this having the same ID, users can end up editing the wrong element without knowing. Ideally, the label is also unique as it reduces confusion. But if a duplicate ID is used and the user activates the field by clicking on the label, focus moves to the first field on the page with that ID. I see this frequently.
The best example I can provide is on a tax form. It has “First Name” as the labels for every income earner and dependent. Since the company was just reusing a template, they all ended up with the same ID. A low vision user may click the label to edit but focus may not move and end up editing their name instead of their spouse. Those with decent vision may not notice the jump of the cursor to the first field and get confused when they type and don’t see the field they are looking at fill up. Ensure all labels and IDs are unique.
Custom Controls
Unless you have a web component that has a native input inside it, you will eventually need to build custom labels. The best way for this is to provide a visual label with an ID. Then on the custom control, use aria-labelledby to point at ID of the label. It is vitally important that the label is visible for speech control or dictation users. I’ll cover this more when writing on WCAG 2.5.3 Label in Name.
Use native HTML whenever possible
Even within your custom controls, use native elements as much as possible. Custom controls that use aria-labelledby cannot have their labels activated to put focus on the field unless you write some JavaScript to do so. But these labels are also not supposed to be in the tab order, this is actually a mouse-only operation.
Have thoughts or questions? Come chat with me on LinkedIn or Bluesky!

Comments are closed.