Skip to main content

VoiceOver

VoiceOver (the built-in screen reader on macOS and iOS) relies on clear semantics, correct names/roles/values, and predictable focus and reading order. To work well in any framework (including Angular), start with semantic HTML and add ARIA only when needed. This aligns with WCAG 2.1/2.2.


Examples

Announcing status updates

  • Using an aria-live region (auto announce):
<div aria-live="polite" aria-atomic="true">Loading…</div>
  • Using Angular CDK LiveAnnouncer (manual announce):
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { Component } from '@angular/core';

@Component({ selector: 'app-form', template: `...` })
export class FormComponent {
constructor(private liveAnnouncer: LiveAnnouncer) {}

onSubmit() {
this.liveAnnouncer.announce('Form submitted successfully');
}
}

Focus management in a dialog

<!-- Example: Focus is trapped inside modal and returned to trigger on close -->
<div cdkTrapFocus role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm action</h2>
<button type="button">Confirm</button>
<button type="button">Cancel</button>
</div>

HTML elements

A lot of accessibility is already natively implemented in the different HTML elements. These elements will give the screen readers context about what the user has focus on. There are, for example, different kinds of <div> elements that will give screen readers context.

  • <nav> - Provides a section in the page that is used for navigation links.
  • <footer> - Represents a footer for the page. Typically contains information about the web page and copyright data.
  • <header> - Represents the page header usually used for navigation and logo's.
  • <search> - A block that contains a form or controls related to searching and filtering options.

To find more specific HTML types and ones that can improve the context a screen reader gets see this list of elements.

✅ Correct

<button type="button">Save</button>

❌ Incorrect

<div role="button" tabindex="0">Save</div>

Clear labels

<search>
<input type="search" aria-label="Search site">
</search>

Expose name, role, value on custom controls

<my-toggle role="switch" aria-checked="true"></my-toggle>

👍 Do's

  • Use semantic HTML first (<button>, <nav>, <header>, <main>)
  • Wrap 3rd‑party components to inject missing aria-* attributes
  • Manage focus: move focus into modals on open; return focus on close
  • Label everything appropriately (aria-label, aria-labelledby)
  • Use ARIA only when needed; avoid redundant roles
  • Test via the VoiceOver Rotor: headings, landmarks, and links should be logical
  • Ensure keyboard operation with Tab/Shift+Tab/Enter/Space/Arrow keys

👎 Don'ts

  • Create fake buttons with <div role="button"> unless truly necessary
  • Duplicate ARIA roles (e.g., <button role="button">)
  • Hide focusable elements with aria-hidden="true"
  • Override tab order with tabindex > 0 except for well‑justified cases
  • Omit visible focus styles on custom components
  • Rely on visual order that differs from DOM order

Extras

  • WCAG Alignment (Key Principles):

    • Perceivable: provide text alternatives (alt, aria-label, aria-labelledby)
    • Operable: fully keyboard accessible, no traps, visible focus
    • Understandable: clear semantics and predictable structure
    • Robust: standard HTML/ARIA compatible with assistive tech
  • Enabling & testing VoiceOver on macOS:

    • Cmd+F5 toggles VoiceOver
    • System Settings → Accessibility → VoiceOver for rotor/web settings
    • Prefer Safari for VoiceOver testing; cross‑check with NVDA/JAWS on Windows if possible
  • Reading and navigation order:

    • DOM order should match the visual and logical order; avoid confusing CSS re‑ordering
  • Headings and landmarks:

    • Use <h1><h6>, <main>, <nav>, <footer> to power Rotor navigation
  • Announcing dynamic changes:

    • Prefer polite aria-live or targeted announcements via LiveAnnouncer

Resources