Custom controls

Designing custom controls

This section provides a high-level overview of what to consider when developing custom form controls. The ARIA specification and ARIA Authoring Practices Guide explain how to use ARIA (Accessible Rich Internet Applications) to create custom widgets. The Authoring practices describe the expected keyboard and focus behaviour of widgets, along with examples and code.

Please see module 12 for a discussion of custom JavaScript widgets.

Follow these best practices for custom controls:

  • Whenever possible, use native HTML form elements rather than custom controls.
  • Model the behaviour after native HTML form elements.
  • Add appropriate ARIA name, role, and values.
  • Communicate updates and state changes via ARIA live messages when they can’t be communicated through HTML or ARIA methods

Whenever possible, use a native HTML form element rather than a custom control

Standard HTML links and form controls need no custom JavaScript to work. They work out of the box, receiving focus and responding to keystrokes. Users understand these elements already, so there’s no need to provide instructions.

Model the behaviour after native HTML form elements

If you’re building a custom version of a native form element, model it after the original element, including duplicating the expected keyboard behaviour (e.g., controls receive focus, buttons are triggered with either Space or Enter key, checkboxes are checked or unchecked using the Spacebar, radio buttons are selected with the Arrow key).

If your form element needs to differ from native form elements, the native HTML form elements still model desirable keyboard behaviour. Ensure all buttons function with both the Enter key and Spacebar. Implement Arrow keys where users might expect to use them. See the keyboard interactions of the widget patterns and ARIA roles in the ARIA Authoring Practices Guide.

Think twice before building a custom control. It can be a lot of work, as detailed in the Mozilla Developer Network tutorial How to build custom form controls, which steps through building a custom <select> element.

Add appropriate ARIA name, role, and values

Screen reader users rely on a few key pieces of information to make sense of an interface. WCAG Success Criterion 4.1.2: Name, Role, Value spells them out:

“For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.”

Name
The name defines the element's label (e.g., "previous" or “next” or "register" or "Submit"). A custom control will often set the name via the aria-label or aria-labelledby attribute.
Role
The role defines what the widget is or does. The ARIA specification defines a list of roles to choose from, such as "checkbox" or "radiogroup" or "slider" or "tab".
Value
The value, sometimes several values, defines dynamic “states” and static “properties” of the control:
States
“States” are ARIA attributes with values the script updates in reaction to the user. Examples are aria-selected="true", aria-expanded="false" and a slider’s percentage value.
Properties
“Properties” are ARIA attributes with values that tend not to change. Examples are aria-labelledby or aria-describedby or aria-required.

Communicate updates and state changes via ARIA live messages when they can’t be communicated through HTML or ARIA methods

When the available HTML and ARIA states are insufficiently descriptive, add an ARIA live region to describe to screen reader users what’s going on. An ARIA live region can announce a custom value change such as “Table sorted by title, ascending” or “Results filtered by region” etc.

  Good example: A custom share button

In this example, a social media "share button" has two functions: show how many people have already activated the button ("shared") and allow users to press the button to activate the share function.

When the button is activated:

  • The count increases by one.
  • The accessible name changes from “3 shares” to “4 shared (check)”.
  • The button takes the “disabled” attribute, preventing it from regaining focus.

Also, the action attribute of the <form> element references a server-side script that carries out the same functionality for cases when JavaScript is not supported.

HTML

<form action="path/to/submit">
   <button type="submit" id="share-btn" class="btn-primary">
      <span class="count">3</span> 
      <span class="text">Shares</span>
   </button> 
</form>

JavaScript

document.getElementById('share-btn').addEventListener('click', function(event){ 
   event.preventDefault();    
   event.stopImmediatePropagation();
   var count = this.querySelector('.count');
   var text = this.querySelector('.text');
   count.textContent = parseInt(count.textContent) + 1;
   text.textContent = "Shared ✓"; 
   this.setAttribute("disabled", "true"); 
});

Adapted from “Custom Form Inputs” in the module “Form Labels, Instructions, and Validation.” deque University. 2021 Deque Systems Inc.

Good example code from the Web Accessibility Initiative (WAI) document: Custom Controls (WAI), in Forms Concepts (WAI). Eric Eggert and Shadi Abou-Zahra, eds. Copyright © 2019 W3C® (MIT, ERCIM, Keio, Beihang). Status: Draft Updated 27 July 2019.

Related WCAG resources

Related WCAG resources

Success criteria

Techniques

Failures

Back to top