Jump directly to: Content Table of Content Navigation



Visual Accessibility covers every user that is using a screen reader. This group encounters the most pain points when surfing the web. According to the NFB more than 7.5 million people in the United States are blind or have a significant vision loss. That is approximately twice the population of Los Angeles. This section focuses on the ones using a screen reader. A screen reader is a tool that reads the content aloud. There are different types of screen readers, the most popular Screen Readers are JAWS and Zoom Text:

Chart of primary screen reader usage showing decreases in JAWS, NVDA, VoiceOver, SA and significant increases in ZoomText and Window-Eyes.
Chart based on Data from webaim.org. A textual description can be found there.
Contribute to this section

Basic Facts

Contribute to this section

Hiding Elements

Contribute to this section


Contribute to this section


Contribute to this section

Navigation / Menus

Drop Downs

  • Drop downs can be only visually hidden to still be fully accessible for screen readers. Ideally the visuallyhidden class would then be toggled via javascript. See hiding elements. However, if your menu is complex you will have to use some javascript to correctly hide and show your submenus for all users (thus also for the screen reader users since otherwise they would be obliged to traverse everything everytime).

  • Usually a drop down is another ordered or unordered list inside the list item of the first ordered or unordered list:
  • skip code example to next item
    <nav role="navigation" aria-labelledby="exampleNav1">
      <h2 id="exampleNav1">Some Example Navigation</h2>
        <li><a href="#link">Section1</a></li>
        <li><a href="#link">Section2</a>
          <!-- submenu start -->
          <ul class="visuallyhidden">
            <li><a href="#link">Section2-1</a></li>
            <li><a href="#link">Section2-2</a></li>
          <!-- submenu end -->
        <li><a href="#link">Section3</a></li>
  • Consider having a toggling mechanism hiding/showing elements for screen readers as well, if you have a very complex menu. This might be done by toggling the aria-hidden attribute. Here is an example:
  • skip code example to next item
    var menuItems = document.querySelectorAll('.exampleButton');
    for(var i = 0; i < menuItems.length; i++) {
      menuItems[i].addEventListener('click',  function(e){
        var button = this;
        var buttonText = button.querySelector('.visuallyhidden');
        var subMenu = button.parentNode.querySelector('.example-submenu');
        if (!button.classList.contains('open')) {
          buttonText.innerText = 'hide submenu';
          // sadly, we have to set the focus on the first link element,
          // otherwise the screen reader does not notice the change (note how the press-button event is not announced)
        } else {
          buttonText.innerText = 'show submenu';
          subMenu.setAttribute('aria-hidden', 'true');
  • skip code example to next item
    <nav id="exampleNav2" role="navigation" aria-labelledby="exampleNav2title">
      <h2 id="exampleNav2title">Example Accessible Navigation</h2>
        <li><a href="#link">Section1</a></li>
          <a href="#link">Section2</a>
          <button class="exampleButton">
            <span class="visuallyhidden">show submenu</span>
          <ul class="example-submenu hidden" aria-hidden="true">
            <li><a href="#link">Section2-1</a></li>
            <li><a href="#link">Section2-2</a></li>
        <li><a href="#link">Section3</a></li>
    Visual output:


  • Breadcrumbs are a great way to contribute to the users overall understanding of a website and they are a great way to improve cognitive accessibility and user experience in general.

  • Use a nav tag with role="navigation" and aria-label="you are here". Also add a visually hidden clue like the word “current” to the last item.
  • skip code example to next item
    <nav class="breadcrumb" role="navigation" aria-label="You are here:">
      <a href="#link">Home</a>
      <a href="#link">About Sharks</a>
      <span class="visuallyhidden">Current:</span> Hammerhead Shark Facts.
    Visual output:

Skip Links

  • Not every screen reader has an advanced mechanism to jump to sections, headings, links, etc. or to skip repetitive blocks of content as tables, code blocks, graphics, etc. So, probably one of the most important things for a good screen reader user experience, is to provide links that allow to jump immediately to relevant sections and/or to skip repetitive content. On this page skip links were added before each code example to skip it. Moreover, visually hidden skip links were added at the beginning of the page, granting screen reader users the possibility to jump directly to either the table of contents, the menu or the content itself. Skip-links is also a best practice for physical accessibility since they have to traverse the whole page as well. You can reveal skip-links on this page by using your keyboard and tabbing through the page.

  • Skip links are like normal links pointing to anchors (elements with IDs) on your page. You might want to hide them visually, but should display them on focus to improve the usability of your page for keyboard users as well:
  • skip code example to next item
    // Script to unhide Skiplinks on focus
    var skiplinks = document.querySelectorAll('a.visuallyhidden');
    for (var i = 0; i < skiplinks.length; i++) {
      var el = skiplinks[i];
      el.addEventListener('focus', function() {
      el.addEventListener('blur', function() {
      }); }
  • skip code example to next item
    <a href="#linkNext" class="visuallyhidden">Jump to wanted position</a>
    <p>Annoying content</p>
    <a href="#link">Annoying Link</a>
    <p id="linkNext">Next item</p>
    <a href="#link">Wanted Link</p>
    Visual output:


    Jump to wanted position

    Annoying content

    Annoying Link

    Next item

    Wanted Link

  • Another kind of skip links, this one being visible for everyone, is to have a table of content linking to each heading within a long text. A table of content is nothing other than a navigation menu but with an ordered list.
Contribute to this section


Contribute to this section


Contribute to this section


Contribute to this section

Alternative description

Good alt-ernatives

  • do not describe your elements if they are purely decorative. If they are not, follow the guidelines below.

  • Aim to put the most important information at the beginning of your sentence.
  • skip code example to next item
    Green Apples
    Red Apples
    Yellow Apples
    <!-- Instead of -->
    Apples - Green
    Apples - Red
    Apples - Yellow
  • Add a long description if anything more than a short phrase is needed to describe the element.
  • Alternative descriptions are not cryptic messages for specialists, they will be read like every normal text. That is why you should use the same punctuation and write normal sentences in your alternative just as any normal text would be. Also, avoid abbreviations.
  • The text has to give the same information as the image. That is, if someone cannot see the image, they should still get the important information.
  • Keywords like “image”, “button”, “icon”, or “picture” in the alternative are redundant as they are announced by the screen reader.
  • Here are some examples for extensive descriptions (borrowed from 4syllabes):
  • skip code example to next item
    “Two staff members wearing safety gear, shown working outdoors collecting information on a laptop and mobile phone”, 
    “Eucalypt leaf, Tidbinbilla Nature Reserve, Australian Capital Territory”,
    “A researcher draws blood from a patient’s arm”,
    “3 young children, two boys and a girl, play with 2 golden labrador puppies, soon to be trained as seeing eye dogs”
  • Typically the alternative is not a literal description of the image, but conveys the meaning of the image. Here are some examples from w3 to illustrate this:
  • skip code example to next item
    > A chart showing sales for October has an short text alternative of "October sales chart". It also has a long description that provides all of the information on the chart.
    > A search button uses an image of a magnifying glass. The text alternative is "search" and not "magnifying glass".
    > A picture shows how a knot is tied including arrows showing how the ropes go to make the knot. The text alternative describes how to tie the knot, not what the picture looks like.
    > A picture shows what a toy looks like from the front. The text alternative describes a front view of the toy.
    > An animation shows how to change a tire. A short text alternative describes what the animation is about. A long text alternative describes how to change a tire.
    > The text alternative should be “print this page” rather than “(image of a) printer”, “search” rather than “magnifying lens” or “Example.com home page” rather than “Example.com logo”.
  • DO NOT write short descriptions, they do not add any value. Do not write anything like this:
  • skip code example to next item
    “banner”, “news item graphic”, “smiling people”,
    “plant”, “open day photo”, “decorative element”,
    “funny bread”, “needle”, “kids with toys”,

Purely Decorative

  • Visual elements that do not add any value should not be seen by screen readers. The Web Content Accessibility Guidelines 2.0 defines purely decorative images as: “serving only an aesthetic purpose, providing no information, and having no functionality”

  • Do not to add titles, descriptions or anything that could possibly be read. For images, make sure that there is an empty alt attribute without any space characters alt="" otherwise screen reader might read the filename instead. Just to be sure, add the aria-hidden="true" attribute which is supported by modern screen readers and will hide the element.
  • Note that traditional methods used to hide elements as visibility="hidden" or display="none" hide elements for everyone, also for screen readers. If you want to hide elements for users with vision, but keep them available for screen reader users, use the technique described in the Hiding Elements section. If you want to hide elements only for screen reader users and keep them visible for users with vision, use the attribute aria-hidden="true".
  • Elements used as part of the design as borders, ornaments and similar are purely decorative elements and should not be accessible.
  • Images and other elements being part of links, mostly to increase the clickable area and provide visual structure are annoying for screen reader users because they add more unnecessary noise, thus should not be accessible.
  • When the text alternative is adjacent to the element, the element itself should not be accessible to prevent hearing the same information twice.
  • skip code example to next item
    <img src="grandmothers.jpg" alt="">
    <strong>Grandmothers play an important role in our development:</strong>
  • Elements used for ambience should generally not be accessible unless that ambience is an important information.
  • Elements that convey branding, feelings, moods and anything else that is not related to the content are purely decorative. Face the fact that most users are there for the information and not to check out how cool the branding is. Brand values or messages can be communicated in text, but they require something more creative than a simple description of the content of an image.
  • According to Dey Alexanders observations during user-tests: screen reader users, even very experienced ones, sometimes become confused by text alternatives for decorative images. Some might even try to click on images (that are not links) because the text alternative mentioned a word or phrase related to their task.
  • Background images, article banners, thumbnails and small elements supporting links are most likely purely decorative.
  • The W3C Web Accessibility initiative put up a valuable decision tree that describes how to use the alt attribute of the img element in various situations.
Contribute to this section



  • Show errors not only with color. Write them out. Adding a text explaining which part was incorrect and why near the label and bundled errors on top (ideally with skip-links to jump to the incorrect form-field) enhances the user experience.

  • Use role="alert" with aria-live="assertive" or aria-live="polite". Assertive will be pushed immediately to the screen reader as soon as the element appears while Polite will wait until the screen reader is done with its current task. Read more on MDN. It is a good practice to add it on the title of your error summaries.
  • skip code example to next item
    <h1 role="alert" aria-live="assertive">3 Errors: [...]</h1>
  • Also, change the pages title of the page accordingly: “3 Errors –” or “Success –”

Anti Spam

  • As a general usability rule, try to avoid active anti-spams whenever possible, they often add an unnecessary level of complexity to your forms. However, if needed the following guidelines might be useful.

  • If you use captchas make sure to also provide voice checks and add an alternative way to still reach the goal even if the captcha fails. For example, an email address for direct contact.
  • Simple tests as “write ‘Hello World’ into this field:” are o.k. just make sure not to ask for things a blind person could not answer, i.e. asking for colors.
  • Make sure that your anti spam solutions are usable for everyone. Google did an o.k. job with their newest captcha.

Required Fields

  • Indicate required fields not only visually. It has become a convention to add an asterisk *, but explain that asterisk somewhere. You can also simply add the keyword “required”. Also, provide inline instructions when necessary.

  • Use the required attribute and aria-required="true" on required fields. Note that the screen reader will read required twice (once from the label and once from the aria).
  • skip code example to next item
    <label for="name">Name (required): </label>
    <input type="text" name="name" id="name" required aria-required="true">
    Visual output:
  • Users should be able to check their own input and correct it if the data are important.
  • Require the user confirmation for irreversible actions & provide undo mechanisms for reversible actions.
Contribute to this section


Contribute to this section

WAI-ARIA & Roles


  • aria-busy indicates when an element is loading. I.e. when elements are loaded dynamically aria-busy can be set to true while loading and to false when finished. If there is an error aria-invalid should be set.
  • skip code example to next item
    <ul aria-busy="true">
  • aria-controls identifies elements whose contents are controlled by the current element. Similar to the aria-owns attribute which identifies a visual, functional, or contextual parent/child relationship when it cannot be provided in the markup.
  • skip code example to next item
    <button role="button" aria-controls="amount">Increase amount by 1</button>
    <p id="amount">1</p>
    <div aria-owns="child">Parent</div>
    <div id="child">Child</div>
  • aria-describedby identifies elements that describe the current element.
  • skip code example to next item
    <div aria-describedby="#exampleDescription">Strange Thing</div>
    <p id="exampleDescription">Representing [...]</p>
  • aria-disabled indicates disabled elements, those should additionally be visually grayed out and the tabindex should be removed. Which is similar to aria-readonly, which also indicates elements where the user cannot change the value, but with readonly the user is still able to navigate to descendants.
  • skip code example to next item
    <input type="checkbox" aria-disabled="true" disabled>
    Visual output:
  • aria-grabbed and aria-dropeffect indicate the state in a drag and drop operation. aria-grabbed="true" indicates that an element is currently being dragged. aria-grabbed="false" indicates that the element not being dragged but can be. The aria-dropeffect should be added to all elements where the currently dragged element can be dropped. It can have multiple values separated by a blank space: copy, move, link, execute, popup.
  • skip code example to next item
    <div aria-grabbed="false">Draggable</div>
    <div aria-dropeffect="move execute">Target</div>
  • aria-haspopup indicates that the element has a popup context menu or sub-level menu. A popup is generally a group of items that appear to be on top of the main page content.
  • skip code example to next item
    <li aria-haspopup="true">
      <button role="button">Click me to reach sublevel</button>
        <li>Sub Level</li>
  • As seen in the hiding elements section aria-hidden hides elements from the screen reader.
  • skip code example to next item
    <p>This is <span aria-hidden="true">hidden</span>.</p>
    Visual output:

    This is .

  • aria-invalid announces to screen reader users that the elements have invalid data. I.e. you should add this to any form where the value entered by the user has failed validation.
  • skip code example to next item
    <input type="text" aria-invalid="true">
    <input type="text" aria-invalid="grammar">
    <input type="text" aria-invalid="spelling">
    Visual output:
  • aria-label is, with aria-labelledby probably the most used WAI-ARIA attribute. It defines a string that labels the current element. Used, for example, in structure, search-fields, breadcrumbs and inputs. If the label is visible in the document you should use aria-labelledby instead.
  • skip code example to next item
    <input type="email" name="email" aria-label="email:">
    Visual output:
  • aria-labelledby is, with aria-label probably the most used WAI-ARIA attributes. It defines an element that labels the current element. Used, for example, in structuring sections, navigations, SVGs and fieldsets. If the label is not visible in the document you should use an aria-label instead.
  • skip code example to next item
    <section aria-labelledby="example">
      <h2 id="example">This is an example.</h2>
    Visual output:
    This is an example.
  • aria-live indicates that an element will be updated. Is used i.e. with the alert role it sets the type of alert. Polite will wait for the screen reader to end its current task and then read the element aloud while assertive reads it out aloud immediately. Note: that it will be read out aloud as soon as its rendered and visible. It is a good practice to use it when fields have errors.
  • skip code example to next item
    <section aria-labelledby="example">
      <h2 id="example">This is an example.</h2>
  • aria-flowto is used to change the regular flow of a document. (poor support)
  • skip code example to next item
    <div aria-flowto="second">1</div>
    <div id="second">2</div>
  • aria-autocomplete indicates if completion suggestions are provided.
  • skip code example to next item
    <textarea aria-autocomplete="inline">The suggestions are provided inline, after the users input.</textarea>
    <textarea aria-autocomplete="list">A list of choices appears from which the user can choose.</textarea>
    <textarea aria-autocomplete="both">Both types are provided.</textarea>
  • aria-expanded indicates if the element is currently collapsed or expanded.
  • skip code example to next item
    <button aria-expanded="false" role="button">Toggle 1</button>
    <button aria-expanded="true" role="button">Toggle 2</button>
    Visual output:
  • aria-level indicates the level of an element. I.e. to expand the headings further down than level 6.
  • skip code example to next item
    <p role="heading" aria-level="9000">wow</p>
    Visual output:


  • aria-multiline indicates whether a text box accepts multiple lines of input or only a single line. This changes the default behaviour in forms: If set to true the enter button will not submit the form.
  • skip code example to next item
    <textarea aria-autocomplete="inline">The suggestions are provided inline, after the users input.</textarea>
    <textarea aria-autocomplete="list">A list of choices appears from which the user can choose.</textarea>
    <textarea aria-autocomplete="both">Both types are provided.</textarea>
  • aria-multiselectable indicates that the user may select more than one item from the current selectable descendants. Here is an example by James Craig:
  • skip code example to next item
    <div role="application">
      <h1 id="listlabel">ARIA Multiselectable Listbox Example</h1>
      <ul id="list0" role="listbox" aria-labelledby="listlabel" aria-activedescendant="list0_item1" aria-multiselectable="true" tabindex="0">
        <li id="list0_item0" role="option" aria-selected="false">foo</li>
        <li id="list0_item1" role="option" aria-selected="false">bar</li>
        <li id="list0_item2" role="option" aria-selected="false">baz</li>
  • aria-orientation is used in scrollbars, separators and sliders to show whether the element and orientation is horizontal or vertical. Example via MDN:
  • skip code example to next item
    <a href="#" id="handle_zoomSlider"
  • aria-pressed as for aria-checked and aria-selected does what the name suggest, they indicate whether the element is checked, selected or pressed. They can be set to true, false or mixed. Mixed being a mixed mode value for a tri-state element. However, selected has no mixed value. Usually when using standard HTML elements as buttons the state is identified by default and does not require extra aria attributes. They might make sense if you construct your own elements.
  • aria-required indicates that user input is required. As seen under required fields This should be added to any required form-field. do not forget to provide a visual clue as well.
  • skip code example to next item
    <input type="text" name="name" required aria-required="true">
    Visual output:
  • aria-sort indicates whether items in a table or grid are sorted in ascending or descending order. Simplified example of the one from maxdesign:
  • skip code example to next item
    <table id="exampleTable">
        <th aria-label="Account name: Ascending sort applied, activate to apply a descending sort" aria-sort="ascending" aria-controls="exampleTable" role="columnheader" scope="col" tabindex="0">
          Account name </th>
      <tbody aria-relevant="all" aria-live="polite">
        <td>Personal account</td>
        <td>Main Account</td>


  • Here is a convenient list or roles extracted from the W3C Reference in alphabetical order.
  • which role is for what?
    role=""joblearn more
    alertA message with important, and usually time-sensitive, information. See related alertdialog and status.W3C
    alertdialogA type of dialog that contains an alert message, where initial focus goes to an element within the dialog. See related alert and dialog.W3C
    articleA section of a page that consists of a composition that forms an independent part of a document, page, or site.W3C
    bannerA region that contains mostly site-oriented content, rather than page-specific content.W3C
    buttonAn input that allows for user-triggered actions when clicked or pressed.W3C
    checkboxA checkable input that has three possible values: true, false, or mixed.W3C
    columnheaderA cell containing header information for a column.W3C
    comboboxA presentation of a select; usually similar to a textbox where users can type ahead to select an option, or type to enter arbitrary text as a new item in the list.W3C
    commandA form of widget that performs an action but does not receive input data.W3C
    complementaryA supporting section of the document, designed to be complementary to the main content at a similar level in the DOM hierarchy, but remains meaningful when separated from the main content.W3C
    compositeA widget that may contain navigable descendants or owned children.W3C
    contentinfoA large perceivable region that contains information about the parent document.W3C
    definitionA definition of a term or concept.W3C
    dialogA dialog is an application window that is designed to interrupt the current processing of an application in order to prompt the user to enter information or require a response.W3C
    directoryA list of references to members of a group, such as a static table of contents.W3C
    formA landmark region that contains a collection of items and objects that, as a whole, combine to create a form. See related search.W3C
    gridAn interactive control which contains cells of tabular data arranged in rows and columns, like a table. Also, see gridcellW3C
    groupA set of user interface objects which are not intended to be included in a page summary or table of contents by assistive technologies.W3C
    headingA heading for a section of the page.W3C
    imgA container for a collection of elements that form an image.W3C
    inputA generic type of widget that allows user input.W3C
    landmarkA region of the page intended as a navigational landmark.W3C
    linkAn interactive reference to an internal or external resource that, when activated, causes the user agent to navigate to that resource. See related button.W3C
    listA group of non-interactive list items. See related listbox and listitem.W3C
    logA type of live region where new information is added in meaningful order and old information may disappear.W3C
    mainThe main content of a document.W3C
    marqueeA type of live region where non-essential information changes frequently.W3C
    mathContent that represents a mathematical expression.W3C
    menuA type of widget that offers a list of choices to the user. See related: menuitem, menuitemcheckbox and menuitemradioW3C
    menubarA presentation of menu that usually remains visible and is usually presented horizontally. Related: menuitem, menuitemcheckbox and menuitemradioW3C
    navigationA collection of navigational elements (usually links) for navigating the document or related documents.W3C
    noteA section whose content is parenthetic or ancillary to the main content of the resource.W3C
    optionA selectable item in a select list.W3C
    presentationAn element whose implicit native role semantics will not be mapped to the accessibility API.W3C
    progressbarAn element that displays the progress status for tasks that take a long time.W3C
    radioA checkable input in a group of radio roles, only one of which can be checked at a time. Related: radiogroup.W3C
    rangeAn input representing a range of values that can be set by the user.W3C
    regionA large perceivable section of a web page or document, that is important enough to be included in a page summary or table of contents, for example, an area of the page containing live sporting event statistics.W3C
    roletypeThe base role from which all other roles in this taxonomy inherit.W3C
    scrollbarA graphical object that controls the scrolling of content within a viewing area, regardless of whether the content is fully displayed within the viewing area.W3C
    searchA landmark region that contains a collection of items and objects that, as a whole, combine to create a search facility. See related form.W3C
    sectionA renderable structural containment unit in a document or application. Related: sectionhead.W3C
    selectA form widget that allows the user to make selections from a set of choices.W3C
    sliderA user input where the user selects a value from within a given range.W3C
    spinbuttonA form of range that expects the user to select from among discrete choices.W3C
    statusA container whose content is advisory information for the user but is not important enough to justify an alert, often but not necessarily presented as a status bar. See related alert.W3C
    structureA document structural element.W3C
    tabA grouping label providing a mechanism for selecting the tab content that is to be rendered to the user. See related: tablist and tabpanel.W3C
    timerA type of live region containing a numerical counter which indicates an amount of elapsed time from a start point, or the time remaining until an end point.W3C
    toolbarA collection of commonly used function buttons or controls represented in compact visual form.W3C
    tooltipA contextual popup that displays a description for an element.W3C
    treeA type of list that may contain sub-level nested groups that can be collapsed and expanded. Also, see: treegrid and treeitem.W3C
    Less common: application document row rowgroup rowheader separator structure textbox widget window
Contribute to this section

Testing & resources