Accessibility Reference
A quick lookup for web accessibility concepts grouped by category. Each entry covers what it is, when to use it, and how it works so you can build accessible experiences without leaving the platform.
ARIA roles
Section titled “ARIA roles”ARIA roles tell assistive technologies what an element is — its purpose in the interface. Set them with the role attribute. Prefer native HTML elements over ARIA roles when possible; a native <button> is better than <div role="button">.
Landmark roles
Section titled “Landmark roles”Landmarks let screen reader users jump directly to major sections of a page.
| Role | Native equivalent | What it marks |
|---|---|---|
banner | <header> (top-level) | Site header — logo, primary nav |
navigation | <nav> | A set of navigation links |
main | <main> | Primary page content |
complementary | <aside> | Supporting content (sidebars, callouts) |
contentinfo | <footer> (top-level) | Footer — copyright, legal, contact |
search | <search> | Search form or region |
form | <form> (with accessible name) | A significant form region |
region | <section> (with accessible name) | A generic named region |
Prefer the native HTML element — it carries the role implicitly. Use an explicit role only when you cannot use the native element.
Widget roles
Section titled “Widget roles”Widget roles describe interactive controls.
| Role | What it describes | Notes |
|---|---|---|
button | A clickable action | Use <button> instead |
checkbox | A two-state toggle | Use <input type="checkbox"> instead |
radio | A single-select option in a group | Use <input type="radio"> instead |
textbox | A text entry field | Use <input> or <textarea> instead |
combobox | A combined input and listbox | Complex — requires aria-expanded, aria-controls |
listbox | A list of selectable options | Alternative to <select> for custom dropdowns |
option | An item in a listbox | Child of listbox |
tab | A tab in a tab list | Requires tablist parent and tabpanel |
tablist | A container for tab controls | Parent of tab elements |
tabpanel | The content panel for a tab | Linked to its tab via aria-controls |
dialog | A modal or non-modal dialog | Requires accessible name and focus management |
alertdialog | A dialog requiring immediate response | Like dialog but announces urgently |
tooltip | A contextual label shown on hover/focus | Linked to trigger via aria-describedby |
Live region roles
Section titled “Live region roles”Live regions announce dynamic content changes to screen reader users without requiring focus.
| Role | What it does | Use for |
|---|---|---|
status | Politely announces non-critical updates | Status messages, save confirmations |
alert | Urgently interrupts to announce a message | Errors, warnings, important notices |
log | Announces new content in order | Chat logs, activity feeds |
progressbar | Communicates progress of a task | File uploads, form steps |
<!-- Status message (polite) --><div role="status" aria-live="polite">Changes saved.</div>
<!-- Alert (urgent) --><div role="alert">Error: Please correct the highlighted fields.</div>Document structure roles
Section titled “Document structure roles”| Role | What it marks |
|---|---|
article | Self-contained content (blog post, card) — use <article> |
list | A list of items — use <ul> or <ol> |
listitem | An item in a list — use <li> |
heading | A section heading — use <h1>–<h6> |
img | An image or graphic — use <img> with alt |
figure | A figure with optional caption — use <figure> |
separator | A thematic break — use <hr> |
presentation | Removes semantic meaning from element |
none | Same as presentation |
ARIA attributes
Section titled “ARIA attributes”ARIA attributes provide additional information that HTML alone cannot express. They never change visual appearance — only what assistive technologies communicate.
Naming and describing
Section titled “Naming and describing”| Attribute | What it does | Example |
|---|---|---|
aria-label | Provides an accessible name directly as a string | <button aria-label="Close dialog">×</button> |
aria-labelledby | Points to one or more elements whose text is the accessible name | <input aria-labelledby="email-label"> |
aria-describedby | Points to elements that provide additional description | <input aria-describedby="email-hint"> |
aria-placeholder | Hint text for inputs — prefer the placeholder attribute | <input aria-placeholder="mm/dd/yyyy"> |
aria-roledescription | Overrides the role’s spoken name | Use sparingly — can confuse users |
Prefer aria-labelledby over aria-label when a visible label already exists — it keeps the visible and accessible names in sync.
State attributes
Section titled “State attributes”State attributes reflect the current condition of an element and update dynamically as the user interacts.
| Attribute | Values | What it communicates |
|---|---|---|
aria-expanded | true / false | Whether a disclosure (menu, accordion) is open |
aria-checked | true / false / mixed | Checked state of a checkbox or radio |
aria-selected | true / false | Whether an option/tab is currently selected |
aria-pressed | true / false / mixed | Whether a toggle button is active |
aria-disabled | true / false | Whether an element is disabled (but still in tab order) |
aria-hidden | true / false | Hides element from assistive tech while keeping it visible |
aria-invalid | true / false / grammar / spelling | Marks a field as having an error |
aria-busy | true / false | Signals that a region is still loading |
aria-current | page / step / date / true / false | Marks the current item in a set (e.g., current nav link) |
Relationship attributes
Section titled “Relationship attributes”| Attribute | What it does |
|---|---|
aria-controls | Points to the element this control manages (e.g., a button that opens a panel) |
aria-owns | Declares a parent–child relationship not expressed in the DOM |
aria-flowto | Suggests an alternative reading order |
aria-activedescendant | Points to the focused child in a composite widget (e.g., a listbox) |
aria-errormessage | Points to the element containing the error message for an invalid field |
aria-details | Points to an element with extended description |
Live region attributes
Section titled “Live region attributes”| Attribute | Values | What it does |
|---|---|---|
aria-live | off / polite / assertive | Controls how urgently changes are announced |
aria-atomic | true / false | Whether the whole region or only changed parts are announced |
aria-relevant | additions / removals / text / all | What types of changes trigger an announcement |
Landmark elements
Section titled “Landmark elements”These HTML elements have built-in landmark roles. Use them instead of <div> to give your page meaningful structure that assistive technology users can navigate.
| HTML element | Implicit ARIA role | When to use |
|---|---|---|
<header> (top-level) | banner | Site-wide header — wrap logo and primary navigation |
<nav> | navigation | Any set of navigation links — label multiple <nav>s with aria-label |
<main> | main | The primary page content — one per page |
<aside> | complementary | Sidebars, related content, callouts |
<footer> (top-level) | contentinfo | Site-wide footer — copyright, contact, legal |
<section> | region (with accessible name) | Named content region — add aria-labelledby or aria-label |
<article> | article | Self-contained content: blog post, card, comment |
<form> | form (with accessible name) | Significant form — add aria-label or aria-labelledby |
<search> | search | Search form or region |
<header> and <footer> only carry their landmark role when they are direct children of <body>. Nested inside an <article> or <section>, they are generic elements.
<body> <header> <!-- role="banner" --> <nav aria-label="Primary">...</nav> <!-- role="navigation" --> </header>
<main> <!-- role="main" --> <article>...</article> <!-- role="article" --> <aside aria-label="Related">...</aside> <!-- role="complementary" --> </main>
<footer> <!-- role="contentinfo" --> <nav aria-label="Footer">...</nav> </footer></body>Common patterns
Section titled “Common patterns”Accessible buttons
Section titled “Accessible buttons”Always use <button> for actions. If a button only contains an icon, add aria-label to provide an accessible name.
<!-- Text button — no extra ARIA needed --><button type="button">Save changes</button>
<!-- Icon-only button — label required --><button type="button" aria-label="Close dialog"> <svg aria-hidden="true" focusable="false">...</svg></button>
<!-- Toggle button --><button type="button" aria-pressed="false" id="mute-btn">Mute</button>Mark <svg> icons with aria-hidden="true" and focusable="false" so they are invisible to assistive tech and do not receive focus in IE/Edge.
Accessible links
Section titled “Accessible links”Use <a> for navigation (changing location), <button> for actions (doing something). Avoid “click here” or “read more” — the link text should make sense out of context.
<!-- Descriptive link text --><a href="/courses/html/">Start the HTML Foundations course</a>
<!-- Visually hidden additional context (screen-reader only) --><a href="/courses/css/"> CSS Foundations <span class="sr-only"> course overview</span></a>
<!-- Current page in nav --><a href="/courses/html/" aria-current="page">HTML Foundations</a>Accessible forms
Section titled “Accessible forms”Every input needs a visible <label> associated by for/id. Use aria-describedby for hints and error messages.
<div> <label for="email">Email address</label> <input type="email" id="email" name="email" aria-describedby="email-hint email-error" aria-invalid="false" autocomplete="email" > <p id="email-hint">We'll only use this to send your confirmation.</p> <p id="email-error" role="alert" hidden>Please enter a valid email address.</p></div>When a field has an error, set aria-invalid="true" and show the role="alert" message by removing hidden.
Accessible navigation menus
Section titled “Accessible navigation menus”A disclosure navigation (mobile hamburger or desktop dropdown) needs aria-expanded on the toggle and a matching aria-controls pointing to the menu.
<button type="button" aria-expanded="false" aria-controls="primary-nav" aria-label="Open menu"> <svg aria-hidden="true" focusable="false">...</svg></button>
<ul id="primary-nav" hidden> <li><a href="/">Home</a></li> <li><a href="/courses/">Courses</a></li></ul>Toggle aria-expanded between "true" and "false" and show/hide the menu when the button is clicked.
Accessible modals (dialogs)
Section titled “Accessible modals (dialogs)”A modal needs role="dialog", an accessible name, and careful focus management.
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title" id="confirm-dialog" hidden> <h2 id="dialog-title">Confirm deletion</h2> <p>This action cannot be undone. Are you sure?</p> <button type="button">Delete</button> <button type="button">Cancel</button></div>When the dialog opens: move focus to the first focusable element inside it, trap Tab/Shift+Tab within it, and restore focus to the trigger when it closes. When it closes: return focus to the element that opened it.
Skip links
Section titled “Skip links”A skip link lets keyboard users bypass repetitive navigation and jump straight to the main content. Place it as the very first element in <body>.
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- ... navigation ... -->
<main id="main-content" tabindex="-1"> <!-- page content --></main>Style .skip-link to be visually hidden until focused:
.skip-link { position: absolute; top: -100%; left: 0;}.skip-link:focus { top: 0;}Keyboard navigation
Section titled “Keyboard navigation”Focus management
Section titled “Focus management”| Concept | Rule |
|---|---|
| Visible focus indicator | Never remove outline without replacing it with an equally visible alternative |
tabindex="0" | Adds a non-interactive element to the natural tab order |
tabindex="-1" | Makes an element focusable by script (element.focus()) but removes it from tab order |
tabindex > 0 | Avoid — creates an unpredictable tab order |
element.focus() | Use to move focus programmatically — required for dialogs, errors, route changes |
Tab order
Section titled “Tab order”The natural tab order follows DOM order. Elements that receive focus by default:
<a>withhref<button><input>,<select>,<textarea><details>- Any element with
tabindex="0"
Elements that are skipped: <div>, <span>, <p>, and any element with tabindex="-1" or disabled.
Keyboard interaction patterns
Section titled “Keyboard interaction patterns”Standard keyboard behaviors users expect from common widgets:
| Widget | Keys | Behavior |
|---|---|---|
| Button | Enter, Space | Activate |
| Link | Enter | Follow link |
| Checkbox | Space | Toggle |
| Radio group | Arrow keys | Move between options |
| Select | Arrow keys, Enter | Navigate and select |
| Modal dialog | Escape | Close; restore focus to trigger |
| Menu | Arrow keys | Navigate items; Escape closes |
| Tabs | Arrow keys | Switch tabs; Home/End for first/last |
| Accordion | Enter, Space | Toggle panel |
// Trap focus inside a modaldialog.addEventListener('keydown', (event) => { if (event.key !== 'Tab') return;
const focusable = dialog.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); const first = focusable[0]; const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }});
// Close on Escapedialog.addEventListener('keydown', (event) => { if (event.key === 'Escape') closeDialog();});Screen-reader-only text
Section titled “Screen-reader-only text”To provide context for screen reader users without displaying it visually, use a utility class that removes the element from the visual layout while keeping it in the accessibility tree:
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;}Do not use display: none or visibility: hidden — those hide content from assistive technologies too.