Skip to main content

ARIA Is Not Duct Tape

Use ARIA to describe real component behavior, not to patch over missing HTML, keyboard support, labels, focus, or state.

A wall of sorted hand tools hanging above a workbench.
Photo: Wall and Desk in the Workshop with Sorted Tools via Pexels — https://www.pexels.com/photo/wall-and-desk-in-the-workshop-with-sorted-tools-19174967/
View full screen Download image

ARIA can help people understand custom interface behavior when native HTML does not cover the pattern.

It can also make a broken component sound more accessible than it really is.

That is why ARIA should not be treated as duct tape. It is not a patch you stick over unclear labels, missing keyboard support, poor focus management, or a component that behaves differently from what it announces.

The practical rule is simple:

Use ARIA to describe behavior the component actually has.

Do not use ARIA to pretend the behavior exists.

The common project moment

A team builds a custom interaction because the default browser control does not match the design.

The component looks polished. The mouse interaction works. A quick visual QA pass does not find anything obvious.

Then someone tests with a keyboard or screen reader and the cracks appear:

  • focus lands on a div that does not announce what it is;
  • a clickable card acts like a button but is not reachable from the keyboard;
  • a custom dropdown says it is a combobox but does not follow combobox behavior;
  • a disclosure button visually expands content, but the expanded state is not exposed;
  • an icon-only control has an aria-label, but the visible meaning is still unclear;
  • a disabled-looking control can still be activated.

The team reaches for ARIA because ARIA feels like the accessibility layer.

But ARIA is not the layer that makes the interaction work. It is the layer that tells assistive technology what the interaction already is.

Use this when

Use this page when a screen includes:

  • custom buttons, links, menus, tabs, accordions, switches, dialogs, or comboboxes;
  • clickable div or span elements;
  • icon-only controls;
  • controls with expanded, selected, pressed, checked, invalid, required, busy, or disabled states;
  • framework components that hide the actual HTML being rendered;
  • ARIA attributes added late in development because a review found a problem.

Start with the problem, not the ARIA attribute

Before adding ARIA, name the actual problem.

Common problems include:

  • the control does not have a useful name;
  • the wrong element is being used;
  • the control cannot be reached or operated with the keyboard;
  • focus moves somewhere confusing;
  • the visual state changes but the programmatic state does not;
  • the component announces a role it does not actually support;
  • the visible label and accessible name do not match.

Each problem has a different fix.

If the issue is missing keyboard behavior, aria-label will not fix it.

If the issue is the wrong role, more descriptive help text will not fix it.

If the issue is a state change that never updates, a one-time ARIA attribute may make the first state sound right and every later state wrong.

Native HTML comes first

Native HTML gives teams a working starting point.

A real <button> already has button semantics and expected keyboard behavior. A real <a href="..."> is already a link. A real checkbox already exposes checked and unchecked states. A real label already connects text to a form field.

ARIA is most useful when native HTML does not fully describe a real interaction.

It is usually the wrong first move when a simpler native element would work.

Ask this before adding ARIA:

Is there a native element or simpler pattern that already does most of this correctly?

If the answer is yes, start there.

ARIA can describe behavior, but it does not create behavior

ARIA changes what is announced. It does not automatically add the interaction model.

For example:

  • role="button" does not add Enter and Space activation.
  • aria-expanded="true" does not show or hide content by itself.
  • aria-selected="true" does not create a working tab list.
  • aria-disabled="true" does not prevent a control from being clicked.
  • aria-invalid="true" does not explain how to recover from the error.
  • aria-live="polite" does not decide whether the message is timely, useful, or placed in the right flow.

ARIA can expose the contract.

The component still has to keep the contract.

Match the role to the expected behavior

A role is a promise.

If something is exposed as a button, people expect button behavior.

If something is exposed as a tab, people expect tab behavior.

If something is exposed as a combobox, people expect a much more specific pattern for typing, opening, choosing, closing, and moving focus.

Do not choose a role because it sounds close. Choose it only when the component behaves like that role.

If the team cannot support the expected keyboard and focus behavior, choose a simpler pattern.

Keep names, states, and visible meaning aligned

ARIA often gets used to add hidden names or hidden state.

That can be useful, but it can also hide product and content problems.

Check whether:

  • the visible label is clear before adding hidden text;
  • the accessible name includes the visible label when there is visible text;
  • icon-only controls have both a useful visual affordance and a useful accessible name;
  • selected, expanded, pressed, checked, invalid, required, busy, and disabled states update when the UI changes;
  • state is not communicated only by color, position, or styling;
  • the announced role, name, and state match what people can actually do.

If the visible experience and the announced experience tell different stories, people will not have the same interface.

Questions teams can ask

During design and content

  • Can this use a native control without losing the important interaction?
  • Is the visible label clear enough, or are we relying on an ARIA label to explain it later?
  • What state changes does the component need to communicate?
  • Does the design depend on color, position, or shape to explain the control?
  • Would a simpler pattern reduce the amount of ARIA needed?

During development

  • What element is actually rendered?
  • What role, name, and state does the component expose?
  • Does keyboard behavior match the exposed role?
  • Does focus move predictably before, during, and after interaction?
  • Are ARIA attributes updated whenever the component state changes?
  • Are we using ARIA to describe real behavior, or to cover for missing behavior?

During QA

  • Can the control be reached and operated without a mouse?
  • Does a screen reader announce the control in a way that matches its behavior?
  • Do state changes get announced or exposed when they matter?
  • Does the visible label match the accessible name?
  • Does the component still work after errors, loading states, validation, or dynamic updates?
  • Would someone understand the control if they encountered it outside the visual layout?

Common trail hazards

  • Adding role="button" to a generic element without adding real button keyboard behavior.
  • Using aria-label to hide vague visible wording instead of improving the wording.
  • Choosing a complex ARIA pattern when a simple native control would work.
  • Setting ARIA state once and forgetting to update it when the UI changes.
  • Announcing something as disabled while it can still be activated.
  • Using ARIA to create a screen reader-only experience that no longer matches the visible interface.
  • Copying an ARIA example without understanding the expected keyboard and focus model.

Small habit

Before adding an ARIA attribute, write one sentence:

We are adding this because the component already does [behavior], and assistive technology also needs to know [name, role, state, or relationship].

If the sentence is hard to finish, fix the component, label, focus behavior, or interaction model first.

  • Native HTML First — for choosing the simplest element or pattern before adding ARIA.
  • Name, Role, and Value — for checking what custom controls expose to assistive technology.
  • Accessible Names — for checking what controls are called.
  • Button vs Link — for deciding whether a clickable thing should navigate or perform an action.
  • Link Text and Button Labels — for making visible labels clear before adding hidden names.
  • Developer Component Checklist — for a broader component handoff and review pass.
  • Accordions — for checking expandable sections, button headers, keyboard behavior, and expanded or collapsed state.
  • Combobox — for checking a custom autocomplete pattern where ARIA, keyboard behavior, focus, suggestions, and selected value all need to match.
  • Tabs — for checking a custom tab pattern where ARIA, selected state, panel relationships, keyboard behavior, and focus all need to match.
  • Keyboard-Only Navigation — for checking whether custom controls work without a mouse.
  • WAI-ARIA Authoring Practices Guide — for expected keyboard and interaction patterns when ARIA widgets are truly needed.
  • Accessibility Glossary — for plain-language definitions of common accessibility terms used across this guide.

What to do next

If a component already has ARIA, pick one control and check whether its announced name, role, and state match what the component actually does.

If they do not match, do not add more ARIA yet. Fix the element choice, label, keyboard behavior, focus behavior, or state update first.