Accessibility (a11y)

TheTechnoCult
6 min readJan 16, 2024

--

Accessibility Checklist: https://webaim.org/standards/wcag/checklist

1. Semantic Markup

Organize the content meaningfully, and avoid using <div> and <span> tags everywhere.

  • Structural Blocks / Landmarks (<article>, <section>, <aside>, <main>, <header>, <footer>, <nav>— a list featuring links and a nested <ul> tag) .
  • Headings (<h1> to <h6>).
  • Lists (<ul>, <ol>, <dl>, <menu> — a list contains buttons; Equivalent to a <ul> tag) .
  • Links (<a>).
  • Forms (<form>, <label>, <input>, <button>). Use form elements with proper labeling (<label>) to create accessible forms instead of <p> for titles/names of fields. Limitations with the <label> tag: <button>, <input>, <keygen>, <meter>, <output>, <progress>, <select>, <textarea>.
  • Tables (<table>, <th>, <tr>, <td>).
  • Images (<img>). Use the alt attribute with descriptive text for images. Screen readers use the alt text to convey the content and purpose of images to users who cannot see them. If there is no ‘alt’ attribute, screen readers will announce the ‘src’ attribute.

1.1. Creating an element invisible to users but accessible to screen readers.

This technique is often used to provide additional context or information for users who rely on screen readers.

<div class="visually-hidden">
This content is hidden from sighted users but accessible to screen readers.
</div>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
white-space: nowrap; /* Added for preserving space for single line content */
}

1.2. Focus Management

1.2.1. Focus

All interactive elements must display their focus state when navigating with tabs.

You can access the active element through scripts.

// When the active element is present
const activeElement = document.activeElement
// You can save it and show a popup element to manipulate it further.
// After closing the popup add focus to the previous one
activeElement.focus()

1.2.2. Tab Navigation

Ensure that all interactive elements are accessible for tab navigation.

  • Tab — to the next element.
  • Tab + Shift — to the previous element.

Tabbable content (by default): (1) Links (<a href=”…”>), (2) Form Inputs: Such as <input>, <textarea>, and <select>, as long as they are not disabled, (3) Buttons (<button>): Unless they are disabled, (4) Iframes (<iframe>): Can be focused and allow interaction within the frame, (5) Editable Content ([contenteditable=”true”]): Elements with the contenteditable attribute set to true, (6) Widgets: Some ARIA widgets like sliders, checkboxes, and radio buttons, when appropriately coded

(!) Add arrow key navigation for child elements that appear after interacting with the Tab key. Popups, dropdowns, hidden menues etc.

1.2.3. tabindex

tabindex is an important attribute in HTML used to manage keyboard focus, making web content more accessible, especially for users who rely on keyboard navigation.

  • tabindex="0":

This value places the element in the natural tab order. It’s particularly useful for elements that are not natively focusable (like <div>, <span>, and <p>). Example: <div tabindex="0">Focusable Div</div> makes the div focusable without disrupting the flow of document tab order.

  • tabindex="-1":

This allows an element to be focusable programmatically (through JavaScript) but not via keyboard navigation. It’s useful for managing focus in dynamic applications, like when a modal opens and you want to set focus to its content for screen reader users. Example: <div tabindex="-1">Programmatically Focusable Div</div>.

  • tabindex="1":

Avoid using this. It can make navigation less predictable and more complex.

1.2.4. Tab Trapping

Tab trapping is a technique used in web development to confine the keyboard focus within a specific area of a webpage, typically used in modal dialogs or popups.

Steps: (1) Detect when the focus is about to leave the designated area. (2) Redirect the focus back to the first or last element within the area, depending on the direction of tab navigation.

<div id="modal" tabindex="-1">
<input type="text" placeholder="Input 1">
<input type="text" placeholder="Input 2">
<button>Click Me</button>
<a href="#">Link</a>
</div>
const modal = document.getElementById('modal')
const focusableElements =
'button, [href], input, select, textarea,
[tabindex]:not([tabindex="-1"])'
const firstFocusableElement = modal.querySelectorAll(focusableElements)[0]
const focusableContent = modal.querySelectorAll(focusableElements)
const lastFocusableElement = focusableContent[focusableContent.length - 1]

document.addEventListener('keydown', function(e) {
const isTabPressed = e.key === 'Tab' || e.keyCode === 9

if (!isTabPressed) {
return
}

if (e.shiftKey) { // if shift key pressed for shift + tab combination
if (document.activeElement === firstFocusableElement) {
lastFocusableElement.focus() // add focus for the last focusable element
e.preventDefault()
}
} else { // if tab key is pressed
// if focused has reached to last focusable element
if (document.activeElement === lastFocusableElement) {
firstFocusableElement.focus() // add focus for the first focusable element
e.preventDefault()
}
}
})

