eslint-plugin-aria-state-validator
v1.1.1
Published
Comprehensive ESLint plugin for ARIA accessibility validation: dynamic state binding and static ARIA attributes correctness
Downloads
119
Maintainers
Readme
eslint-plugin-aria-state-validator
Comprehensive ESLint plugin for ARIA accessibility validation:
- Dynamic State Validation: Validates that ARIA state attributes are properly bound to component state
- Static ARIA Validation: Validates static ARIA attributes for correctness (spelling, values, conflicts)
Catches both runtime and static accessibility errors at build time.
Problem Statement: The "Runtime Gap" in Static Analysis
Existing accessibility tools like eslint-plugin-jsx-a11y validate the syntactic correctness of ARIA attributes (e.g., ensuring aria-expanded uses 'true' or 'false'), but they cannot verify whether these attributes are correctly connected to the component's JavaScript state and dynamically updated at runtime.
Issues This Causes:
- Toggle buttons with
aria-expanded="false"hardcoded, never changing when opened - Boolean values directly bound to ARIA attributes:
aria-expanded={isOpen}(incorrect - should be string) - Static ARIA states on interactive elements that should be dynamic
- Screen readers receiving inaccurate information about component states
Core Concept: State-Dependent ARIA Validation
This plugin uses AST (Abstract Syntax Tree) analysis to statically verify that ARIA state attributes are logically connected to component state (State/Props) and will update dynamically at runtime.
Key Differentiator
Goes beyond syntax checking to infer component dynamic behavior, proactively catching runtime accessibility errors during build/CI phase.
Installation
npm install --save-dev eslint-plugin-aria-state-validatorUsage
ESLint Flat Config (ESLint 9+)
// eslint.config.js
import ariaStateValidator from 'eslint-plugin-aria-state-validator';
export default [
...ariaStateValidator.configs['flat/recommended']
];Available flat presets:
flat/recommendedflat/strictflat/dynamic-onlyflat/static-only
Legacy ESLintRC Config
{
"plugins": ["aria-state-validator"],
"rules": {
"aria-state-validator/state-dependent-aria-validator": "warn"
}
}Two Rules for Comprehensive ARIA Validation
This plugin provides two complementary rules:
Rule 1: state-dependent-aria-validator (Dynamic State Validation)
Validates that ARIA state attributes are properly bound to component state and update dynamically.
Rule options:
interactiveHandlers(string[]): Override which event props are treated as interactive (default:onClick,onKeyDown,onKeyPress,onChange,onToggle)strictBooleanInference(boolean): Whentrue(default), only flags identifiers inferred as boolean-like; whenfalse, treats any identifier binding as boolean-riskignorePatterns(string[]): Regex patterns to skip checks for matching attribute names or value source text
Example:
{
rules: {
'aria-state-validator/state-dependent-aria-validator': ['warn', {
interactiveHandlers: ['onClick', 'onPointerDown'],
strictBooleanInference: true,
ignorePatterns: ['^aria-current$', '^\\{isOpen\\}$']
}]
}
}Rule 2: static-aria-validator (Static ARIA Validation)
Validates the correctness of ARIA attributes when they are used:
- Valid ARIA property names (catches typos with auto-fix)
- Correct values for boolean and enum ARIA attributes (auto-fix for "yes"/"no")
- No conflicting ARIA attributes (auto-fix available)
- No redundant roles on native HTML elements (auto-fix available)
- Required ARIA attributes based on roles (e.g.,
aria-checkedforswitch) - Role-attribute compatibility (e.g., prevents
aria-expandedonimg)
Note: This plugin validates how ARIA attributes are used, not whether they should be used. For enforcing the presence of accessibility features, use eslint-plugin-jsx-a11y.
Features & Validation Patterns
1. Boolean Value Direct Binding Detection
Detects: Direct binding of Boolean values to ARIA attributes
// ❌ Error: Boolean value directly bound
<button aria-expanded={isOpen}>Toggle</button>
// ✅ Correct: Explicit string conversion
<button aria-expanded={isOpen ? 'true' : 'false'}>Toggle</button>Suggestion Available: The rule provides a safe suggestion to convert bindings to string format ('true'/'false').
2. Static Value + Interactive Handler Detection
Detects: Static ARIA state values on elements with event handlers
// ❌ Error: Has onClick but aria-expanded is static
<button onClick={handleClick} aria-expanded="false">
Toggle
</button>
// ✅ Correct: Dynamic state binding
<button onClick={handleClick} aria-expanded={isOpen ? 'true' : 'false'}>
Toggle
</button>Smart Suggestion: When the handler contains state variable references (e.g., () => setExpanded(!expanded)), the plugin can suggest binding the inferred variable to the ARIA attribute:
// ❌ Before suggestion
<div onClick={() => setExpanded(!expanded)} aria-expanded="false">
Click to expand
</div>
// ✅ Suggested fix (automatically detects 'expanded' variable)
<div onClick={() => setExpanded(!expanded)} aria-expanded={expanded ? 'true' : 'false'}>
Click to expand
</div>Supported Patterns for Suggestions:
() => setX(!x)() => setX(true)() => setX(prev => !prev)
3. Role-Based State Association Validation
Detects: Interactive roles with static ARIA states or missing required states.
// ❌ Error: role="button" with static aria-pressed
<div role="button" aria-pressed="false">Button</div>
// ❌ Error: interactive switch role missing aria-checked
<div role="switch" onClick={toggle}>Toggle</div>
// ✅ Correct: Dynamic state binding
<div role="button" aria-pressed={isPressed ? 'true' : 'false'}>Button</div>
<div role="switch" aria-checked={isChecked ? 'true' : 'false'}>Toggle</div>Static ARIA Validation Patterns
4. Invalid ARIA Property Detection
Detects: Typos in ARIA attribute names
// ❌ Error: Typo in aria-label
<button aria-labell="Close">X</button>
// ✅ Correct (Auto-fixed): Proper spelling
<button aria-label="Close">X</button>5. Empty Required Values
Detects: ARIA attributes that require non-empty values
// ❌ Error: Empty aria-label
<div aria-label="">Content</div>
// ✅ Correct: Non-empty value
<div aria-label="Description">Content</div>6. Invalid Boolean Values
Detects: Incorrect boolean-like values
// ❌ Error: Invalid boolean value (Auto-fix available for "yes"/"no")
<div aria-hidden="yes">Content</div>
<div aria-disabled="1">Content</div>
// ✅ Correct: Use "true" or "false"
<div aria-hidden="true">Content</div>
<div aria-disabled="false">Content</div>7. Invalid Enum Values
Detects: Invalid values for enum-type ARIA attributes
// ❌ Error: Invalid aria-live value
<div aria-live="loud">Live region</div>
// ✅ Correct: Use valid enum value
<div aria-live="polite">Live region</div>
// Valid values: "off", "polite", "assertive"8. Conflicting ARIA Attributes
Detects: aria-label and aria-labelledby used together
// ❌ Error: Both attributes present (aria-labelledby takes precedence)
<div aria-label="Label" aria-labelledby="other-id">Content</div>
// ✅ Correct: Use only one
<div aria-labelledby="other-id">Content</div>9. Redundant Roles
Detects: Explicit roles that match native HTML semantics
// ❌ Error: Redundant role
<button role="button">Click me</button>
<nav role="navigation">Nav</nav>
<a href="#" role="link">Link</a>
// ✅ Correct: Use native semantics
<button>Click me</button>
<nav>Nav</nav>
<a href="#">Link</a>Supported ARIA Attributes (Dynamic Validation)
The plugin validates these dynamic ARIA state attributes:
aria-expanded- Toggle buttons, expandable elementsaria-selected- Tabs, selectable optionsaria-checked- Checkboxes, radio buttons, switchesaria-pressed- Toggle buttonsaria-hidden- Dynamically shown/hidden elementsaria-disabled- Dynamically enabled/disabled elementsaria-modal- Dialogs, modalsaria-current- Current active item indicatorsaria-valuenow,aria-valuemin,aria-valuemax- Range widgets (sliders, progress bars)aria-level- Hierarchical levelaria-posinset,aria-setsize- Set position and size
Supported Interactive Roles
Elements with these roles are expected to have dynamic ARIA states:
button,tab,checkbox,radio,switchmenuitem,menuitemcheckbox,menuitemradiooption,treeitem,slider,progressbardialog,alertdialog
Examples
Valid Patterns ✅
// Ternary operator for string conversion
<button aria-expanded={isOpen ? 'true' : 'false'}>Toggle</button>
// String() function
<button aria-pressed={String(isPressed)}>Toggle</button>
// .toString() method
<div aria-hidden={isHidden.toString()}>Content</div>
// Inverted boolean with ternary
<button aria-expanded={!isOpen ? 'true' : 'false'}>Toggle</button>
// Static ARIA on non-interactive element (no handler)
<div aria-label="static label">Content</div>Invalid Patterns ❌
// Direct boolean binding
<button aria-expanded={isOpen}>Toggle</button>
// Fix: aria-expanded={isOpen ? 'true' : 'false'}
// Logical expression without string conversion
<button aria-expanded={foo && bar}>Toggle</button>
// Fix: aria-expanded={foo && bar ? 'true' : 'false'}
// Static value with event handler
<button onClick={handleClick} aria-expanded="false">Toggle</button>
// Fix: Use dynamic state binding
// Interactive role with static ARIA
<div role="tab" aria-selected="true">Tab</div>
// Fix: Use dynamic state bindingTechnical Implementation
Technology Stack
- Base: ESLint plugin (Node.js environment)
- Parser:
@babel/eslint-parseror TypeScript parser for JSX/TSX AST parsing - ARIA Data Source:
aria-queryrole/property definitions for role-attribute compatibility - Analysis: Visitor pattern on JSX elements
Analysis Logic
- JSXElement Traversal: Identifies
roleandaria-*attributes - Pattern Analysis: Detects boolean-like bindings and static-value patterns in JSX expressions
- State Inference Heuristics: Infers likely state variables from handler/setter patterns (e.g.,
setX(!x)) - Pattern Validation: Checks for proper string conversion patterns
Expected Benefits
1. Accessibility Quality Improvement
Catch dynamic ARIA errors at build/CI phase instead of runtime
2. Developer Productivity
Apply targeted suggestions for repetitive ARIA state binding errors
3. Standardization
Enforce consistent dynamic ARIA usage patterns across projects
Development
Running Tests
npm testContributing
Contributions welcome! Please feel free to submit issues or pull requests.
License
MIT
Keywords
- eslint
- eslint-plugin
- accessibility
- a11y
- aria
- jsx
- react
- state-validation
- dynamic-attributes
Related Projects
- eslint-plugin-jsx-a11y - Comprehensive JSX accessibility checking
- axe-core - Runtime accessibility testing engine
Note: This plugin focuses on static analysis of ARIA state binding patterns. For comprehensive accessibility testing, combine with runtime testing tools like axe-core.
