Roles and status semantics
VLoader, the named loaders andVSpinnerrender arole="status"region with an accessible name. Status regions are polite, so they never interrupt what a screen reader is currently saying.VSkeleton,VPatternSkeletonand the named skeleton presets are decorative by default. Add alabelto turn them into arole="status"region witharia-busy="true".VProgressrendersrole="progressbar"and exposesaria-valuemin,aria-valuemaxandaria-valuenowonly while the progress is determinate. An indeterminate bar never claims a percentage.VLoadingsetsaria-busyon its wrapper and announces its label through the overlay. The built-in spinner is decorative so the state is not announced twice.VLoadingButtonexposesaria-busywhile it works and relies on the nativedisabledattribute to prevent repeated activation. The label stays in the accessibility tree.
Decorative mode
A page can contain dozens of indicators. Announcing each of them would be noise, so use the decorative mode when a parent region already reports the state, for example inside a card that has its own aria-busy label.
<!-- Announced once, as a polite status -->
<VLoader label="Loading projects" />
<!-- Silent, because a parent region already announces the state -->
<VLoader decorative />
<!-- Silent, because the label is empty -->
<VLoader label="" /><!-- Decorative by default -->
<VArticleSkeleton />
<!-- Announced as a busy status region -->
<VArticleSkeleton label="Loading article" />Loading overlays and focus
VLoading makes the wrapped content inert while the overlay is visible. Content beneath the overlay can no longer receive pointer or keyboard focus, so keyboard users cannot tab into a region that is being replaced. The overlay itself is not focusable: it reports state, it does not accept input. Set :inert-content="false" when the content must stay interactive while it refreshes.
Progress values
Only expose a percentage when you really know it. An indeterminate bar is a valid and often better answer than a fabricated one.
<!-- Determinate: the value is exposed to assistive technology -->
<VProgress :value="42" label="Uploading files" value-text="42 of 84 files" />
<!-- Indeterminate: no percentage is claimed -->
<VProgress label="Preparing download" />Buttons
A loading button keeps its accessible name but is natively disabled while loading, so it cannot be activated or reached by tabbing until the operation finishes.
<VLoadingButton :loading="isLoading" @click="save">
Save changes
</VLoadingButton>Reduced motion
Every animation in the library is disabled or reduced under prefers-reduced-motion: reduce. The shimmer disappears, the loader animations stop and the progress bar falls back to a static fill. Align your own transitions with the same media query.
/* The library honours the operating system preference.
You can align your own animations with the same query. */
@media (prefers-reduced-motion: reduce) {
.my-fade {
animation: none;
transition: none;
}
}Contrast
Indicators inherit currentColor by default, so they follow the contrast of the text around them. The skeleton base colour is decorative and never carries meaning on its own. When you override the colours, check them against the surface behind the placeholder and keep the ratio of meaningful text at or above 4.5:1.
Checklist
- Give every indicator an accessible name, or make it decorative.
- Announce the result of a finished operation, not just its start.
- Keep focus inside the region that is being updated.
- Do not use a placeholder as the only indicator of an error.
- Test with a screen reader and with reduced motion enabled.
The behaviour described here is covered by the library unit tests. No formal WCAG conformance audit has been carried out.