Skip to content

Get started

Two surfaces, one rule set.

Both packages are live on npm at 0.2.0. The plugin and the CLI run the exact same rule modules — output is identical by construction, and a parity test in the repo asserts it.

The ESLint plugin

A standard flat-config plugin. It also runs under oxlint via jsPlugins, unchanged — the aria repo’s own .oxlintrc.json is a working example.

install
npm install --save-dev eslint eslint-plugin-aria-a11y
eslint.config.js
// eslint.config.js
import aria from 'eslint-plugin-aria-a11y';

export default [
  { plugins: { 'aria-a11y': aria }, rules: aria.configs.recommended.rules },
];

The recommended config sets the three format-tier rules to error — that’s the CI gate — and the five lint-tier rules to warn. Note: installing the package doesn’t wire it in — you have to add it to your eslint.config.js as above, which is a separate step from running the standalone CLI below.

The zero-config CLI

No ESLint config file, no host setup — point it at files or directories and it works, parsing .jsx/.tsx (and plain JS) out of the box.

usage
npx @aria-a11y/cli check [paths]   # report a11y diagnostics (both tiers);
                                   # exits nonzero on any format-tier issue — the CI teeth
npx @aria-a11y/cli fix   [paths]   # apply format-tier (safe, meaning-preserving) fixes only

Under the hood the CLI wraps ESLint’s Linter programmatically with a Babel→ESTree parser — so eslint is a real internal dependency. That’s an implementation detail, not something you configure. “Standalone” means no ESLint config and no host — not a claim of zero ESLint code inside.

Optional: declare your design system

Both surfaces pick up an aria.config.{ts,js,json} if present, but require none. Declaring a component’s semantics turns Aria’s guesses into ground truth — diagnostics move from inferred to declared basis, and for a component that renders a non-semantic element, opting in with injectRole graduates a suggestion to a real auto-fix. The line between guess and known moves in your favor as your design system declares more.

aria.config.ts
// aria.config.ts — optional; how a design system declares component semantics
import { defineConfig } from '@aria/config';

export default defineConfig({
  componentSemantics: {
    // native <button>: declared so name checks understand it
    IconButton:  { role: 'button', requiresName: true, nameProp: 'aria-label' },
    // <div> that needs a role: opt into injection
    MenuButton:  { role: 'button', injectRole: true },
  },
});

Version note

Version note: latest is 0.2.0; anything 0.1.1+ is safe. Avoid only 0.1.0 — it exists in npm’s history but was broken for installers (a packaging bug — its manifest pointed at unshipped src); fixed in 0.1.1 and guarded by a real install-and-import CI check. 0.2.0 makes role injection opt-in (injectRole); details in the repo’s CHANGELOG.