Accessibility usually gets lost before anyone writes an ARIA attribute. Teams choose the wrong element, build interaction states that only work with a mouse, or ship a component without checking what a keyboard user or screen reader experiences. Advanced tooling can’t repair a button that’s really a div, a dialog that loses focus, or a form error that never reaches the person who needs to correct it.
The stronger workflow starts with native HTML, then adds carefully scoped interaction patterns. Developers model expanded, selected, pressed, invalid, and busy states deliberately. They preserve predictable focus and screen reader behaviour, then verify the result with automated checks and manual testing. This matters in live services, where passing a one-off audit doesn’t prove that a dynamic component remains usable at larger text sizes, different orientations, or with assistive technology. The Government Digital Service has documented real implementation failures across public-sector services, including poor reflow, rather than treating accessibility as a purely theoretical checklist (GDS monitoring findings).
The ten accessibility best practices below follow a component through its lifecycle, from page structure and semantics to forms, visual states, responsive behaviour, screen reader verification, and CI delivery. DOM Studio provides a useful implementation lens because its headless primitives and Vue wrappers can reduce repeated work around ARIA patterns and keyboard handling, but no library removes the need for page-level judgement and user testing.
Table of Contents
- 1. WAI-ARIA Roles, States, and Properties
- 2. Keyboard Navigation and Focus Management
- 3. Semantic HTML and Progressive Enhancement
- 4. Colour Contrast and Visual Accessibility
- 5. Text Alternatives and Descriptive Labels
- 6. Responsive Design and Mobile Accessibility
- 7. Form Accessibility and Error Handling
- 8. Screen Reader Testing and Assistive Technology Compatibility
- 9. Focus Visibility and Visual Indicators
- 10. Internationalisation and Multilingual Accessibility
- 10-Point Accessibility Best Practices Comparison
- Make Accessibility a Component Contract
1. WAI-ARIA Roles, States, and Properties
ARIA works best as a precise description of behaviour, not as decoration added to generic markup. Start with native elements such as button, nav, main, and form. Add roles, states, and properties when a component has interaction semantics that HTML doesn’t provide directly, such as a menu, listbox, combobox, dialog, or command palette.
A submenu might expose aria-expanded on its controlling button and give its actionable entries the expected menu semantics. A dialog needs a clear accessible name through aria-labelledby or an equivalent visible label, and aria-modal="true" when the modal interaction restricts the rest of the page. A combobox must keep its expanded state, popup relationship, current value, and active suggestion synchronised. If those values drift, a screen reader user hears a different interface from the one sighted users see.
DOM Studio’s patterns illustrate why documenting these relationships matters. Its menu, dialog, combobox, toast, and command palette components use distinct roles and state attributes rather than asking each product team to recreate the model. For a practical example of the decisions behind an expandable control, see this guide to an accessible dropdown menu.
Keep ARIA state truthful
A stale aria-selected, an incorrect aria-expanded, or an overactive live region can be worse than no attribute because it creates misleading output. Use aria-live="polite" for limited, non-urgent updates such as a toast confirming that an action completed. Don’t place an entire changing results area in a live region unless you’ve tested the announcement volume and timing.
Automated tools such as axe-core and Playwright accessibility checks can detect missing names, invalid relationships, and some role violations. They can’t confirm whether the announcement makes sense during a real interaction.
Practical rule: Every ARIA attribute should have a corresponding state transition in the component’s interaction model and a manual test that confirms what assistive technology announces.
2. Keyboard Navigation and Focus Management
A component isn’t accessible if its primary action requires a pointer. Keyboard users need a logical tab sequence, visible focus, and interaction rules that match the component’s pattern. A menu commonly uses arrow keys to move between items, Enter or Space to activate, and Escape to close. A tab set typically uses arrow keys between tabs, while Tab moves into the selected panel and then onward to the next focusable element.
Dialogs need stronger focus management. When one opens, move focus to a sensible heading, close button, or first meaningful control. Keep Tab within the modal while it’s active, return focus to the invoking control when it closes, and make Escape behave consistently. Comboboxes need the same care around the input, highlighted suggestion, selection, and cancellation. If a filtered result is visible but focus or active-descendant state doesn’t follow it, keyboard users can’t reliably choose it.