firstFocusableElement.focus()

1.2.5. Keyboard Shortcuts

You can customize keyboard shortcuts for your application to enable content manipulation solely using the keyboard. It should be well-documented and should not replace standard key combinations as Cntr + C, Cntr + V etc.

1.2.6. Skip Links

While navigating with the Tab key, you can display an additional button that allows users to skip repetitive content, such as long navigation etc.

2. ARIA Tags

SPEC: https://www.w3.org/TR/html-aria/#aria-table

The first rule of ARIA use: If you can use semantic markup instead, do so! In other cases, use ARIA.

For example, use a <button> element for buttons instead of a <div> with role="button".

The second one: No ARIA is Better Than Bad ARIA.

You might need it when:

  • Roles: use role if there is no html5 analogs. Such a tab, tabpanel, checkbox, alert, banner etc.

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles

<div class="alert-message" role="alert">Alert text</div>
  • Labels: area-label to describe elements.

aria-label (inline elements) and aria-labelledby (for block elements) provide accessible names, while aria-describedby offers a longer extended description.

<p> 
Download <a aria-label="Github page for repository" href="...">this repo</a>
</p>

<!-- or -->

<div id="some-id" class="hidden">Github page for repository</div>
<a aria-labelledby="some-id" href="...">this repo</a>
  • Dynamic / Live Content.

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#live_region_roles

ARIA is particularly useful for dynamic content (like modal dialogs, notifications, and dynamic form validation) where traditional HTML might not convey changes to assistive technologies.

aria-live options:

polite — will announce the live region update when its next idles.

assertive — will interrupt whatever it’s doing to announce.

off — will not read the update.

...</form>

<div id="status"
aria-live="polite"
aria-atomic="true">{{ Some dynamic content from JS }}</div>

Live Region (aria-live="polite"): The div with id="status" is marked as a live region.

aria-atomic="true": This attribute ensures that the entire content of the live region is announced as a single atomic update, rather than just the changed part.

  • Properties and States.

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#states_and_properties

Provide additional information about elements, such as whether a dropdown is expanded (aria-expanded="true" or "false") or an element is disabled (aria-disabled="true"). Should be changed in scripts depending on the state.

  • Relationships between elements.

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#relationship_attributes

ARIA attributes to enhance the accessibility of a navigation menu structure.

<nav id="main-nav">
<button id="menu-button-1"
aria-haspopup="true"
aria-controls="menu-list-1">Menu List 1</button>
<ul id="menu-list-1" role="menu" aria-labelledby="menu-button-1">
<li role="none"><a role="menuitem" href="...">Link 1</a></li>
<li role="none"><a role="menuitem" href="...">Link 2</a></li>
</ul>
</nav>

aria-haspopup="true" indicates that this button controls the display of a popup menu.

aria-controls="menu-list-1" establishes an association between the button and the menu it controls.

This ul (unordered list) is identified as a menu using role="menu".

The li (list item) elements have role="none" to remove the default list item role. This is often done in complex menu structures to ensure proper interpretation by screen readers, especially when using role="menu" and role="menuitem".

<a role="menuitem" href="...">. The anchor (a) elements within the list items are assigned role="menuitem". This role explicitly defines each link as an item in a menu, which can be particularly important for screen readers to convey the nature of the list items in this specific context.

3. Color Contrast

Contrast Checker (for the design stage): https://webaim.org/resources/contrastchecker/

Use the Lighthouse Chrome tool to test the current state. The Color Tool in the DevTools also have build-in ability to measure the contrast.

Color contrast is a critical aspect of accessibility, ensuring that text and interactive elements have sufficient visual distinction from their background to be easily readable.

4. Language Preference

It’s important to add the lang attribute to the <html> tag or other elements for localization to set up the language of the reader.

<html lang="en">
...
<blockquote lang="es">
...
</blockquote>

5. Prefers Reduced Motion

This media query is commonly used to check whether the user has requested the system to minimize animations or motion effects due to preferences related to motion sensitivity or motion-related disabilities.

.animated-element {
animation: exampleAnimation 2s ease-in-out infinite;
}

/* Adjust styles when user prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
.animated-element {
animation: none; /* Disable animation for reduced motion preference */
}
}

6. Tools

JSX-Linter: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y

Systems:

Dev Tool: Chrome Lighthouse

--

--

No responses yet