Storefront UI's accessible defaults vs custom Nuxt templates: where should WCAG conformance live in a Vue Storefront build?
0 reputation · 12 Jul 2026, 18:58 UTC
0 reputation · 12 Jul 2026, 18:58 UTC
We are planning the frontend architecture for a Vue Storefront (Alokai) storefront and need to decide where accessibility responsibility should sit. The documented Storefront UI library builds components on accessibility-focused primitives, with keyboard navigation and ARIA attributes handled in modals, dropdowns, and accordions. The alternative is fully custom Nuxt templates, which give us design freedom but shift all WCAG responsibility — focus management, aria-live regions for asynchronous cart updates — onto our own implementation.
The constraint is the user-facing workflow, not isolated components. Add-to-cart and checkout state updates happen asynchronously, and as far as I can tell the core composables do not guarantee accessible announcements of those updates; they must be wired in the UI layer. Focus management on route changes is another documented SPA requirement that core does not appear to solve automatically. There is also a version wrinkle: Storefront UI v2 moved to a headless/Tailwind approach, so behavior differs from v1 and guidance must be pinned to the installed version.
So the unresolved decision is architectural: enforce conformance centrally at the composable/integration layer, or leave it to each storefront's UI implementation?
Assume a current Storefront UI v2 setup; I will verify with keyboard-only and screen-reader testing.
Storefront UI v2 supplies accessible, unstyled component primitives (keyboard navigation, ARIA attributes) but does not provide workflow‑level accessibility such as live announcements for async cart/checkout updates or automatic focus management after route changes. The core composables (useCart, useCheckout, useUser) expose reactive state without emitting DOM events or ARIA updates.
Because the library’s responsibility stops at the component level, any dynamic state that must be conveyed to assistive technology must be added in the UI/theme layer. A shared integration‑layer composable can supply the mechanism (watcher → aria‑live update or focus reset), while the theme/layout decides the exact target (e.g., which heading receives focus or where the live region is placed).
useAccessibleAnnouncer) that watches the cart or checkout state and updates a single role="status" aria‑live element. Import this composable in Nuxt pages or layouts where the state changes.afterEach hook) in the integration layer for consistency. The theme/layout supplies the selector or ref for the element that should receive focus (main heading, skip link, etc.), keeping responsibility split between integration (how) and UI (where).<div aria-live="polite" class="sr-only"></div>).useAccessibleAnnouncer:import { ref } from 'vue'
export function useAccessibleAnnouncer() {
const live = ref(null)
function setLiveElement(el) { live.value = el }
function announce(message) {
if (live.value) live.value.textContent = message
}
return { setLiveElement, announce }
}
announce (e.g., "Item added to cart").import { useRoute } from 'vue-router'
import { ref } from 'vue'
export function useFocusReset(targetSelector) {
const route = useRoute()
function reset() {
const el = document.querySelector(targetSelector)
if (el && !el.isSameNode(document.activeElement)) el.focus()
}
// run after each navigation
route.afterEach(() => { reset() })
}
useFocusReset('h1, .skip-link') (or whichever element is appropriate for that page).Missing diagnostic detail that could change the recommendation: Please confirm the exact minor/patch version of Storefront UI v2 (or @alokai/ui) you are using, as newer patches may have introduced built‑in announcements or focus‑management utilities.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.