Use tabindex="-1" for elements that need programmatic focus without joining the normal tab order. Use tabindex="0" only when a custom focusable element needs natural tab access. Positive values such as tabindex="1" create a second, fragile tab order and should be avoided.
Test without the mouse
Switch off pointer input and complete the entire flow. Check that focus never disappears behind a sticky header, that a menu doesn’t trap the user after closing, and that a custom shortcut doesn’t override standard browser behaviour without an alternative. The :focus-visible pseudo-class can show stronger indicators for keyboard navigation without adding visual noise after pointer clicks.
A visible indicator needs sufficient contrast and must remain visible against every surface the component can appear on. Test hover, active, disabled, selected, and expanded states separately. A component library should document its keyboard contract beside the component API, not hide it in an implementation file.
The video below provides a visual reference for keyboard interaction patterns.
3. Semantic HTML and Progressive Enhancement
Semantic HTML gives users and browsers useful structure before JavaScript adds behaviour. A native button already participates in keyboard interaction and exposes a button role. A div with role="button" still needs keyboard handling, focus management, activation logic, and careful testing. The custom version has more ways to fail and more code to maintain.
The same principle applies to page structure. Use nav for navigation landmarks, main for the primary content, and article or section where those elements describe the document’s organisation. Use a real form, and group related controls with fieldset and legend. Use select when a simple native choice is sufficient. A custom dropdown may offer richer presentation, but it also inherits responsibility for keyboard behaviour, popup positioning, selection announcements, and mobile interaction.
Progressive enhancement means the baseline task still works if JavaScript loads slowly, fails, or gets interrupted. A server-rendered form should submit to a server endpoint and return understandable validation feedback. A navigation link should remain a link rather than becoming a click handler attached to a non-interactive element.
Make the fallback meaningful
Turn off JavaScript during development and test the important path. The page doesn’t need to look identical, but its content and core actions should remain available. Preserve a sensible heading hierarchy, starting with the page’s main heading and avoiding heading levels chosen only for visual size.
This approach also improves maintainability. Developers can enhance a reliable document rather than reconstructing browser behaviour from scratch. DOM Studio’s headless primitives can then provide richer patterns where native controls don’t cover the requirement, while Vue bindings handle application state and composition.
4. Colour Contrast and Visual Accessibility
Colour choices affect text, icons, controls, focus indicators, and error states. Test the complete component state set, not only the default design token. A button that passes in its resting state may fail on hover, focus, disabled, or loading. A pale border can make an input boundary disappear, and a focus ring that works on white may vanish over a tinted card.
Use a contrast checker during design and inspect the rendered result in browser accessibility tools. Don’t communicate status through colour alone. An error should combine a clear message with an appropriate icon or other visual cue, while a selected tab or active toggle should have a state indicator that doesn’t depend only on hue. Charts need labels, patterns, or a text alternative when colour distinctions carry meaning.
Treat design tokens as constraints
A colour system should make the accessible choice the easy choice. Store foreground, background, border, focus, and status tokens together, then test their combinations across themes. Tailwind CSS 4 can support this approach through a constrained theme, but utility classes don’t prove that a particular pair has enough contrast in every context.
Disabled controls need particular scrutiny. Reduced opacity can make text and boundaries harder to perceive, while removing all contrast can hide an action’s status. Consider whether the control should be disabled at all. If it must be unavailable, communicate why and make sure adjacent help text remains readable.
For a deeper implementation concern, review guidance on high-contrast mode support.

A colour token is not accessible in isolation. It’s accessible only in the foreground, background, size, weight, and state where your component uses it.
5. Text Alternatives and Descriptive Labels
A text alternative should communicate purpose, not merely describe pixels. A product image linked to a detail page needs useful identifying text. A decorative flourish should use empty alternative text, alt="", so a screen reader can skip it. A complex chart may need a concise visual alternative plus a nearby data table or written summary.
Interactive labels need the same discipline. An icon-only close control should expose a name such as aria-label="Close dialog". A visible heading is often better than a duplicated ARIA label, so connect the control to that heading with aria-labelledby where the relationship is meaningful. Don’t rely on placeholder text as the only label because it disappears during input and often has weak contrast.
Links should make sense out of context. “View our pricing plans” tells a user what the destination contains, while “click here” forces them to reconstruct meaning from surrounding text. The same applies to buttons. “Save draft” describes the action more clearly than a generic “Continue” when the interface has several possible outcomes.

Write alternatives in context
The right wording depends on the task. “Blue wireless headphones” may identify a catalogue image, while “Headphones folded beside the charging case” may explain a product demonstration. Don’t add “image of” unless the surrounding context needs it, since assistive technology often announces the element type already.
Captions help people who can’t hear video, but a transcript also supports users who prefer reading, searching, or processing information away from the recording. Localise labels and alternative text with the rest of the interface. A technically correct English label in a Spanish flow still creates an inconsistent experience.
6. Responsive Design and Mobile Accessibility
Responsive accessibility is more than making a layout fit a narrow viewport. Components must remain operable when text grows, browser zoom changes, an on-screen keyboard appears, or a mobile screen reader changes the way focus and touch exploration work. A menu that looks fine in a desktop browser can place its close control beneath browser chrome or open beyond the usable screen on a phone.
Design touch targets with comfortable size and spacing, and test them on actual devices rather than relying only on a desktop emulator. Avoid hover-only disclosure. If a tooltip or submenu appears on hover, provide a focus and touch equivalent, and ensure the content remains available long enough to read and operate.
Test reflow and orientation
Use flexible layouts, rem or em sizing where appropriate, and CSS that allows text to expand without clipping. Test portrait and orientation, zoom, long labels, validation messages, and mobile keyboards. A combobox may need a different popup position when the keyboard reduces the visible area. A toast that appears above a desktop footer may need repositioning around a mobile navigation bar.
The UK Government’s monitoring programme examined 1,203 public-sector websites and 21 mobile apps between January 2022 and September 2024, identifying 29,787 accessibility issues. Monitoring fixed 16,482 issues, resolving 55.3% of the total, according to the UK accessibility monitoring report. The lesson is practical: systematic testing and remediation can expose problems that a desktop-only review misses, but teams still need to verify the behaviour users encounter on their own devices.
7. Form Accessibility and Error Handling
Forms fail users when labels, instructions, and errors exist visually but not structurally. Associate every label with an input through matching for and id values. Use fieldset and legend for radio groups and related checkboxes. Link helper text and errors with aria-describedby, and set aria-invalid="true" only when the field currently has an error.
An error message should explain what went wrong and how to fix it. “Invalid input” leaves the user guessing. “Enter an email address in the format name@example.com” gives a clear recovery path. Preserve the entered value when possible, move focus to the first invalid control after submission, and ensure the error summary also has an accessible name and useful navigation.
Validate without making typing hostile
Server-side validation remains necessary because client-side JavaScript can fail or be bypassed. Client-side checks can provide earlier feedback, but real-time validation shouldn’t interrupt every keystroke or announce transient states repeatedly. Validate at a sensible point, expose the result through the field’s description, and keep the message present until the user corrects the value.
Use appropriate input types such as email and tel to provide better mobile keyboards. Mark required fields programmatically and explain the convention in visible text. Don’t force users to infer required status from colour or an asterisk without an accessible explanation.
For teams handling sensitive workflows, accessibility should sit alongside security and privacy requirements. A HIPAA compliant form builder may help with the operational context, but developers still need to inspect the generated labels, descriptions, errors, focus order, and submission behaviour.
8. Screen Reader Testing and Assistive Technology Compatibility
A passing accessibility scan does not prove that a component communicates clearly. Screen readers can announce the wrong name, repeat content hidden from sighted users, miss a state update, or report focus in an unhelpful order. Test the spoken interface as a separate output, not as a side effect of visual inspection.
Use targeted workflows for each assistive technology. In NVDA, open Speech Viewer to capture the exact announcement as labels, expanded states, and live results change. In VoiceOver, use the rotor to review headings, landmarks, form controls, and links, then move through the page with VoiceOver navigation rather than relying only on Tab. TalkBack provides the equivalent mobile check on Android. Record wording, timing, repetition, and whether the announcement gives enough context to complete the task.
Build a component test script
Give each reusable component a short, repeatable script. For a combobox, type a query, listen for the result count or status, move through options, select one, and clear the value without losing the input. For a data table, verify the announced column and row context while moving between cells. For a loading region, confirm that the update is announced once and that focus does not jump unexpectedly.
DOM Studio’s screen reader compatibility guidance supports this layered workflow. Automate stable DOM contracts in Playwright or Cypress, such as accessible names, roles, states, and relationships. Reserve manual sessions for speech output, rotor or browse-mode navigation, timing, and task comprehension. Component-library stories can store these scripts beside the implementation, while pull requests can require the relevant automated checks.
For non-HTML deliverables such as exported documents, the PDF accessibility guide covers equivalent checks for structure, reading order, labels, and navigation.
Invite people with assistive technology experience into usability testing. A developer can verify that a node receives focus. A user can identify an announcement that is confusing, repetitive, or too late to support the task.
9. Focus Visibility and Visual Indicators
Keyboard focus must remain visible throughout the interface. CSS resets that include outline: none often remove the only reliable location cue, leaving users to guess which control will activate when they press Enter. Restore a clear focus treatment for every interactive element, including links, buttons, tabs, menu items, inputs, and custom controls.
Use :focus-visible when you want the strongest indicator for keyboard interaction, but don’t let the fallback disappear in browsers or contexts where the pseudo-class behaves differently. An outline with an offset can separate the indicator from the control’s border. A second visual treatment may help on complex backgrounds, but it must not obscure text or change the layout unexpectedly.
Separate focus from selection
Focus answers “where will the next action happen?” Selection answers “which option is active?” Hover answers “what is under the pointer?” These states may share colours, but they shouldn’t become indistinguishable. A selected tab can have a persistent active style, while the focused tab needs an additional outline that remains visible when the active tab changes.
Check contrast against every surface and state. The planned pattern calls for a visible focus indicator with at least 3:1 contrast, and a 2px outline is a practical baseline for visibility. Test at zoom, in forced-colour modes, over images, and inside cards with tinted backgrounds. Keep focus within the viewport when opening menus, drawers, and dialogs.
DOM Studio’s button and tab patterns can provide a consistent starting point, but the host application still controls surrounding backgrounds, stacking contexts, and custom theme tokens. A wrapper can expose a focusable primitive correctly, while a parent container clips the outline. Test the composed component, not only the isolated story.
10. Internationalisation and Multilingual Accessibility
Internationalisation affects both visual layout and assistive technology output. Set the correct lang attribute on the document and on regions that switch language so screen readers can select an appropriate pronunciation model. Localise visible labels, ARIA names, descriptions, error messages, and status announcements together. A translated interface with English-only aria-label values is only partially translated.
Support right-to-left layouts with dir="rtl" where required and prefer logical CSS properties such as margin-inline-start, padding-inline, and inset-inline. Don’t assume that mirroring every control is correct. Text direction changes layout, but interaction conventions and icon meaning may need separate review.
Allow content to grow
Translations can be longer or shorter than the source language. Build flexible controls that wrap or expand rather than clipping labels, and test headings, buttons, tabs, menus, form errors, and live announcements with realistic translated strings. Dates, numbers, currencies, and validation examples should use the user’s locale rather than a fixed format.
Screen reader testing should include the target language and voice where the audience depends on it. Ask localisation reviewers to test keyboard order, focus visibility, truncation, and reading order, not only spelling. The Translators USA translation guide can inform translation workflow decisions, but developers must still verify the rendered interface and its accessibility tree.
A component library should expose language and direction as deliberate inputs. Vue components need to react when locale changes, update labels and descriptions, and avoid caching an English name after the visible content has changed.
10-Point Accessibility Best Practices Comparison
| Item | 🔄 Complexity | ⚡ Resources | 📊 Expected outcomes | ⭐ Key advantages | 💡 Ideal use cases / tips |
|---|---|---|---|---|---|
| WAI-ARIA Roles, States, and Properties | Moderate → High: requires ARIA spec knowledge and correct usage | Moderate dev time; testing with assistive tech | High accuracy of semantics for complex widgets | Standards-based accessibility across screen readers | Use for custom widgets (menus, dialogs); prefer native HTML first; test with NVDA/VoiceOver |
| Keyboard Navigation and Focus Management | High: tabindex, trapping, and keyboard patterns are intricate | Moderate–High: dev/testing across states and components | Critical for keyboard-only users; prevents traps | Enables full keyboard control and predictable focus flow | For menus, comboboxes, dialogs; use tabindex=“-1”/“:focus-visible”, avoid positive tabindex |
| Semantic HTML and Progressive Enhancement | Low: follow native semantics and HTML5 patterns | Low: less JS, simpler testing | High baseline accessibility and SEO even without JS | Native keyboard/screen reader support; better performance | Start HTML-first; verify with JS disabled; use correct headings and landmarks |
| Color Contrast and Visual Accessibility | Low → Moderate: design trade-offs and theming work | Low: design tooling and contrast checks required | Broad readability improvement for low-vision users | Immediate, measurable impact on legibility and usability | Enforce WCAG contrast ratios; add non-color cues (icons/text); test interactive states |
| Text Alternatives and Descriptive Labels | Low → Moderate: writing good alt/labels needs context | Low: content effort and review, localization overhead | High for screen reader clarity and SEO benefits | Improves discoverability and fallback when media fails | Alt for meaningful images, aria-labels for icon-only buttons; keep alt concise (~<125 chars) |
| Responsive Design and Mobile Accessibility | Moderate: responsive layouts + touch considerations | Moderate–High: device testing and layout adjustments | Better mobile usability, fewer mis-taps and higher retention | Supports varied devices, touch accessibility, and performance | Ensure 44×44px touch targets, responsive typography, test on VoiceOver/TalkBack |
| Form Accessibility and Error Handling | Moderate → High: labels, aria-describedby, validation flows | Moderate: integration of client/server validation and UX | Higher completion rates and fewer user errors | Clear feedback and labeled inputs improve success rates | Use label/for, aria-describedby for errors, validate server-side, use fieldset/legend for groups |
| Screen Reader Testing & AT Compatibility | High: multi-OS/browser/screen reader variance | High: tooling, licenses (optional), and user testing time | Finds issues automated tools miss; validates real announcements | Real-world validation of behavior and announcements | Start with NVDA/VoiceOver, build SR checklists, record sessions for team review |
| Focus Visibility and Visual Indicators | Low: mainly CSS rules and design choices | Low: minor styling effort and cross-browser tweaks | Improves navigation confidence for keyboard users | Easy global application, low performance impact | Use :focus-visible, 2px outline with outline-offset, never remove focus styles |
| Internationalization & Multilingual Accessibility | High: RTL, locale formats, and translation management | High: localization workflows and multilingual testing | Enables correct UX across languages and regions | Expands accessibility to global audiences | Set lang attributes, use CSS logical properties, plan for text expansion and RTL testing |
Make Accessibility a Component Contract
Accessibility becomes repeatable when every component carries an explicit contract. The contract should describe its semantic output, accessible name, keyboard commands, focus entry and exit, state attributes, live announcements, responsive behaviour, and expected fallback. That documentation gives product, design, engineering, and QA teams the same reference instead of leaving behaviour implicit in a demo.
A release review should inspect the rendered DOM, not only the component API. Verify that Vue props update the underlying attributes, that v-model changes are announced when they should be, and that slots don’t remove a required label or heading. Dynamic updates deserve special attention. A wrapper may expose a polished interface while a consumer passes an unlabeled icon, replaces a visible heading, or renders a popup outside the relationship the primitive expects.
The UK’s public-sector accessibility regulations came into force in 2018, requiring public bodies to meet accessibility requirements and publish accessibility statements. In its first monitoring round, the Government Digital Service reviewed 593 accessibility statements, and only 39 were fully compliant, according to the UK Statistics Authority accessibility guidance. The early gap shows why policy, components, testing, and ownership need to work together. A statement or audit can document a position, but it doesn’t replace working interaction.
Leadership also has to fund the workflow. AbilityNet’s 2025 survey identified senior leadership understanding, support, and advocacy as the top need reported by digital professionals, while the UK government’s 2025 monitoring report recorded 60% of surveyed organisations adhering to WCAG 2.0 AA or higher, compared with 54% in 2024 (2025 inclusion monitoring findings). Progress is useful, but partial adoption still leaves teams dependent on individual effort. Put accessibility ownership in component governance, procurement, design review, engineering definition of done, and CI maintenance.
A practical release check
- [ ] Inspect semantic HTML and accessible names in the rendered DOM.
- [ ] Complete every interaction with a keyboard, including entry, escape, selection, and focus restoration.
- [ ] Verify state announcements with a screen reader on the supported browser and device combinations.
- [ ] Test focus, contrast, zoom, reflow, orientation, long content, and translated labels.
- [ ] Run automated accessibility checks in CI, then investigate every failure rather than suppressing it.
- [ ] Record the component contract beside its Vue props, slots, events, and usage examples.
DOM Studio is one practical way to start with headless primitives and Vue integration that provide established roles, focus management, and interaction behaviour. Its wrappers can reduce repeated implementation work, but teams still need to check page structure, content quality, design tokens, dynamic bindings, and real user flows. A component library should lower the cost of doing the right thing, not become a reason to skip verification.
The best accessibility best practices are therefore lifecycle decisions. Choose the simplest semantic element, add only the interaction model you can explain, test what assistive technology hears, and make the checks part of delivery. That process produces interfaces that remain usable after the first audit, the next redesign, and the next feature release.
DOM Studio provides headless web component primitives with a Vue integration layer for accessible menus, dialogs, listboxes, tabs, comboboxes, toasts, and other production interfaces. Visit DOM Studio to explore components that can help you encode keyboard, focus, and ARIA behaviour into your library workflow, then validate each implementation with your own automated and manual tests.
