Accessibility structure
Use these rules to review how a page is organized and described: headings, landmarks, labels, frames, media, and focusable interactive elements. Start here when a component adds a new section, embedded frame, image, or media player.
For example, heading-levels provides a concrete heading-order
check. Compare its Bad/Good examples, enable the rule using its configuration, and run vp run lint.
For the full set, follow the accessibility guide.
Every rule on this page includes its purpose, prerequisites, configuration, and complete Bad/Good examples. Highlighted lines show what changes; copying an example gives you the complete source. Each rule also states its current support limits.
| Rule | Examples | Purpose |
|---|---|---|
a11y/heading-has-content |
Bad · Good | Require heading elements to have accessible content |
a11y/heading-levels |
Bad · Good | Disallow skipping heading levels |
a11y/iframe-has-title |
Bad · Good | Require iframe elements to have a title attribute |
a11y/img-alt |
Bad · Good | Require alt attribute on images for accessibility |
a11y/interactive-supports-focus |
Bad · Good | Require interactive role elements to be focusable |
a11y/label-has-for |
Bad · Good | Require labels to have associated form controls |
a11y/landmark-roles |
Bad · Good | Validate landmark role placement and uniqueness |
a11y/media-has-caption |
Bad · Good | Require media elements to have captions |
All rules · Rule Options · ESLint migration map · Project checks · Attributes across components
a11y/heading-has-content
Require heading elements to have accessible content
Default severity: warning
Presets: happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/heading-has-content": "warn"
}
},
},
});
vp run lint
Bad
The h2 contributes a heading level but has no heading content.
<template>
<h2></h2>
</template>
Good
Billing settings supplies the content of the existing level-two heading.
<template>
<h2>Billing settings</h2>
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/heading-levels
Disallow skipping heading levels
Default severity: warning
Presets: nuxt, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/heading-levels": "warn"
}
},
},
});
vp run lint
Bad
The heading sequence jumps directly from h1 to h3, skipping level two.
<template>
<h1>Account</h1>
<h3>Billing</h3>
</template>
Good
Changing the billing heading to h2 preserves a consecutive heading hierarchy.
<template>
<h1>Account</h1>
<h2>Billing</h2>
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/iframe-has-title
Require iframe elements to have a title attribute
Default severity: warning
Presets: happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/iframe-has-title": "warn"
}
},
},
});
vp run lint
Bad
The checkout frame has a source URL but no title describing the embedded content.
<template>
<iframe src="/checkout"></iframe>
</template>
Good
title="Checkout preview" names the content of that frame.
<template>
<iframe src="/checkout" title="Checkout preview"></iframe>
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/img-alt
Require alt attribute on images for accessibility
Default severity: warning
Presets: none
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/img-alt": "warn"
}
},
},
});
vp run lint
Bad
The avatar image is missing its alt attribute.
<template>
<img src="/avatar.png" />
</template>
Good
alt="User avatar" supplies a text alternative for the avatar.
<template>
<img src="/avatar.png" alt="User avatar" />
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/interactive-supports-focus
Require interactive role elements to be focusable
Default severity: warning
Presets: happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/interactive-supports-focus": "warn"
}
},
},
});
vp run lint
Bad
Giving a span the button role and a click handler does not make the element keyboard-focusable.
<template>
<span role="button" @click="open">Open</span>
</template>
Good
The native button is focusable and retains the same open action.
<template>
<button type="button" @click="open">Open</button>
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/label-has-for
Require labels to have associated form controls
Default severity: warning
Presets: happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/label-has-for": "warn"
}
},
},
});
vp run lint
Bad
The separate label is neither associated through for nor wrapped around the input.
<template>
<label>Email</label>
<input id="email" />
</template>
Good
for="email" matches the input ID and explicitly associates the two elements.
<template>
<label for="email">Email</label>
<input id="email" />
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/landmark-roles
Validate landmark role placement and uniqueness
Default severity: warning
Presets: nuxt, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/landmark-roles": "warn"
}
},
},
});
vp run lint
Bad
Two main elements declare duplicate main landmarks in the same template.
<template>
<main>Dashboard</main>
<main>Settings</main>
</template>
Good
The dashboard remains the main landmark; the settings area becomes a named navigation landmark.
<template>
<main>Dashboard</main>
<nav aria-label="Settings">...</nav>
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.
a11y/media-has-caption
Require media elements to have captions
Default severity: warning
Presets: happy-path, nuxt, ecosystem, opinionated
Automatic fix: None; review the suggested change
Applies to: Vue SFC templates and blocks, with script context where the rule requires it
Options: No rule-specific options. Severity and preset selection are configurable.
Configuration (Vite+)
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: {
"preset": "incremental",
"rules": {
"a11y/media-has-caption": "warn"
}
},
},
});
vp run lint
Bad
The video has playback controls but no caption track.
<template>
<video src="/demo.mp4" controls />
</template>
Good
A track with kind="captions" supplies the English captions for the same video.
<template>
<video src="/demo.mp4" controls>
<track kind="captions" src="/demo.en.vtt" srclang="en" label="English" />
</video>
</template>
Good avoids this rule's finding under the configuration above; other rules may still report diagnostics.