Skip to Main Content

Web Developer Accessibility


Web accessibility refers to the inclusive practice of removing barriers that prevent interaction with, or access to websites, by people with disabilities. When sites are correctly designed, developed, and edited, all users have equal access to information and functionality.

While a lot of documentation currently exists online describing how to create accessible web-based content, a lot of it is arguably hard to digest and doesn’t address the “how-to” in a concise and practical way. This document aims to provide an easy general guideline for UH web-developers to follow when creating their web-applications and static-pages.

The Scanner "Issue"

Many web developers often turn to one of the many available automatic web-accessibility checkers such as Google Lighthouse, Siteimprove's Chrome extension, or an online tool like 'acheckera''. While these tools are incredibly helpful and should be in every front-end web-developer’s toolkit, they cannot be solely relied on when ensuring that web-content is accessible.

Scanners are fantastic at ensuring big ticket items like alt-text, color contrast ratios, and aria-roles are all being used properly, but they can’t cover the entire spectrum of what makes a site accessible. Some things not checked include: ARIA landmarks, keyboard navigation, focus state indicators, hidden-state checking and various others that this article will quickly address.

ARIA Landmarks & Semantic HTML

An aspect that makes web-pages truly accessible are ARIA landmarks. You can think of ARIA landmarks as a form of "table of contents" for screen-reader users. They effectively allow screen-readers to jump around to logical sections of a page in a quick fashion. Without landmarks, a screen-reader user would be read giant blocks of text when they navigate to the main content of a page.

                            <div id="header">
                              <div id="top-nav">


                            <div class="page-container">
                              <div id="main-content">


                              <div id="sidebar">


                            <div id="footer">

An example of common unaccessible markup. To make this accessible, add “role” attributes on each div.

The easiest way to implement ARIA landmarks is to simply use the semantic tags that HTML5 comes with out of the box. Using semantic tags eliminates the need to remember different ARIA roles such as “banner” and “complementary”, and instead allows you to compose your document with easier to remember tags such as <header>, <main>, and <aside>. When combined with your favorite HTML linter, you can easily ensure your markup is semantic and properly written.



                            <div class="page-container">





An example of accessible markup. Accessible technologies know where the main page content is as well as supplementary material.

To handle the aforementioned case of “reading a giant block of text”, developers can rely on tags such as <article> and <section> to cut their documents into logical chunks. When using these tags, developers need to ensure that they are labeling and using them properly as just using them isn’t enough to be accessible.

                            <article id="what-accessibility" aria-labelledby="accessibility-heading">

                              <h2 id="accessibility-heading">What makes an accessible page?</h2>
                              <p class="body-text">Read on to find out</p>

                              <section id="intro" aria-labelledby="intro-heading">
                                <h3 id="intro-heading">Semantic markup</h3>

An example of sectioning off logical chunks in your markup.

There are plenty of general good HTML practices that accessibility calls for, but the gist of it goes as follows:

  1. Use semantic tags properly. Try to reserve <div>’s purely for layout and styling reasons as much as possible. Refer to this helpful MDN article for a list of all semantic tags. Remember to adhere to limits on tags like <main>!
  2. Label your elements. Use “aria-label” and “aria-labelledby” liberally. Refer to the above markup for a practical example.
  3. Structure your content. Layout your HTML in a way that makes sense. Headings come first, followed by paragraph text, etc.. If you remove your CSS files from your site, your web content should still follow a logical and sensible reading order.

Semantic and well structured HTML is almost the entirety of the accessibility battle. While there are definitely other nuances to accessibility that aren’t covered purely by HTML (‘position: absolute’, AJAX, and dynamically displayed content), well structured markup is the building block for accessible web content.

Keyboard Navigation & Focus State

Users with motor impairments may rely on a keyboard or switch device to navigate web content, so establishing an accessible navigation layout is imperative to ensuring an accessible experience for all users.

As a keyboard navigator traverses through a webpage, they focus on various interactive controls that are displayed on a page such as links, inputs, and buttons. It’s important to ensure that every item that is focusable has a clearly defined focus indicator. By default, web browsers usually ship with a default set of style definitions that will handle this for developers. For example, Chrome’s blue outline and Firefox’s dashed border are two “out-of-the-box” focus indicators. However, these default styles are often overwritten via CSS, which poses a problem if handled improperly.

Example of Chrome's blue outline focus indicator Chrome's default focus outline indicator

