Accessibility for Vue apps.
Vue 3's reactivity and template directives let you ship dynamic UI fast — and often skip semantic HTML in the process. The patterns, plugins, and audit workflows that catch the recurring failures.
What goes wrong in Vue projects.
v-on:click on <div> instead of <button>
Vue's `@click` directive works on any element, which makes it easy to drop click handlers on `<div>` or `<span>` and ship something that mouse users can use but keyboard users cannot. Use `<button>` and style it down.
v-if dropping content without aria-live
Toggling validation messages, toasts, or notifications with `v-if` is invisible to screen readers unless the parent is an aria-live region. Wrap with `role="status"` or `role="alert"`.
Custom modal components without focus trap
Hand-rolled `<Teleport to="body">` modals usually skip focus trapping. Use Vue's `focus-trap-vue` or Headless UI Vue's `Dialog` for correct behavior.
Forms without proper label binding
Vue components that wrap `<input>` often break the label-input relationship. Pass `id` from props and bind to both `<label for={id}>` and `<input :id="id">`. Use `useId()` from Vueuse for unique IDs.
v-for without :key on accessible widgets
Missing or non-unique keys cause Vue to reuse DOM nodes incorrectly when lists update. This can move focus unexpectedly and re-announce content. Always provide a stable, unique `:key`.
Transition components masking aria-live updates
Vue's `<Transition>` and `<TransitionGroup>` can delay or mask DOM changes from screen readers. For aria-live regions, prefer immediate visibility over animated entry.
The Vue accessibility stack we recommend.
eslint-plugin-vuejs-accessibility
npm i eslint-plugin-vuejs-accessibilityVue-specific port of jsx-a11y. Catches missing alt, invalid roles, click without keyboard handler, in `.vue` templates at lint time.
@axe-core/vue
npm i @axe-core/vuePlugin that runs axe-core against the live Vue app during development. Console logs WCAG violations. Strip in production.
Headless UI Vue
npm i @headlessui/vueAccessible primitives: Dialog, Menu, Listbox, Combobox, Switch. Same family as the React version, Tailwind-friendly.
Radix Vue
npm i radix-vueVue port of Radix UI primitives. Unstyled, accessible, full ARIA coverage. Use for complex widgets where Headless UI is too thin.
focus-trap-vue
npm i focus-trap-vueWrapper around focus-trap. Drop-in component to trap focus inside a custom modal or panel.
Step-by-step for a Vue accessibility audit.
- 1
Install eslint-plugin-vuejs-accessibility
Add to your eslint config: `extends: ["plugin:vuejs-accessibility/recommended"]`. Run on existing code; expect 10-100 violations on the first pass.
- 2
Add @axe-core/vue in main.ts (dev only)
Gate the plugin install on `import.meta.env.DEV`. Watch the browser console for live a11y reports.
- 3
Audit your design system templates
Most Vue accessibility debt clusters in shared components (Modal, Dropdown, FormField). Audit those first — fixes cascade to every consumer.
- 4
Add AccessProof to CI
External audit of deployed URLs. Block PRs that introduce regressions. Works for any Vue framework (Nuxt, Vite SPA, custom).
- 5
Manual screen reader pass
Use NVDA (Windows) or VoiceOver (macOS) on a representative flow. Tab order, announcement quality, error visibility.
Run a WCAG audit on your Vue site in 42 seconds.
External scan — no JS injected into your app
WCAG 2.2 + Section 508 + EN 301 549 in one pass
Court-ready PDF with element selectors
CI/CD gate — block deploys on regression
Works with Vue on any hosting (Vercel, Netlify, Fly, self-hosted)
Free plan — 1 site, monthly scan
Vue-specific questions.
Is Vue 3 more accessible than Vue 2?
They are equivalent in baseline accessibility — both emit standard HTML and depend on what your components render. Vue 3 adds `<Teleport>` (useful for modal portals) and improved TypeScript support (helps catch some bugs at compile time), but the accessibility responsibility is the same.
Does Nuxt have built-in accessibility tooling?
Nuxt does not bundle accessibility tooling by default. It provides good defaults (page title management, automatic head tags) that help with some WCAG criteria, and the broader Vue ecosystem (eslint plugin, vue-axe, Headless UI Vue) integrates cleanly with Nuxt.
How do I handle accessibility in component libraries like Vuetify or PrimeVue?
Both ship many components with reasonable ARIA defaults but specific accessibility quality varies by component. Audit the components you use heavily (data table, dialog, menu) against your own users' assistive tech. Some teams pair a major component library with react-aria-style primitives for the components where the library falls short.
What about Vue 2 / Options API legacy code?
Same WCAG rules apply. The eslint plugin works on both Composition and Options API. Focus management and ARIA attributes are framework-agnostic. The biggest legacy risk is custom-built modals from the Vue 2 era that pre-date the focus-trap libraries — audit those specifically.