๐Ÿค–NEW:AI-Powered Incremental Builds โ€” your site updates in under 30 seconds. See what's new โ†’
WAI-ARIA Linter ยท 100% Client-Side

WAI-ARIA Attribute Validator & Linter

Lint HTML markup for invalid ARIA roles, missing required state attributes, redundant native tags, and inaccessible focus traps.

Load Sample ARIA Snippets
ARIA Conformance Score

100 / 100

โœ“ Clean ARIA Implementation

Diagnostics & Findings (0 items)

โœ“ No ARIA syntax errors, missing state attributes, or redundant native roles detected!
Technical Deep-Dive

How WAI-ARIA Validation Works โ€” And What This Linter Checks

1. What This Tool Actually Does

This linter runs five independent regex-based checks against the HTML you paste. First, it extracts every role="..." attribute and cross-references the value against the full W3C WAI-ARIA role taxonomy (landmark, widget, and structure roles) โ€” any value outside that list is flagged as an invalid role. Second, it scans for redundant roles that duplicate a native HTML5 element's implicit semantics, such as role="navigation" on a <nav>. Third, it checks that any role="checkbox" declares its state via aria-checked. Fourth, it confirms role="dialog" and role="alertdialog" elements carry an accessible name through aria-labelledby or aria-label. Fifth, it flags aria-hidden="true" placed directly on a native interactive element (button, link, input, select, textarea).

Each finding is returned as an error (20-point deduction) or a warning (8-point deduction) with a specific recommendation, and the running conformance score updates live as you edit the markup.

