ismaelramos.dev
  • Search in articles

    Top 20 Must-Know Tips for Web Accessibility

    Accessibility 19 min read
    Accessibility Beginners
    Top 20 Must-Know Tips for Web Accessibility

    Table of content


    This is the third part of the series (after why accessibility matters and designing accessibly). It covers essential principles (not all of them, that’s almost impossible), practical advice, and examples for building accessible websites.

    I’ll share the guidelines our team followed to make our site accessible and reach a double-A conformance level.

    We’ll cover writing clear image descriptions, navigating with just the keyboard, and a lot more, so everyone can browse your site like a pro. :)

    a sign showing the text ‘step free route’ and an arrow to the right


    1 - Learn how to use a screen reader

    Before getting into the code, you need to understand how to use a screen reader. It’s a fundamental skill that helps you grasp the challenges users face when navigating your site.

    Most of our team uses VoiceOver since we’re on macOS. On Windows there are alternatives like Speechify, JAWS, and NVDA.

    Taking the time to get familiar with one will give you valuable insight into the user experience.

    showing how a screen reader looks like in dev.to


    Most commonly used keys

    • Tab and Shift+Tab: move forward and backward through interactive elements on a page, such as links, form fields, and buttons.

    • Arrow keys: navigate within a block of text or move between components like headings, paragraphs, and lists. Users can go up, down, left, or right to explore content in detail.

    • Nodes: navigating by nodes lets you explore and interact with every element, like moving from one piece of the puzzle to the next. In VoiceOver you do this with Control + Option + Arrow key.

    • Enter or Spacebar: activate buttons, links, or form controls. When the user lands on an interactive element, they press Enter or Spacebar to trigger the action or follow the link.

    • Headings: many screen readers offer shortcuts to jump between headings. For example, pressing the “H” key moves to the next heading, which makes it easier to skim a page’s structure.

    • Search: screen readers usually include a search function so users can find specific words or phrases on a page without navigating through everything. There’s also a very useful shortcut in VoiceOver to list all headings, links, and form controls: Control + Shift + U.

    icons of all screen readers mentioned


    2 - Gain good knowledge of ARIA

    For this second tip, it’s worth giving some context about what ARIA actually means. ARIA stands for Accessible Rich Internet Applications. It’s a set of guidelines and tools designed to make websites more usable for people with disabilities.

    ARIA adds extra information to elements like buttons or links so screen readers and other assistive technologies can understand them better. It’s a way to give a hand to people who can’t see the screen or use a mouse, so they can navigate and interact with sites smoothly.


    Most common ARIA attributes

    These are the ARIA attributes we mostly used while taking the site to double-A conformance. There are more out there, but you should at least be familiar with these:

    • aria-hidden: setting aria-hidden="true" tells assistive technologies to skip the element in the accessibility tree. Use it for non-interactive elements.
    <button class="icon-button">
      <span class="visually-hidden" aria-hidden="true">Close</span>
      <i class="fas fa-times"></i>
    </button>
    
    • aria-label: provides a text alternative for an element when no visible label is present.
    <button class="icon-button" aria-label="Search">
      <i class="fas fa-search"></i>
    </button>
    
    • aria-labelledby: associates an element with another element that serves as its label.
    <div id="alert-message" role="alert" aria-labelledby="alert-heading alert-description">
      <h3 id="alert-heading">Important Info!</h3>
      <p id="alert-description">Please be aware of the upcoming changes.</p>
    </div>
    

    The attribute value "alert-heading alert-description" matches the IDs of the heading (<h3>) and paragraph (<p>) that label and describe the alert.

    With aria-labelledby, you link the <div> to the heading and description. The screen reader reads the full text inside those elements.

    • aria-describedby: associates an element with another element that describes its purpose.
    <label for="username">Username:</label>
    <input type="text" id="username" aria-describedby="username-description">
    <p id="username-description">Please enter a unique username consisting of letters and numbers.</p>
    

    A common case is providing extra information for inputs like usernames, emails, and passwords, where the user has to follow a specific format.

    • aria-expanded: indicates whether a collapsible element is currently expanded or collapsed.
    <button id="toggle-button" aria-expanded="false" onclick="toggleContent()">
      Toggle Content
    </button>
    

    By updating aria-expanded based on the state of the content, the screen reader can announce the current state to the user.

    • aria-disabled: indicates that an element is disabled and cannot be interacted with.
    <button aria-disabled="true">Submit</button>
    
    • aria-live: indicates that content inside an element should be announced to the screen reader as it changes. We often use this to notify the user of form errors.
    <div aria-live="polite" id="status-message">
      New message received: You have 1 unread notification.
    </div>
    

    a form with an alert showing errors

    • role: defines the purpose or type of an element on the page.

    The role attribute can be applied to many HTML elements, like <div>, <span>, <button>, <nav>, and more. Common values include “button”, “link”, “navigation”, “heading”, “list”, “form”, and “banner”. Each one conveys a specific meaning and behavior to assistive technologies.

    <div role="alert" aria-live="assertive">
      <p>This is an important alert message!</p>
    </div>
    

    If you need to build a component with a specific role, check the W3C’s ARIA patterns reference.


    3 - Use semantic HTML

    Semantic HTML is just using tags that give meaning and structure to your content. Instead of slapping any old tag on things, use tags like <header>, <nav>, <article>, and <footer> to convey the structure and purpose of each part of the page.

    For example, instead of a <div> for your site’s navigation menu, use the <nav> tag. That tells humans, search engines, and screen readers that the content inside it is the site’s navigation.

    <nav>
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/services">Services</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>
    

    Examples of semantic usage of HTML tags

    Here are key cases where you should reach for the right tag.

    • <header>: represents the introductory or top section of a page.
    <header>
      <h1>Welcome to My Website</h1>
      <nav>
        <!-- Navigation menu -->
      </nav>
    </header>
    
    • <main>: represents the main content of a document.
    <main>
      <h2>About Me</h2>
      <p>I'm passionate about web development, ...</p>
    </main>
    
    • <article>: represents a self-contained composition, such as a blog post, news article, or forum post.
    <article>
      <h3>Post title</h3>
      <p>Recently, I went on an amazing trip...</p>
    </article>
    
    • <section>: represents a standalone section within a document.
    <section>
      <h2>Services</h2>
      <ul>
        <li>Web Design</li>
        <li>Front-end Development</li>
      </ul>
    </section>
    
    • <footer>: represents the bottom section of a document or section.
    <footer>
      <p>&copy; 2023 MyWebsite. All rights reserved.</p>
      <nav>
        <!-- Footer navigation goes here -->
      </nav>
    </footer>
    
    • <fieldset>: groups related form fields together, like contact information.
    <form>
      <fieldset>
        <legend>Contact Information</legend>
    
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
    
        <label for="email">Email:</label>
        <input type="email" id="email" name="email">
    
        <label for="message">Message:</label>
        <textarea id="message" name="message"></textarea>
      </fieldset>
    
      <button type="submit">Submit</button>
    </form>
    

    The main advantage of semantic HTML

    Semantic HTML elements give you a logical, well-organized tab order. Keyboard-only users rely heavily on the Tab key to move through interactive elements on a page.

    By using the right semantic elements, like <button>, <a>, and properly labeled form fields, you ensure keyboard users can navigate and interact with your site efficiently.

    Without Semantic HTML

    <section>
      <h2>Welcome!</h2>
      <p>Some paragraph</p>
    
      <div class="button-container">
        <div class="button">Register</div>
      </div>
    
      <p>Or, if you have any questions, feel free to <span class="link"><a href="/contact">contact us</a></span>.</p>
    </section>
    

    With Semantic HTML

    <section>
      <h2>Welcome!</h2>
      <p>Some paragraph</p>
    
      <button>Register</button>
    
      <p>Or, if you have any questions, feel free to <a href="/contact">contact us</a>.</p>
    </section>
    

    This is crucial for people with motor disabilities who can’t use a mouse.

    So go ahead, embrace semantic HTML and enjoy the ride!

    two forms, on the left using only divs, and on the right using proper tags


    Basic concepts

    4 - Use tabindex, but with caution

    tabindex is an attribute you can add to HTML elements to control their keyboard navigation order. It decides which elements receive focus as users move through the page with the Tab key.

    By setting tabindex values you can customize the order and make your site more accessible, especially for users who rely on the keyboard.

    four buttons that show how to tab order works because the second one has focus


    Best practices for tabindex

    Only change tabindex values when you have to. Most interactive elements receive focus automatically in the default Tab order, so only step in when it improves the user experience.

    • Possible values: the two recommended values are “0” and “-1”.

    If you use tabindex="0" to make an element focusable, the keyboard interaction has to be correct and intuitive. For example, anything presented as a button must respond to both the Enter and Spacebar keys.

    A tabindex of 0 ensures these elements can receive keyboard focus and be reached with the Tab key. It opens the door for keyboard users to interact with elements that aren’t normally clickable.

    tabindex="-1" takes the element out of the regular navigation flow.

    That said, don’t assign “-1” to elements that need to be reachable by keyboard, such as links or buttons that sighted users can easily click with a mouse.

    In general, avoid tabindex="-1" unless you have a specific reason for it.

    • Keep a logical flow: arrange tabindex values to match the visual and reading flow of your content. Users should be able to navigate naturally, without unexpected jumps or skipped elements.

    • Skip non-interactive elements: don’t assign tabindex to non-interactive elements like paragraphs or images. It keeps the focus on the elements that actually need action or input.


    5 - Create proper outlines

    Outlines are the visual ring around interactive elements like buttons, links, and form fields when you interact with them.

    .focus-outline {
       outline: 2px solid blue;
       outline-offset: 2px;
    }
    

    They give a clear visual cue, usually a border or highlight, showing which element is currently focused. Whether you’re using a keyboard or a mouse, outlines make it easy to identify and interact with the right element on the page.

    representation of an outline


    Best practices for outlines

    1. Visibility: outlines must be visible and clearly distinguishable from the surrounding content. Pick colors and styles with enough contrast, and avoid designs that confuse users or distract from the main content.

    2. Don’t remove outlines: never remove or disable outlines unless you’ve replaced them with an accessible alternative. Removing them disorients users, especially those who rely on keyboard navigation. Some developers still strip outlines out, but don’t do it.

    3. Design consistency: keep a consistent outline style across the site. It helps users get familiar with the visual cues and makes browsing smoother.

    If you’re a designer, check the second part of this series.


    6 - Allow keyboard navigation throughout your website

    Keyboard navigation is one of the most important parts of web accessibility.

    a person using a braille keyboard

    First, it’s closely tied to semantic HTML and native elements. When you combine the two, something great happens: users can navigate most of the site with just the keyboard, without any special tweaks or tricks.

    So one of the principles we follow when building new components and features is to always use native elements when possible.

    Take the dialog element as an example. Instead of building your own modal, use the native component. It already handles escape, focus trapping, the close button, and so on.

    <dialog open>
      <p>This is a simple dialog!</p>
      <form method="dialog">
        <button>nice! ok!</button>
      </form>
    </dialog>
    

    One last tip: take the #nomouse challenge. Try navigating your page and using all of its features with just the Tab key, without touching the mouse. This simple test is usually a good indicator of accessibility. For more info, see nomouse.org.


    7 - Identify the language

    You need to provide context about the language of the page, plus any paragraphs or components that are in a different language.

    The lang attribute

    Setting the lang attribute on the HTML tag declares the language of the page. This helps screen readers and other assistive technologies identify and present the content correctly.

    <!DOCTYPE html>
    <html lang="en">
      <head>
      </head>
      <body>
      </body>
    </html>
    

    Keep in mind that if a paragraph is in a different language, you have to specify its language like this:

    <body>
      <!-- English (US) content -->
      <p lang="en-US">Explore and enjoy our diverse content.</p>
    
      <!-- Hindi content -->
      <p lang="hi">हमारी विविधतापूर्ण सामग्री का अन्वेषण और आनंद लें।</p>
    </body>
    

    There are a few exceptions, like quotations, proper nouns, technical terms, foreign words like “Sushi”, or the language selector itself:

    language selector showing different languages in amazon website


    8 - Ensure ARIA attributes are in the correct language

    If your page is primarily in English, make sure your ARIA attributes are in English too.

    If the user switches to another language, the ARIA attributes should switch with it. Avoid mixing languages as much as possible to keep the experience consistent and clear.


    9 - Texts must be resizable

    To make your site more user-friendly and accessible, the text has to be resizable.

    Ideally, users should be able to increase the font size by up to 200% without any content getting cut off or losing functionality.

    In general, use em units for font sizes.

    div.a {
      font-size: 14px; // :(
    }
    
    div.b {
      font-size: 1rem; // :)
    }
    
    div.c {
      font-size: 1.2em; // :)
    }
    

    Using scalable units like em and rem lets users control the scale of the site, which is what gets us the accessibility we’re after.

    If you’re considering rem, keep in mind that getting every text element to scale up and down accurately can be tricky.

    Either way, avoid px for font sizes. They simply don’t scale.


    10 - Use headings in the proper order

    When structuring your content, use headings in a logical hierarchy.

    Start with the main heading (usually <h1>) for the page title or main section, then <h2> for major subsections, and so on.

    This hierarchy helps users understand how the content is organized and lets screen readers navigate the page more effectively.

    <h1>Best practices for Web Accessibility!</h1>
    <h2>Basic concepts</h2>
    <h3>Headings</h3>
    <h4>Best practices for headings</h4>
    ....
    <h2>Advanced concepts</h2>
    <h3>Creating an accessible component library</h3>
    

    Stick to a consistent heading order without skipping levels. For example, don’t jump straight from <h2> to <h4>. Skipping levels confuses screen readers and makes it harder for users to follow the structure.

    One final tip for this section: place important interactive elements higher up on the page.

    different headings all with a proper order

    Accessibility in base components

    11 - Make images accessible

    Let’s go through some easy-to-implement techniques to make your images more accessible.

    • Provide descriptive alt text: use concise, descriptive language to convey the content and purpose of the image.
    <img src=".jpg" alt="A group of friends enjoying a picnic in the park">
    
    • Decorative alt: sometimes you’ll include decorative images that don’t add meaningful information. For those, use empty alt text or the aria-hidden attribute so screen readers skip them.
    <img src="dec.jpg" alt="" aria-hidden="true">
    
    • Provide captions and image descriptions: for complex images, charts, or infographics, add extra context with captions or detailed descriptions.
    <figure>
      <img src="info.jpg" alt="Data visualization of population growth">
      <figcaption>Data visualization of population growth in the last decade</figcaption>
    </figure>
    
    • File formats: use widely supported formats like JPEG, PNG, or GIF for regular images. For complex graphics or images with transparency, consider SVG. These formats ensure compatibility with most browsers and assistive technologies.

    • Implement responsive images: use the srcset attribute to provide multiple image sources of varying sizes.

    <img srcset="small.jpg 320w,
                  medium.jpg 768w,
                  large.jpg 1200w"
         sizes="(max-width: 768px) 100vw,
                50vw"
         src=".jpg" alt="A responsive image">
    

    summary from above inside a decision graph


    12 - Create icons for everyone

    Instead of using icons as purely decorative elements, consider wrapping them in semantic HTML like <span> or <i> and providing a proper text alternative.

    <span class="icon" aria-hidden="true">📞</span>
    <span class="sr-only">Phone</span>
    

    Icon fonts and SVGs are highly recommended for accessibility. They scale without losing quality and work well across devices and browsers.

    For complex SVGs, use title and desc elements along with aria-labelledby, like this:

    <svg
       role="img"
       aria-labelledby="svg-title svg-description"
       xmlns="http://www.w3.org/2000/svg"
       xmlns:xlink="http://www.w3.org/1999/xlink"
       x="0px"
       y="0px"
       viewBox="0 0 48 48"
       xml:space="preserve">
          <title id="svg-title">A short title that summarizes the purpose of the svg!</title>
          <desc id="svg-description">A description with more details.</desc>
          <path d="..."/>
    </svg>
    

    13 - Don’t forget states in buttons

    Buttons are fundamental web design elements. They provide interactivity and act as key call-to-action components.

    Beyond using the semantic <button> tag with descriptive text like “Send Message”, you should also provide visual feedback through :focus and :hover states.

    <button class="primary-button">Save Changes</button>
    
    .primary-button:hover {
      background-color: #42b983;
      color: white;
    }
    
    .primary-button:focus {
      background-color: #342455;
      color: black;
    }
    

    There’s also the disabled state, which requires aria-disabled if the button is still focusable:

    <button aria-disabled="true">
      Continue
    </button>
    

    If the button is fully disabled, then:

    <button disabled>
      Continue
    </button>
    

    For toggle buttons, use aria-pressed="true/false". For menus, use aria-expanded="true/false".

    3 examples of buttons, two of the with icons inside


    Sometimes it’s hard to decide between a button and a link. Our rule is simple: if the user is taken to another page or location, it should be a link.

    Even if a link looks like a big shiny button, it’s crucial to use the <a> element when coding it.

    This is a design tip we covered in the second part of the series too: avoid generic link labels like “click here” or “read more”.

    Note: links shouldn’t be the raw URL unless it’s meant for printing (like in a Word document). And if a link opens in a new window, make sure to tell the user.

    Example: Article about Web Accessibility (opens in new tab)

    an example of the w3c link in google


    15 - Ensure forms are accessible

    This is one of the trickiest parts of web accessibility.

    Forms can include a wide range of components: comboboxes, inputs of various types (numbers, dates, etc.), text areas, and many more.

    Making each form component accessible is crucial, but covering every one in detail would be insane. So here are some more general, but equally important, tips:

    • Semantic HTML (yes, again): when structuring forms, use semantic HTML elements like <form>, <fieldset>, and <legend> to give them a clear, organized structure.
    <form action="#">
      <fieldset>
        <legend>Do you agree?</legend>
        <input type="checkbox" id="chbx" name="agree" value="Yes!" />
        <label for="chbx">I agree</label>
      </fieldset>
    </form>
    

    This lets the screen reader provide context about the field the user has to fill in.

    • Use labels: make sure every form field has a descriptive label. Use the <label> element and tie it to the field with the for attribute, or nest the input inside the label.
    <label for="name">Name:</label>
    <input type="text" id="name" name="name">
    
    • Clear instructions: include clear, concise instructions to guide users through the form. Place them near the related fields, using extra text or elements like <p> or <span>.
    <label for="email">Email:</label>
    <input type="email" id="email" name="email">
    <p class="instructions">Please enter a valid email address.</p>
    
    • Provide error validation and feedback: tie error messages to their fields with ARIA attributes, and use visually distinct styles or alert boxes to notify users of errors, while keeping enough color contrast for readability.

    Here’s an example, but since this is a complex topic, I recommend reading Sandrina’s post on Accessible Form Validation. Simply amazing.

    <input
      id="address"
      type="text"
      required="required"
      aria-invalid="true"
      aria-describedby="addressError addressHint"
    />
    <span>
      <p id="addressError">Address cannot be empty.</p>
      <p id="addressHint">Remember to include the door and apartment.</p>
    </span>
    

    16 - Include captions for videos

    This isn’t as common as forms, but it’s generally easier to get right.

    First, provide captions:

    <video>
      <source src="video.mp4" type="video/mp4">
      <track src="captions.vtt" kind="captions" label="English" srclang="en" default>
    </video>
    

    Second, always make controls available for keyboard users:

    <video controls tabindex="0">
      <source src="video.mp4" type="video/mp4">
    </video>
    

    A few more tips:

    • Avoid flashing or blinking content that can trigger seizures.

    • Avoid auto-playing media or animations that can distract users.

    • Color plays a big role in videos, from subtitles to graphical elements. Make sure there’s enough contrast between text and background.

    • Pick an accessible video player.


    17 - Provide transcripts or audio descriptions for audio content

    In this case, you can provide a transcript like this:

    <audio controls>
      <source src="audio.mp3" type="audio/mpeg">
    </audio>
    <p><a href="audio-transcript.txt">Read the audio transcript</a></p>
    

    Check that the audio content, transcripts, captions, and audio descriptions are interpreted correctly.

    Note: audio descriptions should give a clear, concise narration of the visuals and actions in the video.

    a deaf person


    18 - Use titles and make iframes responsive

    Iframes are versatile tools for embedding external content into your site. But there are a few things you need to do to make them accessible.

    Use descriptive titles

    When you use an iframe, give it a descriptive title that conveys the purpose and content of the embedded page.

    <iframe src="external-page.html" title="Weather Forecast"></iframe>
    

    Make them responsive

    Make sure iframes are responsive and adapt well to different screen sizes and resolutions.

    <div class="wrapper">
      <iframe src='some url' frameborder="0" allowfullscreen></iframe>
    </div>
    
    .wrapper {
      position: relative;
      padding-bottom: 56.25%; /* 16:9 */
      padding-top: 25px;
      height: 0;
    }
    .wrapper iframe {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
    

    Provide a fallback option

    In some cases, iframes aren’t supported or accessible for certain users or devices. To make sure everyone can still access the essential information, provide a fallback.

    <iframe src="external-page.html">
      <img src="fallbac.jpg" alt="Fallback Image: Content Not Available">
      <p>If you are unable to view the content, please try accessing it directly using the <a href="external-page.html">fallback link</a>.</p>
    </iframe>
    

    Other improvements

    19 - Make downloads descriptive

    Quick note on this one, because we sometimes run into tricky situations that confuse users.

    The tip is simple: use descriptive link text, and include the file extension and file size (when possible).

    <a href="document.pdf">Download the User Manual (PDF, 2.5MB)</a>
    

    Bypass blocks are essential for web accessibility. They let users skip repetitive or non-essential content and jump straight to the main content of a page.

    An example:

    <a href="#main-content" class="skip-link">Skip to Main Content</a>
    
    .skip-link {
      position: fixed;
      top: 10px;
      left: 10px;
      z-index: 9999;
      padding: 10px;
      background-color: #42b983;
      color: white;
      text-decoration: none;
    }
    
    .skip-link:not(:focus):not(:active) {
      position: absolute;
      top: -9999px;
      left: -9999px;
    }
    
    .skip-link:focus {
      top: 5px;
      left: 5px;
      background-color: #0066cc;
    }
    

    The skip link is positioned fixed and visible by default. But the :not(:focus):not(:active) selector hides it (position: absolute; top: -9999px; left: -9999px;) for mouse users.

    It only becomes visible when it receives focus or is active, using the styles in the :focus selector.

    This way, the skip link is hidden for mouse users but stays accessible and functional for keyboard users.

    You can try it out on dev.to :)

    skip to content button inside dev.to


    Last words

    So there you have it. We’ve covered what I think are the top 20 must-know tips for web accessibility. Special thanks to @pablo_medina and @juanpabloamador for their feedback!

    The next article in the series will be about “Common accessibility challenges our team has faced”. Stay tuned.

    And remember, making your site accessible isn’t just good practice, it’s the right thing to do.

    two hands reaching out to help each other


    This post is long, but I hope you’ve picked up something new along the way. If you think it might help others, please hit the like button so more people can find it. ❤️

    If you have any thoughts or questions, feel free to leave a comment!

    Share X / Twitter LinkedIn WhatsApp

    Join the conversation

    No account needed — leave your name and a thought. Comments are reviewed before going live.