# Disabled buttons and accessibility
URL: https://pandus.online/en/disabled-buttons-accessibility
Updated: 2026-10-14

Disabled buttons and WCAG: the contrast exemption for inactive controls, disabled vs aria-disabled, when to keep a button enabled and how to explain why it is unavailable.

The greyed-out "Add to cart" until a size is chosen, the "Place order" button that stays disabled until every field is filled — disabled buttons are everywhere on online shops. WCAG has a specific exemption for them, but that does not make them a good idea in every case. This page covers the rules, the two ways to disable a button and better alternatives. It is part of the [accessible UI patterns](https://pandus.online/en/accessible-ui-patterns) series.

## What WCAG says

- **Contrast exemption.** [1.4.3 Contrast (Minimum)](https://pandus.online/en/wcag/1-4-3-contrast-minimum) exempts "text or images of text that are part of an inactive user interface component", and [1.4.11 Non-text Contrast](https://pandus.online/en/wcag/1-4-11-non-text-contrast) exempts inactive components too. W3C defines an inactive component as one that "is visible but not currently operable" and gives a disabled submit button as the example. The Understanding document explains that the exemption exists because no single good way to show disabled controls has been found.
- **Name, role, state.** [4.1.2 Name, Role, Value](https://pandus.online/en/wcag/4-1-2-name-role-value): the disabled state must be exposed in code, not only by grey colour.
- **Instructions.** [3.3.2 Labels or Instructions](https://pandus.online/en/wcag/3-3-2-labels-or-instructions): if the user must do something before a button works, say so.
- **Errors.** [3.3.1 Error Identification](https://pandus.online/en/wcag/3-3-1-error-identification): a disabled submit button is not an error message. The user still needs to learn in text what is wrong.

So low contrast on a disabled button does not fail WCAG — but a disabled button nobody can understand may still fail the criteria above and certainly costs sales.

## disabled vs aria-disabled

| | `disabled` attribute | `aria-disabled="true"` |
| --- | --- | --- |
| Works on | Native form controls: `button`, `input`, `select`, `textarea`, `fieldset` | Any element with an interactive role |
| Can be activated | No, the browser blocks it | Yes — your script must ignore the action |
| In the tab sequence | No | Yes |
| Screen reader announces "unavailable"/"dimmed" | Yes | Yes |

The APG keyboard guide says "browsers remove HTML input elements with the `disabled` attribute from the tab sequence". Where users need to discover a disabled element — the APG mentions menu items, tabs, listbox options and tree items — it uses `aria-disabled="true"` so the element stays focusable. The APG button pattern also says that when a button's action is unavailable, "the button has aria-disabled set to true".

The practical rule: use `disabled` when it does not matter that the button is skipped; use `aria-disabled` when users need to find the button and learn why it does not work.

## Code example

```html
<!-- Discoverable: stays in the tab order, the reason is announced -->
<p id="size-hint">Choose a size to add this item to your cart.</p>
<button type="button" id="add" aria-disabled="true" aria-describedby="size-hint">
  Add to cart
</button>

<script>
  const add = document.getElementById('add');
  add.addEventListener('click', (event) => {
    if (add.getAttribute('aria-disabled') === 'true') {
      event.preventDefault();          // block the action yourself
      document.getElementById('size').focus();
      return;
    }
    addToCart();
  });
  // When a size is selected: add.setAttribute('aria-disabled', 'false');
</script>

<!-- Fine to skip: a native disabled control the user does not need to reach -->
<button type="button" disabled>Previous page</button>
```

Style `[aria-disabled="true"]` so it looks unavailable — the browser will not do that for you.

## Better alternatives

1. **Keep "Place order" enabled.** When it is pressed with missing data, show error messages in text, move focus to the first error or an error summary. See [form validation and errors](https://pandus.online/en/accessible-form-validation-errors).
2. **Say what is needed** next to a disabled button, in visible text, not in a hover tooltip.
3. **Do not hide** an unavailable option without explanation — a sold-out size can stay in the list, marked "sold out".

## Keyboard interaction

| Key | `disabled` button | `aria-disabled` button |
| --- | --- | --- |
| Tab | Skips the button | Moves focus to it; screen reader says it is unavailable and reads the description |
| Enter / Space | Nothing | Nothing happens — or the script moves focus to what is missing |

## Common mistakes on online shops

- **"Add to cart" disabled until a variant is chosen, with no visible hint.** Keyboard users never reach the button and do not know why.
- **The checkout "Pay" button disabled until all fields pass validation**, while the fields show no error messages.
- **A button that looks disabled but is not** (grey style only), or one that is disabled in code but looks active.
- **`aria-disabled="true"` without a script guard**, so the button still submits.
- **Disabled state shown only by lower opacity** while the reason appears in a `title` tooltip.
- **Unreadably light disabled text for important information** such as "Out of stock". The text may be exempt from contrast, but the information should also be available in readable text.

## What automated checks catch vs manual testing

| Automated (axe-core rule) | What it finds |
| --- | --- |
| `color-contrast` | Low-contrast text; disabled controls are skipped, as WCAG exempts them |
| `button-name` | Buttons without a name, disabled or not |
| `aria-valid-attr-value` | `aria-disabled` with an invalid value, `aria-describedby` pointing to a missing ID |
| `aria-allowed-attr` | ARIA attributes not allowed on the element's role |

All are cards in our [rule library](https://pandus.online/en/rules). **Manual testing:** try to complete the form or the product choice with the keyboard only, check that every disabled control has a visible reason, and check with a screen reader that the state and the reason are announced. The [contrast checker](https://pandus.online/en/color-contrast-checker) helps choose a disabled style that stays readable.

Run the free [one-page check](https://pandus.online/en/free-accessibility-check). It is **an automated check, not a full audit**.

## Frequently asked questions

### Do disabled buttons need to meet WCAG contrast requirements?

No. Both 1.4.3 Contrast (Minimum) and 1.4.11 Non-text Contrast exempt inactive user interface components, which W3C defines as visible but not currently operable. A disabled submit button is the example W3C gives.

### What is the difference between disabled and aria-disabled?

The HTML disabled attribute makes a native control inoperable and removes it from the tab sequence. aria-disabled="true" only tells assistive technology the control is unavailable; it stays focusable and you must block the action in your script.

### Is it bad to disable the submit button until the form is valid?

It often causes problems: users cannot find out what is missing, and keyboard users cannot even reach the button. Keeping it enabled and showing clear error messages on submit usually works better.

### How do I explain why a button is disabled?

Put the reason in visible text near the button, for example "Choose a size to add to cart", and connect it with aria-describedby. Do not rely on a tooltip on hover.

### Can an automated check test disabled buttons?

Contrast checks skip disabled controls, as WCAG does. Automated rules check that aria-disabled has a valid value and that buttons have a name. Whether users understand why a button is unavailable needs a manual test.

## Sources

- W3C, Understanding 1.4.3 Contrast (Minimum): https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html
- W3C, Understanding 1.4.11 Non-text Contrast: https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html
- W3C WAI, APG Developing a Keyboard Interface (focusability of disabled controls): https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/
- W3C WAI, APG Button pattern: https://www.w3.org/WAI/ARIA/apg/patterns/button/

Q: Do disabled buttons need to meet WCAG contrast requirements?
A: No. Both 1.4.3 Contrast (Minimum) and 1.4.11 Non-text Contrast exempt inactive user interface components, which W3C defines as visible but not currently operable. A disabled submit button is the example W3C gives.

Q: What is the difference between disabled and aria-disabled?
A: The HTML disabled attribute makes a native control inoperable and removes it from the tab sequence. aria-disabled="true" only tells assistive technology the control is unavailable; it stays focusable and you must block the action in your script.

Q: Is it bad to disable the submit button until the form is valid?
A: It often causes problems: users cannot find out what is missing, and keyboard users cannot even reach the button. Keeping it enabled and showing clear error messages on submit usually works better.

Q: How do I explain why a button is disabled?
A: Put the reason in visible text near the button, for example "Choose a size to add to cart", and connect it with aria-describedby. Do not rely on a tooltip on hover.

Q: Can an automated check test disabled buttons?
A: Contrast checks skip disabled controls, as WCAG does. Automated rules check that aria-disabled has a valid value and that buttons have a name. Whether users understand why a button is unavailable needs a manual test.
