Write Accessible Code

Accessibility requirements relevant to front-end development cluster around issues of screen reader hinting, keyboard and mobile compatibility, and interactive and form controls.

Key concepts

  • Imitation is the sincerest form of flattery. Creating accessible forms and interactive elements is hard. Whenever possible, use accessible frameworks and design patterns.
  • Test as you work. Running quick automated and keyboard tests will naturally guide you into discovering and fixing many of the items on this page.
  • Know how screen readers read. Screen reader users skim headings, regions or links to indicate items of interest, and they expect to hear their readers always announce the name, role and value of components: "Visited [value] link [role]: headings, regions or links [name]."
  • Beware the :hover state:
    • Keyboard users use the Tab key (or the equivalent) to advance through links and interactive elements. They need :focus states on elements to see where their cursor is, keyboard click event handlers for custom buttons, and skip links to get past navigation.
    • Screen readers often do not trigger hover OR focus events. Screen readers in their default mode explore pages using a virtual cursor, and do not advance the "real" cursor unless the user clicks to interact. This is incompatible with some menu design patterns.
    • Automatically playing media can present significant obstacles to many users; screen readers cannot be heard over audio, and users with vestibular (motion sickness), reading, or attention disorders may have trouble interacting with text near animated elements.

    Developer's Guide

    Structuring the Page

    1. Help screen reader users skim:
      • Use clear and specific page titles.
      • Structure the page with HTML landmark regions (header, nav, main, footer).
      • Use headers to label key regions, such as the navigation, search and footer, as well as any alerts. These labels can be visually hidden.
      • Differentiate reusable landmarks like <nav> elements with headings or aria-labels: (the main "site" navigation, the vertical "section" navigation).
      • Make sure every focusable element contains accessible text or an aria label, especially icon buttons.
      • Use semantic elements (regions, lists, figures, tables, etc). They are understood and announced by screen readers, and many have special browsing modes (jump to next region/cell/list item/row); simple containers (<div>, <span>) do not provide any of this.
    2. Don't visually reorder elements and regions. Keyboards and assistive devices read the DOM order, and users expect that to match the visual reading order (left to right, top to bottom).
    3. Provide a "skip to main" link, and point it at a named anchor set to tabindex="-1". Targeting unfocusable elements may scroll them into view, but only focusable targets actually transfer focus for keyboard users!
    4. Validate your HTML.

    Responsive Breakpoints & Reflow

    1. Never disable viewport zoom.
    2. Refrain from limiting the height of text containers. Up to 3% of users have modified their default font or font size, which can cause unexpected overflows of fixed-size containers. At a minimum, try to size elements relative to the font size (em or rem) instead of using px units. 
    3. Make sure your code supports accessible design considerations for device flexibility regarding element scaling for small, large, low PPI and high PPI devices.
    4. Expect users to commonly have viewports as narrow as 320px and as wide as 2560px.
      • Make sure your code works in both portrait and landscape.
      • Do not assume users viewing your narrowest (mobile) breakpoint will not be using a keyboard and/or mouse. Their phone may be paired with an input device, or they may be on a desktop with a screen magnifier. Your mobile theme still needs :focus indicators and keyboard click handlers.
      • Do not assume users viewing your widest (desktop) breakpoint will not be using a touchscreen. Large tablets have viewports wider than 1280px. Your desktop theme still needs touch handlers and cannot rely on :hover.

    Forms

    1. Make sure every form element has an accessible label for screen readers, and can be operated by a keyboard.
    2. Identify errors specifically, and be sure to tag errors well for screen readers. Be as helpful as possible; e.g.,  "Please provide your birthday as YYYY-MM-DD," rather than "Invalid date format."
    3. If your form has a time limit of less than a day, make sure it can be extended or disabled, unless one of the exceptions in the "Enough Time" guideline applies.
    4. If your form handles legal commitments or financial transactions, be sure to read the detailed guidelines regarding user review, correction and confirmation.

    Consult the W3C Forms Tutorial for more depth and code samples.

    In detail: Interactive Components

    Modals, Slideshows, Menu Bars, Accordions, Tab Panels...every part of an interactive component, from its outer container to its buttons, status messages and focus transfers, needs to be accessible to keyboards and screen readers. Whenever possible, use pre-tested accessible frameworks or design patterns.

    Before creating your own interactive components, read the MDN WAI-ARIA basics.

    Some general tips:

    1. Every focusable element (links, buttons, fields and anything with a tabindex value of 0 or greater) must have a clear, visible visual indication of :focus.
    2. Make sure element triggers work with all input devices; elements sensitive to :hover (e.g. menu flyouts) should also react to :focus...and screen readers that cannot hover or focus should have some way to get to the same content.
    3. Manage keyboard focus in the same way you manage visual attention grabbing:
      • If a button launches a modal or overlay, set a short JavaScript timeout and then transfer keyboard focus to the first focusable element in the modal or overlay (the short delay makes sure the screen reader parses the new content, so that it notices and announces the context change).
      • If that element can be closed, transfer focus back to the button that launched it after it closes.
    4. If you plan to implement any custom event handling, first read the guidelines regarding multipoint or path-based gestures, motion actuation and down-event pointer cancellation.
    5. Character key shortcuts can conflict with many assistive devices; make sure any shortcuts can disabled or remapped, or are only affect particular focused components.
    6. Users can pause, stop or hide any non-essential motion that starts automatically and lasts more than 5 seconds.
    7. Shoehorning accessibility into custom elements is error-prone and time consuming. The native, semantic <button> does everything <div aria-role="button" aria-label="fake button" tabindex="0" onclick="clickHandler"> does, and also doesn't need additional JavaScript to handle Enter and Space keypresses.

    In detail: Hiding Elements

    Before hiding an element, decide whether it should be hidden from all users or only certain users. The following design patterns work well in the right situation:

    1. To hide an element visually but still let all screen readers easily find and read it: use absolute positioning and opacity:0.
      1. The element will be invisible, but still has a touch target, so if a mobile screen reader user is running their finger around the page, it will be announced in that location. Just be sure to position it in an empty place on the page, so it can be independently touched. Useful for things like invisible section headers and skip-to-main links.
    2. To hide an element visually and hide the element's independent touch target: use the CSS clip pattern.
      1. This pattern reduces the element to a transparent pixel. Unlike the previous pattern, a mobile user is unlikely to find this hidden element out of context when running their finger over the page, but it will still be read in the context of its container. This is ideal for elements which explain their containers or context: hidden text alternatives inside icon buttons, abbreviation expansions, text alternatives for background images, etc.
    3. To prevent the keyboard from focusing a visible and focusable element (e.g., a redundant link): add an attribute of tabindex="-1". The cursor will jump right past it.
      • Note that a mouse user clicking on a component with a negative tabindex will have their focus redirected to the previous element, so you should reserve this attribute for small components (buttons, links) rather than large containers (<main>), or mouse users will be startled to find their browser scrolling up to the top of the page when they click.
    4. To prevent screen readers from reading a visible but non-focusable element (and all of its children): add an attribute of aria-hidden="true." 
      • Used to conceal irrelevant elements from screen readers e.g., the icon next to the accessible text in a button: <button><img aria-hidden="true" />Menu</button>.
      • This attribute should never be given to a focusable element or an element with focusable children, as the screen reader user will still be able to Tab to the element, but their screen reader will abruptly stop speaking.
    5. To prevent screen readers from focusing and reading a visible and focusable element: use both tabindex="-1" and aria-hidden="true".
      • Generally used to hide redundant links ("read more") or offscreen content.
    6. To hide an element from everybody: set the element to display:none; or give it an HTML hidden attribute.
      • Used for elements that should be completely unreachable: they not only become invisible, they are removed from the element lists for keyboard focus and screen reader landmarks. Generally used for things like inactive tabs.

    A note on temporarily-hidden content (hidden on scroll, lazy-loaded, etc): display:none and aria-hidden also remove elements from the page's list of elements a screen reader user can jump to. Since users often start with those lists to build their mental mode of the page and are not expecting the lists to change, if you want to temporarily hide content or page regions, hide the content, not the region, so that screen readers can still detect the landmark and header, and know that the hidden content exists. E.g., if you have a menu toggle button, place the toggle button inside the <nav> container and only toggle the menu items, rather than placing it above the container and toggling the whole container. This way the <nav> and <h2> are always available, and they tell the screen reader user where to go to reveal the hidden content:
    <nav>
        <h2 class="sr-only">Site Navigation</h2>
        <button>Menu</button>
        <ul style="display:none;">...</ul>
    </nav

    Hiding Patterns Compared

    Method Visual State Keyboard State Desktop Screen Reader Mobile Screen Reader
    opacity:0 Hidden Focusable Readable + Focusable Readable + Focusable
    CSS clip Hidden Focusable Readable + Focusable Readable, Nearly unfocusable (caution)
    tabindex=-1 Visible Unfocusable Readable, Unfocusable Readable, Unfocusable
    aria-hidden Visible Focusable Unreadable but possibly focusable (caution) Unreadable but possibly focusable (caution)
    tabindex + aria-hidden Visible Unfocusable Hidden Hidden
    display:none Hidden Unfocusable Hidden Hidden

    A Quick Self-Led Training

    1. Explore the W3C Annotated Accessible Homepage, which provides annotated right/wrong examples, including code samples.
    2. Read our guide to DIY testing, and do some DIY tests on the W3C demo site:
      • Try to navigate the inaccessible and accessible versions with your keyboard. Note the dramatic differences in focus indication.
      • Explore the W3C demonstration again in the WebAIM Wave Tool, to practice doing your own automated testing.

    Further Reading and Online Classes