skip to content

A page has three <nav> elements — the primary menu, a breadcrumb trail, and an on-page table of contents. How do you make them distinguishable in a screen reader's landmark list?

level: seniorimportance: should knowfreq 44%

answer

  1. announced as role plus name
  2. two attributes, one preferred
  3. never repeat the role word
  4. id and class do not reach it
  5. one of them is not even listed unnamed

basics

~20 s

Give each landmark an accessible name with aria-label or aria-labelledby, so the list reads "Primary navigation", "Breadcrumb", "On this page" instead of three identical entries. Leave the word navigation out — the role is announced already.

solid answer

~40 s

Landmarks are announced as role plus name, so three unnamed `<nav>` elements come out as "navigation, navigation, navigation" and the list becomes useless. The fix is to name each one: `aria-label="Primary"`, `aria-label="Breadcrumb"`, and, when the region already has a visible heading, `aria-labelledby` pointing at that heading's `id` — reusing visible text is better than inventing a parallel invisible label. Two details matter. First, do not put "navigation" in the label; the role is spoken automatically, so `aria-label="Primary navigation"` is announced as "primary navigation navigation". Second, the same rule applies to `<section>`, which is not even exposed as a `region` landmark until it has a name — so an unnamed section is invisible in the landmark list rather than merely ambiguous. Naming is only worth doing on landmarks that genuinely repeat.

code

html · 15 lines
html
<nav aria-label="Primary">
  <ul><li><a href="/docs">Docs</a></li></ul>
</nav>

<nav aria-label="Breadcrumb">
  <ol>
    <li><a href="/docs">Docs</a></li>
    <li><a href="/docs/install" aria-current="page">Install</a></li>
  </ol>
</nav>

<nav aria-labelledby="toc-h">
  <h2 id="toc-h">On this page</h2>
  <ol><li><a href="#requirements">Requirements</a></li></ol>
</nav>

go deeper

for a junior

Know that repeated landmarks need names, and that the name goes on the element with aria-label — not an id or a class, which assistive technology never sees.

for a middle

Explain that landmarks announce role plus name, so the label must exclude the role word, and know that section and form only become landmarks once named.

for a senior

Show judgment about how many named landmarks a page should have, prefer aria-labelledby against existing visible headings, and verify the result in the accessibility tree rather than trusting the markup.

for a principal

Standardise the vocabulary across the product — the same names for primary, breadcrumb and in-page navigation on every page — so landmark navigation stays predictable as teams ship independent surfaces.

## How a landmark is announced Assistive technology presents a landmark as **role + accessible name**. With no name, all a user hears is the role. One `<nav>` on a page is fine that way — "navigation" is unambiguous. Three are not: the rotor shows three identical entries and the user has to enter each one and read around to work out which is which, which is exactly the linear reading landmarks exist to avoid. ## Naming them ```html <nav aria-label="Primary">…site menu…</nav> <nav aria-label="Breadcrumb"> <ol>…</ol> </nav> <nav aria-labelledby="toc-h"> <h2 id="toc-h">On this page</h2> <ol>…</ol> </nav> ``` The rotor now reads "Primary navigation", "Breadcrumb navigation", "On this page navigation" — three distinct destinations. Prefer `aria-labelledby` whenever the region already has visible text that names it. It keeps one string in the DOM instead of two, so the label cannot drift out of sync with what is on screen, and it is translated along with the rest of the page by machine translation that would skip an attribute. Use `aria-label` when there is no visible heading to point at, as with the primary menu and the breadcrumb trail above. ## The redundancy trap Because the role is announced automatically, the label must not repeat it. `aria-label="Primary navigation"` on a `<nav>` produces "primary navigation navigation". Write `aria-label="Primary"`. The same applies to `<aside aria-label="Complementary sidebar">` and to `<section aria-label="Region: filters">`. The name is the *distinguisher*, not a description of the element type. ## Which landmarks actually need names Only the ones that repeat, plus the ones that do not exist without a name at all: - **`<nav>`** — name every one when there is more than one. A lone nav needs nothing. - **`<section>`** — not exposed as a `region` landmark until it has an accessible name. Unnamed, it maps to generic and simply is not in the list. - **`<form>`** — same conditional rule: it becomes the `form` landmark only once named. - **`<aside>`** — name them when a page has several. - **`<main>`** — never. There is one, and its role already says what it is. - **`<header>`/`<footer>` at page level** — normally not; a page should have one banner and one contentinfo. ## Techniques that do not work `id` and `class` do not reach assistive technology at all; they are selector hooks. Order in the DOM is not announced. A `title` attribute is a weak fallback and unreliable in practice — do not rely on it for landmark naming. And wrapping each nav in a labelled `<div>` accomplishes nothing, since a `<div>` is generic no matter what attributes it carries. ## Do not over-name Naming has a cost: every named landmark is another entry the user must skim. Adding `aria-label` to a dozen sections turns a five-item map into a directory. Landmarks are meant to be the coarse skeleton of a page — a couple of navs, main, a complementary region or two, the masthead and the footer. If a name is needed only because there are eight regions where two would do, the fix is fewer landmarks, not better labels. ## How to verify Read the landmark list rather than the markup. Browser devtools expose each node's computed role and accessible name, and screen readers offer a landmark rotor or menu that shows exactly what the user will hear. That is also how you catch the two silent failures in this area: a `<section>` you thought was a region but never named, and a label that duplicates the role.

  • Why prefer aria-labelledby over aria-label when the region already has a visible heading?
    Because it reuses text that is already in the DOM. One string means the label cannot drift when the heading is edited, sighted and non-sighted users get the same wording, and page translation covers it — attribute text is often skipped by machine translation. Reach for `aria-label` only when there is genuinely no visible text to reference.
  • Does a page with a single <nav> element need a label?
    No. With one navigation landmark, "navigation" is already unambiguous and a label adds a word without adding information. Naming earns its keep when landmarks of the same role repeat — then the name is the only thing that tells them apart.
  • What happens if you label a <section> but the label duplicates the role?
    The user hears the duplication: `aria-label="Filters region"` on a `<section>` is announced roughly as "filters region, region". Naming a section does promote it from generic to the region landmark, so the label is doing real work — it just needs to be the distinguishing words only, such as "Filters".

saying these in an interview costs you the question

  • Using id or class to distinguish landmarks
  • Writing aria-label="Main navigation" and duplicating the role
  • Assuming an unnamed <section> shows up as a region
  • Labelling every section until the rotor is unusable
  • Relying on the title attribute to name a landmark

context