...
Avoid skipping heading levels (h1
followed by h2
, h2
followed by h3
,...):
Levels can should only be skipped when closing subsections (i.e. an h4 tag followed by an h2 tag to start a new section)
If a heading level is skipped, this could provide a negative experience to users as they may feel compelled to go back and look for the “missing” heading
An exception to this rule is headings for fixed content (e.g. sidebars) where the rankings should not change depending on the ranks of the content area. Consistency across pages is more important
...
If there is no
h1
in a mockup, you can add one withclassName="sr-only"
so only screen readers “see” it.Example:
<h1 className="sr-only">Hello world</h1>
Will be hidden from the view but AT will still pick up on it
This doesn’t have to be the first heading element in the document but it usually is (see this example)
The page title attribute (in the
<head>
element) should generally reflect or be a superset of theh1
content. Example:<h1>Notes</h1>
has<title>Notes | edx.org</title>
Change the
<title>
whenever the H1 changes.If possible, also push a new route onto the Back stack when the
h1
changes
...
Defines the hierarchical level of an element within a structure; when combined with role="heading"
will tell AT that the element is a heading. The “heading” role requires use of the aria-level
attribute to indicate its position in the hierarchy.
role="heading"
without anaria-level
attribute should will default to an H2 semanticallyh2
, but you should include thearia-level
attribute for clarity anyway.This can even be done to
hN
tags if necessaryExample:
<div role="heading" aria-level="3">Hello World</div>
is the same as using anh3
This also enables devs to level headings beyond what is provided natively (
h1
-h6
)We could theoretically create an
h7
this way usingaria-level
...
The main header for dialogs is, by convention, an H2
h2
. If there is a request to use a different heading level tag as the top-level in the dialog, then addrole="heading"
andaria-level=N
attributes to all headers as appropriate to start from level 2
...
This can be useful when there is more than one of the same sectioning element on a page (e.g.
<nav>
,<main>
,<header>
,<footer>
,<article>
, full list here)The
id
of thehN
tag will need match the value of the parent element’saria-labelledby
attribute, and must be unique to the page.See this example of
aria-labelledby
being used to differentiate two different <nav> elementsYou should generally use
aria-labelledby
to name all<section>
and<nav>
elements in this way when appropriate visible DOM elements are present (i.e. headings). If they’re they're not present, then you can either usearia-labelledby
with sr-only elements, or usearia-label
(which takes a human-readable string, not a DOM ID)Dialogs (
div role="dialog"
or<dialog>`
) need an accessible name, which generally comes from anaria-label
attribute oraria-labelledby
(pointing to the main header in the dialog).
...
Paragon provides a CSS class for each hN
element (.h1, .h2, .h3,...). These do not provide any information for screen readers or accessibility. They are intended to change font-size and styling.
...
There is also a .heading-label
class. Worth noting that this doesn’t effect accessibility (has no relation to aria-label
) and is primarily for styling purposes.
See Paragon https://paragon-openedx.netlify.app/foundations/typography for more information on which properties are provided in each class and how they can be implemented.
Resources
https://www.tpgi.com/heading-off-confusion-when-do-headings-fail-wcag/
...