Skip to main content
Vuetify0 is now a release candidate!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Accessibility

v0 provides ARIA attributes out-of-the-box through the attrs pattern. You provide styling and visual feedback.

The attrs Pattern

Every v0 component exposes an attrs object containing all accessibility attributes. Spread it onto your elements:

vue
<script setup>
  import { Selection } from '@vuetify/v0'

  const items = ['Apple', 'Banana', 'Cherry']
</script>

<template>
  <Selection.Root v-slot="{ attrs }">
    <div v-bind="attrs">
      <Selection.Item v-for="item in items" :key="item" v-slot="{ attrs }">
        <button v-bind="attrs">{{ item }}</button>
      </Selection.Item>
    </div>
  </Selection.Root>
</template>

What’s Included in attrs

ComponentARIA Attributes Provided
Selection.Itemaria-selected, aria-disabled, data-selected, data-disabled
Group.Itemrole="checkbox", aria-checked, aria-disabled, data-selected, data-disabled, data-mixed
ExpansionPanel.Activatorid, role, tabindex, aria-expanded, aria-controls, aria-disabled
Pagination.Rootaria-label, role="navigation"[1]
Popover.Activatorpopovertarget, data-open[2]
Tip

Always spread the attrs object from slot props onto your interactive elements. Missing ARIA attributes break screen reader support.

Developer Responsibilities

v0 provides the ARIA plumbing. You must provide:

ResponsibilityExample
Visual focus indicators:focus-visible { outline: 2px solid blue }
Color contrastEnsure 4.5:1 ratio minimum
Visible labelsAdd <label> or aria-label for inputs
Skip linksNavigation landmarks for keyboard users

Focus Trapping

v0 does not provide focus trapping. Use external solutions:

Roving Tabindex

v0 does not provide roving tabindex. This keeps the library headless - implement in your design system layer if needed for arrow key navigation between items.

Keyboard Navigation

What v0 Handles

  • tabindex management (-1 when disabled, 0 when enabled)

  • ARIA state synchronization (aria-expanded, aria-selected)

  • Data attributes for styling (data-selected, data-disabled)

What You Implement

PatternKeys to Handle
List selectionArrow keys, Home/End
MenusArrow keys, Escape, Enter
DialogsEscape to close, focus trap
TabsArrow keys, Home/End
ts
// You implement navigation logic - v0 provides selection state
function onKeydown (e: KeyboardEvent, ids: string[], currentIndex: number) {
  switch (e.key) {
    case 'ArrowDown': selection.select(ids[currentIndex + 1]); break
    case 'ArrowUp': selection.select(ids[currentIndex - 1]); break
    case 'Home': selection.select(ids[0]); break
    case 'End': selection.select(ids[ids.length - 1]); break
  }
}

Testing Strategies

Automated Testing

MyComponent.test.ts
import { axe } from 'vitest-axe'

it('passes accessibility audit', async () => {
  const { container } = render(MyComponent)
  expect(await axe(container)).toHaveNoViolations()
})

Manual Testing Checklist

Use this checklist during manual QA:

markdown
- [ ] Tab through all interactive elements
- [ ] Verify focus visibility
- [ ] Test with keyboard only (no mouse)
- [ ] Check color contrast with DevTools
- [ ] Validate with browser accessibility tree
ToolPurpose
axe DevToolsBrowser extension for WCAG scanning
LighthouseBuilt-in Chrome audit
NVDA/VoiceOverScreen reader verification

Internationalization

Every v0 component ships an inline English accessible name for the ARIA labels it renders — a Dialog close button reads "Close", a pagination landmark reads "Pagination", and so on. Each default lives at the component’s call site as locale.ti(key) ?? 'English default', so an app that never installs a locale plugin still satisfies WCAG 4.1.2 Name, Role, Value↗︎ out of the box. No locale strings are bundled into the runtime.

To localize those names, install the Locale plugin with translations for the component keys (Dialog.close, Pagination.label, Carousel.next, …). When a key resolves the component uses your translation; when it doesn’t, the inline English default still renders. The optional @vuetify/v0/locale/messages/en export is the canonical map of every key v0 components look up — import it as a starting point for a new translation, or register it as-is for a complete English baseline.

For RTL (right-to-left) support, see useRtl. Direction is managed independently from locale — useRtl provides a reactive isRtl boolean and sets the dir attribute on the target element.


  1. role="navigation" is only added when the root element isn’t already a <nav>. If you render Pagination.Root as="nav", the role is omitted to avoid redundant landmark roles. ↩︎

  2. Popover.Activator wires the native Popover API↗︎ via the popovertarget attribute. See the Browser Support page for fallback behavior in older browsers. ↩︎

Was this page helpful?

Ctrl+/