Making UI Elements Accessible

Contents

Introduction

If you stick to the basic HTML controls, it’s fairly easy maintain accessibility with UI controls. Where things get tricky, however, is when you use elements in unexpected ways. Then, it’s possible to fail a good number of WCAG requirements all at once. This article demonstrates how these issues can come up– and how to work around them in your design. And, in the process, it will also demonstrate how to meet often-overlooked accessibility requirements in the process.

A Working Example

In the iframe below is a very basic (and inaccessible) example of three buttons– the first and third are created using a <button> element and the second one is created using a <div>.

Clicking on any of these three buttons triggers an alert in the browser just notifyng the user that a “button” was pressed. The first and third button can receive focus (they are <button>‘s after all) but not the second <div> “button”– and when they receive focus, they turn a lovely shade of dark blue. The code for this example is pretty sparse and looks like the following.

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
    .click {
        background-color: black;
        color: white;
        width: 120px;
        text-align: center;
        font-family: sans-serif;
    }
    .click:focus {
        background-color: darkblue      ;
        outline: solid;
        text-align: center;
        font-family: sans-serif;
    }   
</style>

<script>
    function hiya(){
        alert("A Control was Pressed!!!");
    }
    </script>
</head>
<body>
<button class="click" onclick="hiya()">this is a button</button>
<p>      
<div class="click" onclick="hiya()">this is a div</div>
<p></p>                             
<button class="click" onclick="hiya()">this is a button</button>

</body>
</html>

You can also examine this file more closely as a separate HTML file.

Adding Keyboard Functionality

There are a number of steps needed to make this <div> element behave like a button. Specifically, we have to:

  • Set TabIndex to Zero. Just by using the tabindex attribute, we can place an item in the tab index and make and element “reachable” by the keyboard. You really should stick with keeping the tabindex value either “0” (if you want to add an element to be keyboard reachable in the natural order of the DOM) or “-1” (if you want to remove an element).
  • Set “Role” to “Button” and Create an Additional Keyboard Event Handler. Just because we made our <div> reachable by the keyboard doesn’t mean that its usable. To satisfy 4.1.2.1 Non-Standard HTML Controls, we need to give it a “role” and the most logical role would be “button”. Now, you would think that once we set role="button", we could just treat it like a button and so just an onclick event handler would be enough– but no, that would be too easy. So we also have to create an extra event handler to handle keyboard presses.

We then need a function to handle the keyboard events. In this case, we call it hiya2() and all it does it wait for the user to press the enter or spacebar keys– anything else is ignored. And, if the user hits the spacebar or enter keys, then the code merely calls the same function as the clicking on the same control with the mouse. So here is the modified example that leverages these changes

and here is the modified code

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
    .click {
        background-color: black;
        color: white;
        width: 120px;
        text-align: center;
        font-family: sans-serif;
    }
    .click:focus {
        background-color: darkblue      ;
        outline: solid;
        text-align: center;
        font-family: sans-serif;
    }   
</style>

<script>
    function hiya(){
        alert("hiya");
    }
    function hiya2(event){
        // Keypresses other then Enter and Space should not trigger a command
        if (event instanceof KeyboardEvent && event.key !== 'Enter' && event.key !== ' ') {
        return;
        }
        hiya();
    }
</script>
    

</head>
<body>
<button class="click" onclick="hiya()">this is a button</button>
<p>
<div class="click" tabindex="0" role="button" onclick="hiya()" onkeydown="hiya2(event)">this is a div</div>
<p></p>                             
<button class="click" onclick="hiya()">this is a button</button>

</body>
</html>            

If you’d like to explore this code or take it with you, here is a link to modified file.

Extending this Example

While the example used in this KB article is quite simple, the principles demonstrated here extend easily to far more complex controls. To make UI elements accessible, your process should be:

  1. Elements “Reachable” by the Keyboard. If you can’t reach an element by using the tab key, use tabindex to make it reachable. 99.999% of the time, this means setting tabindex="0".
  2. Make Elements “Operable” by the Keyboard. This usually involves adding event listeners that respond to keyboard actions when an element has focus.
  3. Identify Name, Role, and Value. The previous requirements focused on keyboard operability– but this doesn’t ensure that the control is readily known to assistive technology or screen readers. This is where ARIA is invaluable and why we needed to set role="button".

We also kept this example simple on the coding side. As a best practice, some authors specify that space bar’s default action should be disabled on the keydown event to prevent the key from scrolling contents. Other authors create a second event handler to recognize the enter key only through onkeyup and not onkeydown to make the event cancellable more readily. We avoided including this level of detail in our example; accessibility is complicated enough and this code already works just fine.