Every last tag
The leftovers: base, naming things from text already on the page, the input types nobody mentions, modern script loading, locked-down frames, and five small tags most developers never learn.
By the end you can
- Name a control using words already on the page
- Reach for the input type that already exists, whatever the answer is
- Load modern JavaScript and tell the browser what is coming next
- Use the small tags that solve problems people usually solve badly
Easier if you did first
step 1 of 4
base, labelledby, hidden and data
<base href="..."> sets what every short address on the page is measured from. One tag, enormous reach. Get it wrong and every link, picture and script on the page points somewhere else. Most sites are better off without it. Learn it mainly so you recognise it when a page behaves impossibly.
Next, naming things. You already know aria-label, which holds the words in the attribute itself. aria-labelledby does the same job a different way: it points at words that are already on the page, by id.
Why bother? Because you can point at more than one piece of text, and they are read in the order you list them. Ten identical Delete buttons in a list become 'Delete order ORD-4821', 'Delete order ORD-4822', by pointing each one at its own row.
The rule of thumb: visible words are the best name, aria-labelledby when those words are somewhere else on the page, aria-label only when there are no words at all.
aria-hidden="true" hides something from screen readers while leaving it on screen. It is for decoration that is already described, like a small symbol beside a word. Never put it on anything a keyboard can reach: you would create a control someone can land on that announces nothing.
<data value> is the machine-readable twin of <time>. The words read however you like, the value attribute carries the exact figure, such as a product code or a price in pesewas.
accesskey offers a keyboard shortcut. It is here for completeness: the shortcuts clash with the browser's own and with screen reader keys, so most teams avoid it.
Mark up the order row on the right.
<base href="https://example.com/shop/" />
<h2 id="order-4821">Order ORD-4821</h2>
<button type="button" aria-labelledby="delete-label order-4821">
<span id="delete-label">Delete</span>
<span aria-hidden="true">x</span>
</button>
<p>Price: <data value="60000">GHS 600.00</data></p>What these words mean
- base
- Sets the starting point for every short address on the page. One per page.
- aria-labelledby
- Names an element using words already on the page, by id. Several ids are read in order.
- aria-label
- A name written into the attribute. The last resort, when no words exist.
- aria-hidden="true"
- Hide from screen readers only. Never on anything the keyboard can reach.
- data
- Words with an exact machine value beside them.
- accesskey
- A keyboard shortcut. Widely avoided, because the keys clash.
Easy to get wrong
aria-hidden="true" on a button
Leave the button alone, hide only the symbol inside it
The button is still reachable by Tab, but no longer exists to a screen reader. Someone lands on a control that announces nothing at all.
Ten buttons all called Delete
aria-labelledby pointing at the word and the row
Screen reader users often pull up a list of all the buttons on a page. Ten identical entries is a guessing game. 'Delete order ORD-4821' is not.
What we check
- There is a base URL for relative links
- The button is named from text on the page
- It points at two pieces of text that both exist
- The decorative character is hidden from screen readers
- The price carries a machine-readable value
- The button itself is not hidden from assistive tech