This isn’t to say developers have to use the default focus indicators built in to their browsers. One common example of focus on <button> elements is an outline. However, this can sometimes be a visually undesireable effect that clashes with the visual theme your content is going for. Instead of just removing the outline via outline: none; in CSS, consider creating an effect like darkening or lightening the background or even scaling the size of the button on focus. For example:

                            .primary-btn {
                                [...] /* other styles omitted for brevity */
                                background: linear-gradient(-180deg, #636abd, #474fac);
                                outline: none;

                            .primary-btn:focus {
                                background: linear-gradient(-180deg, #6e76d1, #545eca); /* lighten the background */
                                transform: translateY(-1px) scale(1.05); /* slightly raise and increase the size */

Can be an alternative that still provides a clear indication of focus to a user while removing the default focus styling.

DOM Order

DOM order can generally be referred to as the order in which elements are written in your HTML. For example, consider the following markup:

                          <button class="primary-btn">First</button>
                          <button class="primary-btn">Second</button>
                          <button class="primary-btn">Third</button>

Pressing Tab will cause your focus to move through the buttons in an order you would expect.

It’s important to be aware of common CSS rules that can sometimes “break” the corresponding DOM order and expected Tab order on your page. 3 of the most common ways to break this coupling of order is via: Floats, Flexbox, and CSS grid. The snippet below will result in a decoupling of DOM order and Tab order. Feel free to try tabbing through these buttons to see the disparity for yourself.

                          <div style="display: flex;">
                              <button class="primary-btn" style="order: 3;">First</button>
                              <button class="primary-btn">Second</button>
                              <button class="primary-btn">Third</button>

Hidden Content & tabindex

In cases that developers have content that isn't currently displayed (but will be at some point) but also needs to be focus-navigable like a responsive side-nav, they would need to utilize the ‘tabindex’ HTML attribute to manage the focus of their page. Generally, whenever new or dynamic content is displayed on a page, it’s an accessible pattern to have the current focus jump to that new content.

‘tabindex’, in conjunction with focus(), allows developers to explicitly handle tab ordering on their web-content when necessary. ‘tabindex’ can be applied to any element — although it is not necessarily useful on every element — and takes a range of integer values.

‘tabindex=0’: Inserts an element into the natural tab order. The element can be focused by pressing the Tab key, and the element can be focused by calling its focus() method.

‘tabindex=-1’: Removes an element from the natural tab order, but the element can still be focused by calling its focus() method.

                                <button class="primary-btn" tabindex="-1">I'm not focusable</button>

Any tabindex greater than 0 jumps the element to the front of the natural tab order. If there are multiple elements with a tabindex greater than 0, the tab order starts from the lowest value that is greater than zero and works its way up. Using a tabindex greater than 0 is considered an anti-pattern.

Jumping Focus

As mentioned earlier, whenever ‘dynamic’ content is shown on the page, it’s ideal to jump the current focus to that new content. To do this, you would select the content area, give it a tabindex of -1 so that it doesn't appear in the natural tab order, and call its focus() method. Below is a snippet showing one way to accomplish this:

                            const dynamicButton = document.querySelector('#dynamic-toggler');
                            const hiddenContent = document.querySelector('.message');

                            dynamicButton.addEventListener('click', () => {
                                // toggle the visibility and opacity to show the item
                       = 'visible'; // visibility must be set before toggling focus
                       = '1';

                                hiddenContent.focus(); // <- important!
If you are unfamiliar with arrow functions, you can refer to:

You can also see this in action by clicking the button below. The focus will be indicated by a blue outline.

This Is Some Dynamic Content

You should see the focus indicator on this element. It is indicated by a blue outline.

Focus Link

There are plenty of good practices when it comes to keyboard navigation and focus, but the gist of it goes as follows:

  1. Ensure logical tab order. Tabbing through the page follows the visual layout. Users cannot focus on elements offscreen or hidden.
  2. All interactive controls are keyboard focusable. Ensure that there is an apparent focus indicator for all controls.
  3. Focus new content when it’s displayed. Use tabindex and focus() to accomplish this.
  4. Avoid adding focus to non-interactive items. Headings and plain text generally do not need to be focusable.
  5. Prevent focus trapping. Ensure your keyboard users always have a method to escape or navigate through elements. Modals and dialogs are often culprits of this.

Handling Hidden Elements

It is common for web content to dynamically display or render elements depending on certain criteria or conditions such as viewport width. Due to the increasing popularity and utilization of mobile browsers and various screen sizes, dynamic content has become more prominent than ever before. It is a web developers responsibility to ensure these types of content are still accessible while still providing rich user experiences.

Thankfully, browsers have a few native ways of signaling to assistive technologies when an element is hidden.

Use display: none or visibility: hidden to signal to assistive technologies when content is out of view.

display: none;This CSS rule will signal to assistive technologies that an element is to not be parsed. Keep in mind that display: none; also removes an element from the document flow and will cause elements on the page to ‘shift’ if this is dynamically toggled.

visibility: hidden;This CSS rule will also signal to assistive technologies than an element is to not be parsed. This rule does not remove an element from the document flow and will prevent sudden shifting in your site.

Use display: block or visibility: visible to signal to assistive technologies when content is out of view. As it implies, other display properties also work as well.

HTML ‘hidden’ attribute. This attribute behaves similarly to display: none;. It removes an element from the document flow and will cause elements to shift if it is dynamically added. Generally, you should try to use either of the two options above before reaching for this.

Conclusion & More Resources

Web accessibility doesn’t need to be a daunting topic. While there is admittedly a lot of nuance and information to consider when it comes to accessibility, there are a lot of good general practices that will cover the majority of all web content. In general, ITS recommends:

  1. Audit your site with an automated checker. Google Lighthouse or Siteimprove’s Chrome extension will give detailed reports on problem items and how to fix them.
  2. Create semantic HTML. Refer above for info on this.
  3. Test your site with a keyboard. Fix any focus issues you come across.
  4. Test your site with a screen reader. Ensure all content on the page is reachable with a screen reader.

Please keep in mind that this article does not cover all aspects of web-accessibility. There are other important topics such as aria-live regions that should be reviewed and learned about as well if you expect to utilize AJAX in your web-pages. This article intends to serve as a quick guideline or reference point for common accessibility patterns that will help developers reach AA compliance in a ‘true’ fashion.

For more detailed information on web accessibility and the topics here, Google has an excellent article at:

Additionally, Google offers a free course on web accessibility available at: