Common Accessibility Errors

Common Accessibility Errors

Common accessibility errors and how to fix them.

Hi friends 👋

I wrote an article already, giving you an introduction to accessibility. I highly recommend reading that one if you're completely new to Accessibility.

In this article, we'll cover common accessibility errors, based on WEBAIM's accessibility analysis, and how to fix them 🥳.

Images and Alternative Text

Alternative text makes sure that our relevant images are perceivable to everyone. Every non-text content that conveys something to the user needs to have a text alternative. In many instances, it will be images, but it could be any media, that is why for example transcripts exist for videos.

We need to provide an alt attribute for all images, even the ones that don’t convey anything. If you don't, screen readers will read whatever is in the src attribute. Imagine a screen reader reading: niofnid_leopard-diamond-ggf2.png. Definitely wouldn’t be a pleasant experience.

You may ask now, what if you don’t want to provide an alt because the image doesn’t really convey anything? Then you can have an empty alt tag: alt=””. Now, the screen reader will skip the image completely. This is much better than the src attribute being read.

There are multiple benefits of writing alternative text. The first is assistive technology reads it to help folks understand what it’s conveying. Alternative text is also displayed if images are not loading, which makes the content still perceivable to an extent. Finally, it also helps provide meaning for search engines, which is good for SEO.

Adding an alt attribute is pretty simple:

<img src="image.png" alt="descriptive text" />

Now, you may be wondering when to leave the alt attribute empty, when we need it and when it can be skipped by screen readers.

Questions you can ask yourself:

  • If I was to remove this image, would I lose any context? Would the context be less perceivable?

  • Does the image do something? Is it inside a button or a link?

Good text

Now, when the screen reader reads the image, it usually reads the accessible name and then says what type of content it is.

Bad example:

<img src=”four-cats.jpg” alt=”An image of four cats” />

This will usually be read as An image of four cats, image. It is redundant to mention that the image is an image considering it will be mentioned by the screen reader. Focus on what the content conveys, not its type.

The most used screen reader, NVDA (NonVisual Desktop Access), would read it as An image of four cats, graphic.

Take into account that all screen readers do not function (read the content) in the exact same way, in this context, NVDA says graphic instead of image.

Good example:

<img src=”four-cats.jpg” alt=”Four cats” />

This would be read as Four cats, image.

What if you have an image inside a link or a button, how to go about that?

Your alt tag in the image will be used as the accessible name for the link.

    <a href="">
        alt="facebook twitter profile"

The above example would be read as facebook twitter profile, link when the link receives focus.


If you're interested in accessible SVGs, I strongly advise reading this article: Accessible SVGs.


When it comes to forms, the first thing to bring up is, placeholders are not labels!

Placeholders disappear as soon as you type in it, making it difficult for users to double-check their inputs. Placeholders should be used to show sample values to the users, we will also have a look at how this may be read by screen readers.

We need to label our form control and inputs. We also need to connect those labels with the form inputs, because the input's name will be the text content of the connected label.

Proper Form Labeling Techniques

I will cover two techniques that I use when it comes to properly labeling my form inputs.


If I want my label to be visible, I will do it in the classic fashion, connecting it with the input, by using the for and id attributes.

    <label for="username">Your username</label>
    <input type="text" id="username" placeholder="naruto" />

The above example would be read as Your username with hint naruto, edit text when the input receives focus.

As mentioned before, the voiced text may differ depending on the screen reader.

This goes to show the importance of placeholders being used correctly, sample values, in order for the user to get a hint on what type of text they should enter.


What if I want to hide the label for some reason?

I would follow the exact sample as described above, but I would hide it visually (not seen by the eyes), but not remove it from the DOM.

This can be done by applying a class of sr-only which contains the styles to hide the label visually.

    <label for="username" class="sr-only">Your username</label>
    <input type="text" id="username" placeholder="naruto" />

The styles of sr-only:

.sr-only {

Now the label will be visually hidden, but still, the input is fully accessible for users using screen readers.


The biggest issue among the common issues was color contrast.

Improper color contrast has a negative effect on colorblind people, low vision people, and people with aging or strained eyes.

Don't rely solely on colors to communicate something to the users

Taken from WCAG2.0 1.4.1 Use of Color:

Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.

Check the color contrast on everything that needs to be checked

Now, reducing the opacity would hurt the contrast, when it comes to disabled buttons and making them accessible, I highly advise reading this article: Making Disabled Buttons More Inclusive.

Links and buttons

Another common issue was empty links and buttons, but let's first have a look at the difference between links and buttons.

When should we use a link, and when should we use a button?

This is an important question because it is often asked and also a decision on how the screen reader will voice the content, as well as the native functionality itself, for example, with a button, you can't open a new tab, because it is not a link!

  • Button perform actions.

  • Links navigate to places.

  • Always use links with an href attribute, ensuring that it is used to actually navigate somewhere.

  • If you need an event listener for it, you can be sure it is a button because it is performing an action.

One of the common issues was empty links and buttons.

Above I showed how to deal with images inside of buttons and links.

There may be other scenarios when you don't have a text as the content of the button or link, perhaps you have an SVG or some other type of media.

Now, first, let's keep it clear, this is extremely important, with extremely I really mean it, if users who can't see, using screen readers, only hears button being voiced, they don't even know what to do.

And when you do give the button or link an accessible name to be voiced by the screen readers, give it a proper one. Text such as Click Here, are confusing. What do we mean by clicking here? Where would we navigate? This is violating the first principle of POUR, that a site should be perceivable. The user shouldn't get confused!

        viewBox="0 0 461.516 461.516"
          d="M185.746 371.332a185.294 185.294 0 00113.866-39.11L422.39 455c9.172 8"

In the above example, we have a search button, where the SVG is the search icon, with no accessible name. Screen readers read this as button.

I want to show you 3 ways we could add an accessible name of search without the style being changed visually.


One option is to use the aria-label attribute, which directly gives the button an accessible name.

    <button aria-label="search">
        viewBox="0 0 461.516 461.516"
          d="M185.746 371.332a185.294 185.294 0 00113.866-39.11L422.39 455c9.172 8"

In the above example, we have set the name directly on the button element.


Another option would be to use the aria-labelledby attribute, which is used to not set the accessible name directly on the element, but to label the element by something else.

Let's have a look at what I mean.

    <button aria-labelledby="searchText">
      <svg viewBox="0 0 461.516 461.516" xmlns="">
          d="M185.746 371.332a185.294 185.294 0 00113.866-39.11L422.39 455c9.172 8"
    <span id="searchText" class="sr-only">Search</span>

In the example above, through the attributes id and aria-labelledby, we can set the accessible name on the button via the span's text. Here, we are also visually hiding the span by applying styles that visually hide the span from the sight of the users, but don't remove it from the DOM, hence it can still be voiced by screen readers since the Accessibility Tree is created from the DOM tree.

Hidden text

In our case, we can even just add a text that is visually hidden, which works as well, the accessible name will be the hidden text.

      <span class="sr-only">Search</span>
      <svg viewBox="0 0 461.516 461.516" xmlns="">
          d="M185.746 371.332a185.294 185.294 0 00113.866-39.11L422.39 455c9.172 8"

In the example above, the hidden text is within the button.

In all of the three options shown, the three examples, the button will be voiced as search, button by screen readers.

Heading Structure

The structure of the headings is extremely important.

According to this survey by WEBAIM, most users, around 70%, navigate the page through the headings.

When navigating via the keyboard and using a screen reader, the users are offered keyboard shortcuts which they use to navigate the page. In order to jump to the next heading in NVDA screen reader, you press H, some users will display a list of headings in order to decide where exactly they want to jump to, using NVDA you would do so by pressing Insert + F7.

If we don't properly structure our headings, it can become confusing for the users, where a section belongs, or where they are on the page, if they are at a heading which has a level of 2, and suddenly get to a heading of level 5 when they jump to the next heading on the page.

We should keep this structure: h1 > h2 > h3 > h4 > h5 > h6.

Sometimes people will use a certain level just because of the text size, don't! If it is not meaning to be a heading, it is not a heading! Use CSS if you somehow want to make the text size larger or smaller.

It is best to include only one h1 per page, which will be used as the page title.

For more information, I strongly advise going through the steps mentioned here: Steps to take. The first step contains some neat examples.

Semantic HTML

Considering headings were covered, I thought of mentioning semantic HTML and emphasize its importance.

If something has semantic meaning to it and is used properly, that is far better than using a div for an instance.

  • Built-in functionality (such as a button)

  • Gets properly voiced by screen readers (semantic meaning)

  • Allows users using screen readers to navigate via the keyboard

We mentioned how you would navigate to the next heading in NVDA screen reader by pressing H. Now, imagine if we were to use div instead of heading, that would first cause some serious trouble, because the user can't even begin navigating.

Further reading on Semantic HTML: Semantic Structure: Regions, Headings, and Lists , HTML: A good basis for accessibility and Semantics.


I hope you found this article helpful, I covered a bit more than just the common issues, but I really enjoyed writing this.

I think most issues aren't too hard to be addressed.

We all benefit from making our applications accessible, businesses, users, and even developers.