2. How to Use It

  1. Load one of the sample snippets (Accessible Modal, Common ARIA Violations, Accessible Tab Widget) to see the linter in action, or paste your own component markup.
  2. Read the ARIA Conformance Score โ€” it deducts points immediately as issues are found, giving you a fast severity signal.
  3. Work through the Diagnostics & Findings list from top to bottom; each entry shows the exact offending snippet plus a specific recommendation for the fix.
  4. Apply the fix in your actual codebase (not in this tool โ€” it doesn't modify your source), then re-paste the updated markup to confirm the issue clears.
  5. Treat a 0-issue result as a starting point, not a finish line โ€” pair it with a manual screen reader pass on any custom interactive widget.

3. The Underlying Accessibility Concepts

  • The First Rule of ARIA: "If a native HTML element or attribute has the semantics and behavior you require, use it instead of re-purposing an element and adding ARIA." Redundant-role warnings exist to reinforce this rule.
  • 4.1.2 Name, Role, Value โ€” Level A: Every component must expose a correct role, an accessible name, and any relevant state/value to the accessibility API. Invalid roles and missing required states (like aria-checked) are direct violations of this criterion.
  • Accessible naming for dialogs: a modal without aria-labelledby or aria-label is announced to screen reader users simply as "dialog" with no indication of its purpose, which is disorienting when a user is suddenly moved into it.
  • 2.1.1 Keyboard โ€” Level A (related): hiding a focusable interactive element with aria-hidden="true" doesn't remove it from the tab sequence, so sighted keyboard users land on a control that assistive technology users never hear announced โ€” a mismatch that breaks predictable keyboard operation.

4. A Worked Example

Load the "Common ARIA Violations" sample. It contains <div role="header">Site Header</div> โ€” "header" is not a recognized WAI-ARIA role value, so this raises an Invalid ARIA Role error. A few lines down, <button role="button" aria-hidden="true">Menu</button> triggers two separate findings: a redundant-role warning (native <button> already implies role="button") and an error for hiding a focusable element with aria-hidden="true".

Further down, <div role="checkbox">Accept Terms</div> is missing aria-checked, and <div role="dialog"><p>Unlabelled popup box</p></div> has no accessible name โ€” both flagged as errors. Together these five issues (2 errors + 1 error + 1 error + 1 warning, roughly) pull the conformance score well below the "Clean ARIA Implementation" threshold, illustrating how quickly small oversights compound in real component code.

5. Who Actually Uses This Tool

  • Design system and component library maintainers: lint custom tabs, modals, accordions, and comboboxes before publishing them for reuse across dozens of product teams.
  • WordPress block/plugin developers: validate custom Gutenberg block markup that adds interactive widgets (carousels, accordions, custom form controls) beyond core block defaults.
  • Frontend engineers reviewing pull requests: paste a diff's new markup in during code review to catch ARIA regressions before merge, without needing a full browser-based screen reader setup.
  • QA and accessibility specialists: use it as a fast triage step before a deeper manual NVDA/VoiceOver pass, focusing manual testing time on components that already pass static linting.

6. Common Mistakes & Limitations

  • Static analysis only: the linter reads markup as a snapshot; it cannot execute JavaScript, so state changes driven by click handlers (e.g. aria-expanded toggling open/closed) aren't verified for correctness โ€” only their presence in the pasted snapshot.
  • Presence isn't correctness: a control with aria-checked="false" passes the missing-state check even if the checkbox is visually checked โ€” this tool cannot verify that ARIA state matches actual UI state.
  • Scope is markup validity, not full accessibility: a component can have zero ARIA errors here and still fail on color contrast, keyboard trap, or focus-order issues โ€” those need their own dedicated tools.
  • Not every custom role needs every state: some roles (like role="tab") require aria-selected in the full ARIA Authoring Practices spec but aren't yet covered by this validator's required-state checks โ€” treat a clean scan as a baseline, not an exhaustive audit against the full ARIA spec.

Frequently Asked Questions

What is the First Rule of ARIA use?

The First Rule of ARIA states: "If you can use a native HTML element or attribute with the semantics and behavior already built in, then do so instead of re-purposing an element and adding an ARIA role, state or property to make it accessible." Use <button> instead of <div role="button">.

Why are redundant ARIA roles discouraged?

Adding role="navigation" to <nav> or role="button" to <button> adds unnecessary markup weight and can cause conflicting announcement verbosity on older screen reader versions.

What are required states and properties for ARIA widgets?

Certain interactive roles require accompanying attributes. For example, role="checkbox" requires aria-checked, role="slider" requires aria-valuenow, and role="combobox" requires aria-expanded.

Why should aria-hidden="true" never be placed on a focusable button or link?

When aria-hidden="true" is placed on a link or button, screen readers ignore it entirely, but keyboard users can still tab to it. This creates a confusing experience where focus stops on an unannounced invisible element.

What is WCAG 4.1.2 (Name, Role, Value) and how does it relate to ARIA errors?

SC 4.1.2 requires that every UI component have a programmatically determinable name, role, and state, and that state changes are exposed to assistive technology. An invalid role, a missing aria-checked, or an unlabelled dialog all break this criterion because the accessibility tree can no longer describe the component correctly.

Was WCAG 4.1.1 (Parsing) also related to ARIA validity?

It used to be referenced this way, but WCAG 2.2 formally removed Success Criterion 4.1.1 (Parsing), determining it to be obsolete for modern browsers and assistive technologies that handle malformed markup gracefully. Invalid ARIA roles and missing states are now evaluated purely under 4.1.2.

Can this validator catch every ARIA mistake in my code?

No. It performs static regex-based pattern matching against a snapshot of HTML, so it can only catch mistakes present in the markup as submitted. Dynamically toggled states (aria-expanded changing on click), JavaScript-injected attributes, and semantic correctness of a role choice for a given interaction pattern require manual testing with a real screen reader.

Why is role="presentation" or role="none" sometimes intentional?

These roles strip an element's implicit semantics from the accessibility tree โ€” useful for elements like a purely decorative <table> used for layout, or a wrapping <li> whose bullet semantics aren't meaningful. They are valid roles in this validator's list and are not flagged as errors on their own.