oxlint-plugin-vue-sfc-a11y
oxlint-plugin-vue-sfc-a11y
Vue SFC accessibility rules for oxlint, as a JS plugin.
oxlint cannot read Vue templates natively (oxc#15761),
so projects keep ESLint around for eslint-plugin-vuejs-accessibility. These rules self-parse the
SFC with @vue/compiler-sfc and walk the real template AST, so the checks run under oxlint instead.
Role and element semantics come from aria-query — the
same data source upstream uses — so the interactive-role and interactive-element sets are identical
rather than re-derived.
Companion to oxlint-plugin-vue-sfc, which
ports the eslint-plugin-vue template rules.
Install
npm i -D oxlint-plugin-vue-sfc-a11y
{
"extends": ["./node_modules/oxlint-plugin-vue-sfc-a11y/configs/recommended.json"]
}
The npm package is oxlint-plugin-vue-sfc-a11y (npm rejects oxlint-plugin-vue-a11y as too
similar to eslint-plugin-vue-a11y); the plugin namespace is the shorter vue-a11y, so rule ids
read vue-a11y/alt-text. All four rules default to warn.
Rules
| Rule | Default | Description |
|---|---|---|
vue-a11y/alt-text |
warn | require a text alternative on img, object, area, input[type="image"] |
vue-a11y/aria-props |
warn | disallow aria-* attributes that are not in the ARIA spec |
vue-a11y/no-static-element-interactions |
warn | disallow interactive handlers on elements with no keyboard path |
vue-a11y/tabindex-no-positive |
warn | disallow positive tabindex |
Each is a port of the vuejs-accessibility/* rule of the same name, and defaults to upstream
behaviour exactly. Verified against a 1581-SFC production Nuxt codebase: every finding ESLint
reports, this reports, at identical line:column.
no-static-element-interactions has options; upstream has none
Upstream declares schema: []. It fires on several shapes that are correct as written, and with no
options the only lever left is a project-wide warning ceiling — which suppresses genuine findings
just as readily as false ones.
"vue-a11y/no-static-element-interactions": ["warn", {
"allowDynamicRole": true,
"allowSharedHandlerChild": true,
"ignoreElements": ["canvas"],
"ignoreHandlers": ["mousemove"]
}]
| Option | Default | What it exempts |
|---|---|---|
allowDynamicRole |
false |
:role="cond ? 'button' : undefined" — a bound role upstream cannot read, so it treats it as absent |
allowSharedHandlerChild |
false |
a wrapper whose @click expression is also run from a keyboard handler on a descendant — the real control inside |
ignoreElements |
[] |
tag names to skip outright |
ignoreHandlers |
[] |
handler names that should not count as interactive (mousemove is a pointer gesture, not an action) |
Why allowSharedHandlerChild and not "has a focusable child"
The obvious version of that option — skip when the subtree contains anything focusable — is wrong,
and measurably so. On the codebase this was built against it silenced a real finding: a card whose
@click emitted expand, wrapping an unrelated text input. The input is tabbable, so the weak test
passed it; expand still had no keyboard path at all.
The question worth asking is not "is something in here focusable" but "can this action be reached
from the keyboard". So the option looks for a descendant running the same handler expression
from a keydown/keypress/keyup handler. That clears the genuine proxying wrapper
(<div @click="switchValue"> around <input @keydown.enter="switchValue">) and leaves the card
reported. There is a regression test for both.
Known limitation: diagnostic positions
Template diagnostics render on the <script> block, with the true position prepended to the
message:
Comp.vue:31:1 warning vue-a11y(no-static-element-interactions): [template 12:5] <div> has an interactive handler...
oxlint gives JS-plugin rules a script-relative view of an SFC, so a template position — negative in
a template-first file — cannot be expressed.
oxc#26001 fixes this upstream with an
actualRange, and would give every rule here correct positions with no rule changes.
Tests
npm test # node:test, 54 specs across 4 rules
License
MIT
Related Packages
Vue SFC template rules for oxlint, as a JS plugin. Ports of eslint-plugin-vue ru...
Vue SFC linting and formatting on the oxc toolchain. Rules reach inside , no esl...
Framework-agnostic accessibility helpers (ARIA live regions, focus management, r...
WCAG accessibility audit tool — Nuxt layer with report generation, PDF export an...
Version History
| Version | Released | License |
|---|---|---|
| 0.2.0 | MIT | |
| 0.1.2 | MIT | |
| 0.1.1 | MIT | |
| 0.1.0 | MIT |