Skip to main content

Exercise objective

In this exercise, you’ll learn why native HTML buttons are superior to custom div-based buttons, and how to properly implement an accessible custom button if you absolutely need one.

The challenge

You’ll find a <div> element on the introduction page that needs to be converted into a fully functional, accessible button.

The problematic div

Here’s what you’re starting with:
<div>Click me</div>
This div has several accessibility problems:
  • No semantic meaning (screen readers don’t know it’s a button)
  • Not keyboard accessible (can’t be reached with Tab key)
  • No keyboard interaction (Enter/Space keys don’t work)
  • Not included in the focus order
  • No role announcement for assistive technologies

Your task

1

Locate the exercise file

Navigate to src/exercises/custom-button/custom-button.js in the course repository.
2

Add semantic role

First, give the div a proper ARIA role so screen readers know it’s a button:
const customButton = document.querySelector('.custom-button');
customButton.setAttribute('role', 'button');
3

Make it keyboard accessible

Add the element to the tab order:
customButton.setAttribute('tabindex', '0');
The tabindex="0" value places the element in the natural tab order of the page.
4

Add click handler

Implement the click event handler:
customButton.addEventListener('click', () => {
  console.log('Button clicked!');
  // Your button logic here
});
5

Add keyboard support

Buttons should respond to both Enter and Space keys:
customButton.addEventListener('keydown', (event) => {
  // Check for Enter or Space key
  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault(); // Prevent page scroll on Space
    customButton.click(); // Trigger the click event
  }
});
6

Test your implementation

Test the button with:
  • Mouse: Click the button
  • Keyboard: Tab to the button and press Enter or Space
  • Screen reader: Navigate to the button and verify it’s announced as a button
The button should have a visible focus indicator when you tab to it.

Complete solution

Here’s the complete JavaScript solution:
custom-button.js
const customButton = document.querySelector('.custom-button');

// Add proper role for screen readers
customButton.setAttribute('role', 'button');

// Make it keyboard accessible
customButton.setAttribute('tabindex', '0');

// Add click handler
customButton.addEventListener('click', () => {
  console.log('Button clicked!');
  alert('Custom button activated!');
});

// Add keyboard support for Enter and Space keys
customButton.addEventListener('keydown', (event) => {
  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault();
    customButton.click();
  }
});

The better solution

While the above solution makes the div accessible, using a native <button> element is always preferred.
Here’s why native buttons are better:
<!-- Instead of this custom div: -->
<div class="custom-button" role="button" tabindex="0">Click me</div>

<!-- Just use this: -->
<button class="custom-button">Click me</button>

What native buttons give you for free

Semantic role

Native buttons automatically have role="button" and are announced correctly by screen readers.

Keyboard support

Enter and Space keys work automatically without any JavaScript.

Focus management

Buttons are in the tab order by default with proper focus indicators.

Form integration

Native buttons can submit forms and have type, disabled, and name attributes.

Testing checklist

Verify your button implementation:
  • Button is announced as a “button” by the screen reader
  • Button text/label is read correctly
  • Screen reader indicates when the button is focused
  • Screen reader announces button state changes (if applicable)
  • Button can be reached using the Tab key
  • Button activates when pressing Enter key
  • Button activates when pressing Space key
  • Button has a visible focus indicator
  • Tab order makes logical sense
  • Button responds to mouse clicks
  • Hover states are visible (if styled)
  • Cursor changes to pointer on hover
  • Button looks interactive (not like plain text)
  • Focus indicator is visible and meets contrast requirements
  • Button is clearly distinguishable from surrounding content

Common mistakes to avoid

Don’t use <div> or <span> for buttons unless you have a very specific reason and implement full accessibility support.
Don’t forget the Space key - Many developers only implement Enter key support, but buttons should respond to both Enter and Space.
Don’t forget event.preventDefault() on Space key - Without it, the page will scroll when Space is pressed.

Learn more

Button patterns

Learn more about accessible button patterns and best practices

Next steps

Once you’ve completed this exercise, move on to the Broken landmarks exercise to learn about proper page structure.

Build docs developers (and LLMs) love