Quick-and-Dirty Accessibility

Muddy dog photo by Nathalie Spehner

If you are a web developer who is not hugely familiar with the topic of web accessibility, your first encounter with it may be confusing. I’ve been there. It sucks to be presented with a list of changes to make your finely-crafted code more accessible, it can feel like scope creep, “someone else’s job”, or a critique of your work. The goal of this article is to give you tools – not more work.

We all have a part to play in making a website accessible. Before you start cutting code, the people who hand you the designs should have considered accessibility (is the color palette suitable? Is there good contrast? Will the creative UI be keyboard operable?). As a developer, you are in a great position to ensure that accessibility is factored in before the first line of code is written (something that you can and should do).

I’m not going to list all the reasons why an accessible digital world is important. Today we’re moving fast. If you would like a deeper dive into accessibility check out The History of Digital Accessibility and Why it Matters or Why Accessibility Matters. They wonderfully illustrate its importance. I have also added deep dive links at the end of each section if you want to continue learning.

I want to focus on the practical: How do you, as a developer, make things more accessible and do it fast?

A single article isn’t going to make you an expert in creating accessible markup, but this article should get you a lot closer to that point and may be easier than you might think. Note: I’m going to avoid the nitty-gritty specifics as much as possible so that you can move as fast as possible. I’ll link to the requirements for anyone who’s in a position to really get their hands dirty.

Don’t just build what they tell you to

You don’t always have to build what a designer gives you without first giving it some thought. If you can push back on a design because of its technical limitations, then you can push back if you have accessibility concerns, too. It’s also hugely beneficial to flag such issues at this stage, it is significantly more costly, and time consuming, retrofit an application to be accessible after you’ve pushed it through production.

Here are two areas that your designer should address:

Check color contrast

4 lines that read 'Thank you for caring about accessibility' with good contrast levels in column 1, the same 4 lines with poor contrast in column 2
A comparison of some good and bad contrast ratios between text and background

A “color-contrast ratio” describes how much light-to-dark (or dark-to-light) difference there is between two colors. And as a rule of thumb, if the color contrast of all of your foreground elements (like text and buttons) is always at least 4.5 times darker or lighter than their backgrounds, you’ll always pass WCAG’s contrast guidelines.

You can test the color-contrast ratio of any two colors using a contrast analyzer: If your IDE doesn’t include one, our Colour Contrast Analyser (CCA), which is a free app for macOS and Windows, is great. For nuances like large text, adjacent colors, and interactive controls, see Color Contrast Analyzer 2.2 – updated contrast ratio.

Request designs that cover all the main viewport sizes

Three obfuscated layouts of the TPGi blog showing the difference in mobile, tablet and desktop layouts
Comparison of layouts across three breakpoints

It’s pretty much a given these days that a web site/app will need to have a mobile view and possibly a tablet view also. This will make sure that low-vision users can navigate all the features and text in your app or website. When your viewport is sized to as small as 256 px high by 320 px wide, your app or website needs to be viewable without requiring both vertical and horizontal scroll bars at the same time. So, if you don’t get mockups with a mobile and tablet view, send them back. The designer still has some work to do. (Tip: if the designer later provides mobile or tablet views where they have simply omitted large sections or functionality present in the desktop view to make it fit, unfortunately that won’t cut it. This is a SC 1.4.10 Reflow failure)

All modern desktop browsers provide the capability to emulate mobile and desktop views, as follows:

  • Chrome: open the inspector and then select the “toggle device toolbar” button
    (or press Ctrl+Shift+M on Windows or Cmd+Shift+M on macOS)
  • Safari: enter responsive-design mode with Cmd+Ctrl+R.
  • Firefox: open the inspector and then select the “responsive-design- mode” button
    (or press Ctrl+Shift+M on Windows or Cmd+Shift+M on macOS).

With all of the above, you can manually set the viewport’s dimensions. With Firefox and Chrome, you can additionally select from some typical devices and that then sets the viewport widths accordingly.

Now that you’ve passed some of the accessibility responsibility back to the designer, let’s talk about what you can do.

Semantic HTML

Two web layouts shown side-by-side. On the left: a structure conprising div elements. On the right: the same layout but with semantic HTML elements
Non-semantic vs semantic layout. Which is better? Read on and find out.

Ok. This can solve the most problems out of the gate: <div> and <span> elements aren’t always your friends; there’s a good chance there are more suitable HTML elements that you should use instead. Why is this important? A huge chunk of what your web pages need to become accessible is baked into semantic markup.

Let’s take the humble <button> as an example. But let’s suppose that instead of using a <button> element, you had wanted to recreate the same functionality from scratch:

  • You’d need to make sure your button can be activated when the user presses SPACE or ENTER.
  • Your button would need a visible focus indicator.

Your button would need to have a “button” role. If you use a <div>, you’d have to code and test all of that. But if you’re using the semantic <button> element, it’ll work right out of the gate. Plus, you have the option to add the type attribute when in a form.

On top of that, semantic elements are intuitively named. “What’s the element for the header of the page?” Oh yeah, <header>. “What do I use for a table, an article, or the footer?” <table> <article> and <footer> respectively. Once you get the hang of using semantic elements, you’ll never go back.

Put down the mouse for a bit

It’s time to try using your keyboard to navigate your site.

First up, if you’re on macOS, you might need to make a couple of settings tweaks first:

  • Go to System Settings, then Keyboard, and enable the switch that’s next to “Keyboard navigation.”
  • In Safari preferences, select ‘Advanced’ then check Press Tab to highlight each item on a webpage.

(And that’s just a one-time settings adjustment, so once you’ve got those in place, you’ll be good to go.)

To try out keyboard navigation, open your website in your browser and press the TAB key once: You should see some visual indication of where the focus point is within the page. (I promised you this would be fast!) You can use the TAB key to navigate from the top of your webpage toward the bottom, and you can use SHIFT+TAB to navigate back up.

If you don’t see styling around any of the elements to tell you which one is focused, that means someone has forced the focus not to have an outline. This is a pretty big problem because it makes keyboard navigation extremely difficult, sometimes impossible, for users that use key events, like TAB, to navigate.

There are a thousand ways to build a site, which can make this a bit difficult to resolve. If you are not seeing focus, run a keyword search for “focus” in files that modify the site’s styles. Where you find “focus” is being used to modify CSS ensure the modifications do not result in an element or container’s border being invisible or removed when it receives focus. Common causes are:

  1. outline or outline-style property set to none
  2. border or border-color property where the color is the same or very close to the color surrounding the focused element

Be aware that these focus styles may be nested and a more specific focus style, i.e. button:focus { }, may be overriding a more general one, i.e *:focus { }.

Next, pay attention to how the focus point moves through the page. It should move how an everyday user would expect to read a page in a book. This is normally left to right and then top to bottom (or right to left and then top to bottom, depending on the user’s language).

Now, have you noticed any times when you pressed TAB and you didn’t see focus styling around any of the elements in the page? If so, one of three things are likely:

  1. Nonvisible controls might be in the focus order. Either remove them from the focus order (by removing tabindex from the control) or if they’re natively focusable controls, add a “make it invisible” CSS class using display:none.
  2. If you had to refactor your CSS to display visible focus in the last section, this could mean you missed CSS that is forcing none visible focus.
  3. Finally, !important might have been used to suppress or remove the visible focus styles. The !important rule increases the weight of the CSS property it is attached to, and can be used to force the UI to your will by overriding previous style rules, regardless of their specificity. Here is a simple example.
button:focus {
    outline: red;
*:focus {
    outline: none !important;

The outline property in the button:focus selector would typically be a higher weight than the *:focus selector because button:focus is more specific, but since !important has been added to the outline property in the less specific *:focus selector, that version will be applied. Note, the !important rule only overrides properties preceding it.

!important is similar to a “hot fix” in software, and is just as unreliable long term. If you find this property in your CSS it could be blocking the visible focus from appearing as expected. Wherever possible !important should be removed and replaced with a “true fix”. Think about it like this, if !important can force the UI into the shape you want; so can more robust styling. Good news: your CSS is now even cleaner.

Names, names, names

A collage of words, in a sort of tag-cloud style, with the most prominent words being 'big' and 'data'

To steal from my betters, “What’s in a name?” Well, a lot actually. Imagine that you’re trying to navigate your site—but all the form controls are nameless “select,” “checkbox,” and “input” elements. You’d probably find that very confusing. You might even think that someone was playing a joke on you. Unfortunately, a ton of screen-reader users experience websites in that way because controls’ names aren’t conveyed to assistive technologies.

You’ve most likely made sure that there’s visible text next to or above each form field to let users know the context of the field—such as “First Name”, “Phone Number”, and so on. Have you also created a programmatic name for the field? A programmatic name is what many assistive technologies convey to their users.

Possibility 1: You used a <label> element that’s next to its form field. In that case, make sure that the <label> has a for attribute, the control has an id attribute, and that the for attribute’s value matches the id attribute’s value.

<label for="email">Email</label>
<input id="email" ...>

Possibility 2: You have a visible name, but didn’t use a <label> to create it. In that case, one option would be to use a <label> element instead of the one you chose originally. You could then go with Possibility 1 and call it a day. Alternatively, you can give the element that surrounds the descriptive text an id attribute. Give the form field an aria-labelledby attribute. Then set the aria-labelledby attribute’s value to match the value of the id attribute.

<div id="email">Email </div>
<input aria-labelledby="email" ...>

Possibility 3: You’re using some imagery to visually indicate the purpose of the control. This is commonly seen in search fields using a magnifying-glass icon instead of a text based name. To resolve this, first ensure the image is hidden from assistive technologies. See the Images section for instructions on how to do this. Then, add an aria-label attribute to the form field. Finally, set the attribute’s value to a human-understandable description of the control. In this search example, that would be aria-label="search"

<input aria-label="search" ...>

Some users interact with computers through speech-based interfaces. And though these speech interfaces don’t solely rely on each form field’s programmatically associated label, people who use speech-based interfaces will have a significantly easier time using your page when all controls have programmatically associated labels.


Images are everywhere on the web. Do you ever wonder how people who cannot see manage to understand these images? For the most part, they do it through a text description of the image. Don’t worry, no one is asking you to describe every color, curve and shade of the image. You just need to make sure you convey the informational context—otherwise known as the meaning—of the image.

First things first, though–does the image convey information? If the image doesn’t convey information or it is decorative, you should hide it from assistive technologies. An example of a decorative image would be a scenario where you have an edit button that contains a pencil icon and the word “edit.” There, the pencil icon would be considered decorative because the word “edit” already conveys what the control is for. If you have a decorative image that’s using an <img> element, leave its alt attribute empty. For other image elements, add aria-hidden="true":

<img src="..." alt=""> 
<svg aria-hidden="true">...</svg>
<canvas aria-hidden="true">...</canas>
<picture aria-hidden="true">...</picture>

If the image does convey information, you have a couple of options based on the particulars of the image.

  1. If you have an image of text, add the text as the value for its alt attribute. (Or—better yet—replace that image with real text and use CSS to style the text however you like.)
  2. If your image is using an <img>, <picture>, or <svg> element, add a short description of the image
Satire on the Peninsular war. Napoleon, seated above Madrid, fires a cannonball marked ‘Friendly visit’, from which soldiers fall to attack the city. He blows a bubble enclosing several royals. Napoleon swears friendship, while they look dejected.

<img> – include an alt attribute. The description should be the value of the alt.

<picture> requires an <img> element as its child. Use the alt attribute in the same manner as above.

<svg> are a bit more complicated.

  1. Add the role="img" attribute.
  2. Add the aria-labelledby attribute.
  3. If you can convey the meaning of the image in a few words place them in the <title> element. This element should be a child of <svg>. Ensure the <title> element includes an id attribute
  4. If you need more room to described the image add the <desc> element. This should also be a child of <svg>. Ensure the <desc> element includes an id attribute
  5. Finally, add the value of the title and, if used, the desc element as the value for the previously added aria-labelledby.
<svg role="image" aria-labelledby="title desc" ...>
    <title id="title">Satire on the Peninsular war</title>
    <desc id="desc"> The walrus and a kangaroo armies embrace each other on a field of grass. The wind howls. Light shines on the walrus general helping a kangaroo joey to safety</desc>

There are cases where an image may contain a large amount of information, like a chart, infographic, or graph. This is beyond the scope of “Quick and Dirty”, but I can give you a few high-level options.

  1. For unstructured data, you can use the <details> and <summary> elements as a disclosure.
  2. For structured data, you may be able to use a table to convey the information as well. You can place this table on the same page as the image or link to it.
  3. For the adventurous, you could refactor the image as a combination of CSS and html. This has the added benefit of allowing more interactive features, but this can be very fragile – breaking when a user resizes the screen, changes font settings, or updates text spacing.

<img src="..." alt="a child playing Sonic on an arcade machine">
<svg role="img" aria-label="a child playing Sonic on an arcade machine">...</svg>
<canvas role="img" aria-label="a child playing Sonic on an arcade machine">...</canas>

In summary

These five techniques won’t, by themselves, make your applications and websites accessible, but they’ll get you much closer. By adding them to your development toolkit, you’ll be reducing the risk of more costly, and complicated, accessibility fixes further down the road (and possibly the risk of legal challenges). Plus, no matter what you’re building, you’ll be helping people, and that’s always worth doing.


Contrast and resize deep dive

HTML deep dive

Focus deep dive

Labels deep dive

Images deep dive

Categories: Technical

About William Driver

I spent 7 years as a developer using front-end technologies, Java, and C++, with a smattering of other frameworks and languages here and there as needed. The last 5 year I have focused on digital accessibility. Meaning I invest my time teaching designers and developers how to make websites and applications that everyone can use. Beyond that, I have too many kids, too many dogs, and too much fun.


Add Your Comment