Accessibility isn’t just a checklist—it’s about making your application usable for everyone, regardless of their abilities, devices, or environment. In the Vue and Nuxt ecosystem, accessibility (a11y) is often overlooked until late in development. The good news? Small, intentional changes can make a massive impact.
This guide will walk you through practical, tested accessibility practices for Vue 3 and Nuxt 3 apps.
Enjoy!
🤔 Why Accessibility Matters in Vue/Nuxt?
- Legal compliance — In many regions, accessible websites are legally required.
- Better UX for everyone — Keyboard navigation, clear structure, and readable contrast improve usability for all users.
- SEO benefits — Search engines love well-structured, semantic HTML.
🟢 Improving Accessibility in Vue/Nuxt
Below, you will see a list of common Accessiblity improvement patterns that I try to implement each day in projects - both work and community ones :)
1. Start With Semantic HTML
Accessibility begins before Vue renders a single component.
Avoid <div>
soup by using meaningful HTML tags:
<!-- ❌ Bad -->
<div class="title">Welcome</div>
<!-- ✅ Good -->
<h1>Welcome</h1>
In Vue templates:
- Use
<main>
,<nav>
,<header>
,<footer>
,<section>
appropriately. - Reserve
<button>
for clickable actions—not<div>
with a@click
.
2. Manage Focus & Announce Route Changes
When routes change in Nuxt, screen readers need to know where to start reading again.
Example: Focus the page’s main heading after navigation:
<script setup>
import { onMounted, ref } from 'vue'
const headingRef = ref(null)
onMounted(() => {
headingRef.value?.focus()
})
</script>
<template>
<h1 ref="headingRef" tabindex="-1">Dashboard</h1>
</template>
Nuxt 3 ships with a built-in Route Announcer that automatically notifies assistive technologies when the page changes.
It adds a visually hidden <div>
with aria-live="assertive"
so screen readers announce the new page title without extra setup.
How to use:
- It’s enabled by default in Nuxt 3.
- You can customize the announcement text using Nuxt hooks or middleware if your app needs special phrasing.
- Combine it with focus management so both sighted and non-sighted users get the right context after navigation.
3. Ensure Proper Color Contrast
Tailwind makes adjusting colors easy, but that also makes it easy to pick low-contrast combos.
Check WCAG guidelines:
- Minimum contrast ratio: 4.5:1 for normal text.
- Use tools like Accessible Colors or Contrast Checker.
Tailwind tip: Define accessible colors in tailwind.config.js
so they’re used consistently.
4. Use ARIA Attributes Wisely
ARIA helps only when HTML alone can’t convey meaning.
Examples:
<!-- Dialog -->
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Confirm Delete</h2>
</div>
<!-- Live region for updates -->
<div aria-live="polite">
{{ statusMessage }}
</div>
Don’t use ARIA to “patch” incorrect HTML. Fix the markup first.
5. Make Components Keyboard-Friendly
Every interactive element must be reachable and operable via keyboard.
Example for a custom dropdown:
<template>
<div @keydown.down.prevent="focusNext" @keydown.up.prevent="focusPrev">
<!-- items -->
</div>
</template>
Checklist:
- Tab moves between items.
- Enter/Space activates actions.
- Escape closes modals/menus.
6. Provide Skip Links
For long pages, let keyboard users skip straight to the main content:
<a href="#main" class="sr-only focus:not-sr-only">Skip to content</a>
<main id="main"> ... </main>
7. Test with Real Tools
Automated checks are helpful, but nothing beats real-world testing:
- Lighthouse (Chrome DevTools)
- axe DevTools
- Screen readers — VoiceOver (macOS/iOS), NVDA (Windows), TalkBack (Android)
- Keyboard-only navigation — Try not touching your mouse for an entire session.
📖 Learn more
If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:
It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉
🧪 Advance skills
A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you're advancing your career or switching paths, it's a smart step toward success.
Check out Certificates.dev by clicking this link or by clicking the image below:
Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more!
✅ Summary
Accessibility isn’t an extra—it’s a baseline.
In Vue and Nuxt, adopting semantic HTML, managing focus, ensuring contrast, announcing route changes, and testing often will make your app more usable, inclusive, and even search-friendly.
Take care and see you next time!
And happy coding as always 🖥️
Top comments (3)
nice: you'll need to use polite instead assertive router announcer, that's too much noice for an screen reader: Nuxt has changed the default value to polite github.com/nuxt/nuxt/blob/5d783662...
On page refresh you shouldn't announce page loaded, only on page "transition".
If you're running some server action (fetch), you also need to announce the action, avoiding the auditory flickering (if the action takes less than human perceible time (below 400ms) you shouldn't announce the action), for example, using a simple fetch:
Hey buddy,
Thanks for this additional context!
will send you a few hints more, can you ping me at discord? you can find me at nuxt, vite or antfu servers