Imagine trying to communicate in a foreign language by randomly combining words without understanding grammar rules. You might be understood sometimes, but often you'd create confusion or say something completely different from what you intended. ARIA allowed attributes work similarly—they represent the "grammar rules" that determine which ARIA attributes can be meaningfully used together on different HTML elements.
ARIA allowed attributes refers to the specification that defines which ARIA attributes are valid and meaningful for each HTML element and ARIA role. When you use ARIA attributes that aren't allowed on specific elements, you create "grammatical errors" that can confuse assistive technologies, potentially making your content less accessible rather than more accessible.
Using ARIA attributes correctly isn't just about following specifications—it's about ensuring your accessibility efforts actually work as intended:
Invalid ARIA attributes can be worse than no ARIA at all—they can actively mislead users of assistive technologies, creating confusion and barriers where none should exist.
One of the most frustrating situations in web accessibility is when well-intentioned ARIA implementations actually make interfaces less accessible. This often happens when developers use ARIA attributes that aren't allowed or meaningful for specific elements, creating contradictory or confusing information for assistive technologies.
ARIA attributes fall into different categories, each with specific rules about where they can be used:
These attributes can be used on any HTML element, regardless of its role or type.
<!-- Global attributes work on any element -->
<div aria-label="Close button">×</div>
<p aria-describedby="help-text">Enter your password</p>
<img src="chart.png" aria-labelledby="chart-title" alt="">
<span aria-hidden="true">👍</span>
<!-- Examples of global ARIA attributes -->
aria-label
aria-labelledby
aria-describedby
aria-hidden
aria-live
aria-atomic
aria-relevant
aria-busy
These attributes only make sense on interactive elements or elements with specific widget roles.
<!-- Valid: On interactive elements -->
<button aria-pressed="false">Toggle</button>
<input type="checkbox" aria-checked="mixed">
<div role="tab" aria-selected="true" tabindex="0">Tab 1</div>
<!-- Invalid: On non-interactive elements -->
<p aria-pressed="false">This doesn't make sense</p>
<div aria-checked="true">Not a checkbox</div>
<!-- Widget-specific attributes include -->
aria-checked
aria-pressed
aria-selected
aria-expanded
aria-disabled
aria-required
These establish relationships between elements and must reference valid targets.
<!-- Valid: References existing elements -->
<button aria-controls="menu" aria-expanded="false">Menu</button>
<ul id="menu">
<li>Option 1</li>
<li>Option 2</li>
</ul>
<input type="text" aria-describedby="pwd-help">
<div id="pwd-help">Password must be 8+ characters</div>
<!-- Invalid: References non-existent elements -->
<button aria-controls="nonexistent-menu">Menu</button>
<input type="text" aria-describedby="missing-help">
<!-- Relationship attributes include -->
aria-controls
aria-describedby
aria-labelledby
aria-owns
aria-flowto
These control how dynamic content changes are announced to users.
<!-- Valid: On containers that will have dynamic content -->
<div id="status" aria-live="polite"></div>
<div id="error-messages" aria-live="assertive" role="alert"></div>
<div aria-live="polite" aria-atomic="true">Loading: 50%</div>
<!-- Less useful: On static content that won't change -->
<h1 aria-live="polite">Static Page Title</h1>
<!-- Live region attributes include -->
aria-live
aria-atomic
aria-relevant
aria-busy
Understanding the most common validation errors helps you avoid them in your implementations:
Widget-specific attributes only make sense on interactive elements or elements with appropriate roles.
<!-- Invalid: Widget attributes on static elements -->
<div aria-checked="true">Completed task</div>
<p aria-expanded="false">This paragraph text</p>
<span aria-pressed="true">Bold text</span>
<!-- Valid: Widget attributes on appropriate elements -->
<div role="checkbox" aria-checked="true" tabindex="0">Completed task</div>
<button aria-expanded="false" aria-controls="details">Show details</button>
<button aria-pressed="true">Bold (toggle button)</button>
<!-- Or use semantic HTML -->
<input type="checkbox" checked> Completed task
<details>
<summary>Show details</summary>
<p>Detail content...</p>
</details>
ARIA attributes have specific allowed values, and using invalid values can break functionality.
<!-- Invalid values -->
<button aria-pressed="yes">Toggle</button> <!-- Should be true/false -->
<div aria-live="immediate">Status</div> <!-- Should be polite/assertive/off -->
<input aria-required="yes"> <!-- Should be true/false -->
<div role="heading" aria-level="large">Title</div> <!-- Should be 1-6 -->
<!-- Valid values -->
<button aria-pressed="true">Toggle</button>
<div aria-live="assertive">Status</div>
<input aria-required="true">
<div role="heading" aria-level="2">Title</div>
<!-- Common valid values for different attributes -->
aria-expanded: true | false
aria-pressed: true | false | mixed
aria-checked: true | false | mixed
aria-live: polite | assertive | off
aria-required: true | false
aria-disabled: true | false
aria-hidden: true | false
aria-level: 1 | 2 | 3 | 4 | 5 | 6
Attributes that reference other elements must reference existing elements with valid IDs.
<!-- Invalid: References don't exist -->
<button aria-controls="sidebar">Toggle Sidebar</button>
<input aria-describedby="help-text">
<!-- Missing: <div id="sidebar"> and <div id="help-text"> -->
<!-- Invalid: Multiple references with errors -->
<input aria-labelledby="first-label second-label missing-label">
<div id="first-label">Name:</div>
<div id="second-label">Required</div>
<!-- Missing: <div id="missing-label"> -->
<!-- Valid: All references exist -->
<button aria-controls="sidebar">Toggle Sidebar</button>
<div id="sidebar">Sidebar content</div>
<input aria-describedby="help-text">
<div id="help-text">Enter your full name</div>
<input aria-labelledby="first-label second-label">
<div id="first-label">Name:</div>
<div id="second-label">(Required)</div>
Some ARIA attributes conflict with each other or with implicit HTML semantics.
<!-- Invalid: Conflicting label sources -->
<button aria-label="Close dialog" aria-labelledby="close-text">
<span id="close-text">Close</span>
</button>
<!-- Invalid: Redundant with HTML semantics -->
<button disabled aria-disabled="true">Submit</button>
<input required aria-required="true">
<!-- Valid: Choose one labeling method -->
<button aria-label="Close dialog">×</button>
<!-- OR -->
<button aria-labelledby="close-text">
<span id="close-text">Close</span>
</button>
<!-- Valid: Let HTML handle it when possible -->
<button disabled>Submit</button>
<input required>
<!-- Valid: ARIA when HTML isn't sufficient -->
<div role="button" aria-disabled="true" tabindex="-1">
Custom disabled button
</div>
Different ARIA roles allow different sets of attributes. Understanding these relationships helps you use ARIA correctly:
<!-- Allowed attributes for button role -->
<div role="button"
aria-pressed="false" <!-- Valid: buttons can be pressed -->
aria-expanded="false" <!-- Valid: buttons can control expansion -->
aria-disabled="true" <!-- Valid: buttons can be disabled -->
aria-describedby="help" <!-- Valid: global attribute -->
tabindex="0"> <!-- Required for custom buttons -->
Custom Button
</div>
<!-- Invalid attributes for button role -->
<div role="button"
aria-checked="true" <!-- Invalid: buttons aren't checkboxes -->
aria-selected="true" <!-- Invalid: buttons aren't selectable options -->
aria-level="2"> <!-- Invalid: buttons don't have levels -->
Invalid Button
</div>
<!-- Valid tab interface -->
<div role="tablist">
<div role="tab"
aria-selected="true" <!-- Valid: tabs can be selected -->
aria-controls="panel1" <!-- Valid: tabs control panels -->
tabindex="0">
Tab 1
</div>
<div role="tab"
aria-selected="false"
aria-controls="panel2"
tabindex="-1">
Tab 2
</div>
</div>
<div role="tabpanel"
id="panel1"
aria-labelledby="tab1"> <!-- Valid: panels can be labeled -->
Panel 1 content
</div>
<!-- Invalid: Wrong attributes for tab role -->
<div role="tab"
aria-checked="true" <!-- Invalid: tabs aren't checkboxes -->
aria-pressed="false"> <!-- Invalid: tabs aren't buttons -->
Invalid Tab
</div>
<!-- Valid form control attributes -->
<div role="textbox"
aria-required="true" <!-- Valid: form controls can be required -->
aria-invalid="false" <!-- Valid: can indicate validation state -->
aria-describedby="help" <!-- Valid: can reference help text -->
contenteditable="true" <!-- Required for custom textbox -->
tabindex="0">
</div>
<div role="checkbox"
aria-checked="mixed" <!-- Valid: checkboxes have checked states -->
aria-required="true" <!-- Valid: can be required -->
aria-describedby="info" <!-- Valid: global attribute -->
tabindex="0">
Custom checkbox
</div>
<!-- Invalid: Wrong attributes for form controls -->
<div role="textbox"
aria-expanded="true" <!-- Invalid: textboxes don't expand -->
aria-pressed="false"> <!-- Invalid: textboxes aren't buttons -->
Invalid textbox
</div>
Several approaches can help you identify and fix ARIA attribute validation issues:
Use accessibility testing tools that include ARIA validation.
// Example: Using axe-core programmatically
import { axe, toHaveNoViolations } from 'jest-axe';
test('should not have ARIA violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
// Example: Using Pa11y for ARIA validation
const pa11y = require('pa11y');
pa11y('http://localhost:3000', {
standard: 'WCAG2AA',
includeNotices: false,
includeWarnings: true
}).then((results) => {
console.log(results.issues);
});
Modern browsers provide built-in accessibility inspection tools.
<!-- Chrome DevTools Accessibility Panel -->
1. Open DevTools (F12)
2. Go to Elements panel
3. Select element with ARIA attributes
4. Look at Accessibility panel (may need to enable)
5. Check for warnings about invalid attributes
<!-- Firefox Accessibility Inspector -->
1. Open DevTools (F12)
2. Go to Accessibility panel
3. Inspect elements for ARIA issues
4. Use the accessibility tree view
<!-- Safari Web Inspector -->
1. Open Web Inspector
2. Go to Elements tab
3. Check Accessibility section in sidebar
4. Review ARIA attribute validity
Test with actual screen readers to identify when invalid ARIA causes confusion.
Pay attention to whether the screen reader announces information that matches your visual interface, or if invalid ARIA creates confusion.
Integrate ARIA validation into your development workflow.
// ESLint with jsx-a11y plugin
{
"extends": ["plugin:jsx-a11y/recommended"],
"rules": {
"jsx-a11y/aria-props": "error",
"jsx-a11y/aria-proptypes": "error",
"jsx-a11y/aria-unsupported-elements": "error",
"jsx-a11y/role-has-required-aria-props": "error",
"jsx-a11y/role-supports-aria-props": "error"
}
}
// Stylelint with a11y plugin
{
"plugins": ["stylelint-a11y"],
"rules": {
"a11y/content-property-no-static-value": true,
"a11y/font-size-is-readable": true,
"a11y/line-height-is-vertical-rhythmed": true
}
}
// Pre-commit hooks for validation
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"npm run test:a11y"
]
}
}
Follow these practices to maintain valid ARIA implementations:
Use semantic HTML elements first, then enhance with ARIA only when necessary.
<!-- Good: Semantic HTML needs minimal ARIA -->
<button type="submit">Submit Form</button>
<input type="checkbox" required>
<select aria-describedby="format-help">
<option>Choose format</option>
</select>
<div id="format-help">Select your preferred file format</div>
<!-- Avoid: Recreating semantic elements with ARIA -->
<div role="button"
aria-pressed="false"
tabindex="0"
onclick="submitForm()">
Submit Form
</div>
Use tools during development rather than trying to fix issues later.
// React example with validation
import React from 'react';
function CustomButton({ pressed, disabled, children, ...props }) {
// Validate props during development
if (process.env.NODE_ENV === 'development') {
if (typeof pressed !== 'boolean' && pressed !== undefined) {
console.warn('aria-pressed must be true, false, or undefined');
}
if (typeof disabled !== 'boolean' && disabled !== undefined) {
console.warn('aria-disabled must be true, false, or undefined');
}
}
return (
<div
role="button"
aria-pressed={pressed}
aria-disabled={disabled}
tabIndex={disabled ? -1 : 0}
{...props}
>
{children}
</div>
);
}
Ensure ARIA attributes remain valid as component states change.
// Example: Validating dynamic ARIA states
function ExpandableSection({ title, children }) {
const [expanded, setExpanded] = React.useState(false);
const contentId = React.useId();
return (
<div>
<button
aria-expanded={expanded} // Always boolean
aria-controls={contentId} // Always valid ID
onClick={() => setExpanded(!expanded)}
>
{title}
</button>
<div
id={contentId} // ID matches aria-controls
hidden={!expanded} // Visual state matches ARIA
>
{children}
</div>
</div>
);
}
// Test all states
test('maintains valid ARIA in all states', () => {
const { getByRole } = render(<ExpandableSection title="Test" />);
const button = getByRole('button');
// Test collapsed state
expect(button).toHaveAttribute('aria-expanded', 'false');
// Test expanded state
fireEvent.click(button);
expect(button).toHaveAttribute('aria-expanded', 'true');
});
Proper ARIA validation delivers concrete business benefits:
These benefits compound over time, creating more sustainable and successful accessibility implementations that serve users effectively while supporting business objectives.
Different development environments require tailored approaches to ARIA validation:
The key is integrating validation into your existing development workflow rather than treating it as a separate, manual process.
ARIA allowed attributes represent the precision required to make accessibility implementations truly effective. Like any powerful tool, ARIA's effectiveness depends on using it correctly according to established standards and specifications.
The goal of understanding ARIA validation isn't to memorize every rule, but to develop an appreciation for the systematic approach that makes web accessibility work reliably. When ARIA attributes are used correctly, they create seamless bridges between visual interfaces and assistive technologies.
What makes ARIA validation particularly important is its role in preventing accessibility regressions. Invalid ARIA can be worse than no ARIA at all, creating confusion and barriers where none should exist. By validating your ARIA implementations, you ensure that your accessibility efforts actually improve user experiences.
As web applications become more complex and interactive, the importance of valid ARIA implementation only increases. The investment in understanding and implementing ARIA correctly pays dividends in reliable, maintainable accessibility that serves users effectively across different technologies and contexts.
Greadme's easy-to-use tools can help you identify invalid ARIA attributes on your website and provide clear guidance on fixing them—even if you're not technically minded.
Validate Your Website's ARIA Implementation Today