Accessibility Challenges
with Single Page Applications
Natalie MacLees
COO and Cofounder
What is a SPA?
Single-Page Application (SPA)
…a web app implementation that loads only a single web document, and then updates the body content of that single document via JavaScript APIs…
Why would you build a SPA?
Accessibility Challenges with SPAs
1.
“Page” changes
2.
Focus management
3.
Content changes
4.
Lots of custom components
Managing “Page” changes
Challenge 1
Implement all the usual things
Update history
React Router
Angular Router
Vue Router
Update the document title
Update document.title when component is mounted
Built-in Title service in @angular/platform-browser
<Route path="/about" render={props=> (
<Page {...props} component={About} title="About Us" />
)}
/>
Use render property�of Route component
{
path: "/about",
component: About,
meta: { title: "About Us" }
}
Add meta to routes
router.beforeEach((to, from, next) => {
document.title =
to.meta.title || "Default Title";
next();
});
Update title on beforeEach
Announce the new page title
ARIA live region
<div role="region" aria-label="Page Title" id="pageTitle" aria-live="polite">About Us</div>
ARIA live region
Focus wrapper
<body tabindex="-1">
<div id="app" tabindex="-1">
{/* Application code goes here */}
</div>
</body>>
ARIA live region
Focus wrapper
Focus first focusable element
ARIA live region
Focus wrapper
Focus first focusable element
Focus heading
<body>
<div id="app">
<h1 tabindex="-1">About Us</h1>
</div>
</body>
Focus Management
Challenge 2
Handle modals correctly
Button
Button
ESC
Button
Handle form errors correctly
<label for="email">Email Address</label>
<input type="email" id="email" placeholder="Enter your email" required aria-describedby="email-error">
<p id="email-error">Your email address is required to log in</p>
Content Changes
Challenge 3
Notify users of new content
Confirmation
<div id="list-status" aria-live="assertive">Licorice was deleted</div>
Confirmation
Expected update
Confirmation
Expected update
Information
<div id="report-status" aria-live="polite"></div>
Confirmation
Expected update
Information
Unexpected & critical
Custom Components
Challenge 4
POSH
Plain Old Semantic HTML
HTML tags have meaning
Learn how to correctly use
Use ARIA landmarks
<!-- Banner landmark -->
<header role="banner">
<h1>Welcome to My Simple App</h1>
</header>
<!-- Main landmark -->
<main role="main">
...
</main>
<!-- Navigation landmark -->
<nav role="navigation">
<ul>
...
</ul>
</nav>
<!-- Contentinfo landmark -->
<footer role="contentinfo">
<p>© 2024 Simple App. All rights reserved.</p>
</footer>
Wrap form elements in a <form>
<template>
<form @submit.prevent="submitForm">
<label for="name">Name</label>
<input type="text" id="name" v-model="formData.name">
<button type="submit">Submit</button>
</form>
</template>
Use <fieldsets> and <legends>
<p>Select your preferred hobbies</p>
<input type="checkbox" id="hobby1" name="hobbies" value="reading">
<label for="hobby1">Reading</label>
...
<fieldset>
<legend>Select your preferred hobbies</legend>
<input type="checkbox" id="hobby1" name="hobbies" value="reading">
<label for="hobby1">Reading</label>
...
</fieldset>
Use <label> for all form fields
<form>
<input type="text" placeholder="Full Name" required>
<select required>
<option value="" disabled selected>Select your country</option>
...
</select>
<button type="submit">Submit</button>
</form>
<!-- Text Input -->
<label for="name">Full Name</label>
<input type="text" id="name" name="name" placeholder="Enter your name" required>
<!-- Dropdown/Select Input -->
<label for="country">Country</label>
<select id="country" name="country" required>
<option value="">Select your country</option>
...
</select>
Use <button> and <a> correctly
Update the current page
<a href="/about" aria-current="page">About Us</a>
Avoid interactive <div> and <span>
<div v-on:click="doThis">
<div onClick={doThis}>
The problems with interactive <div>s
Can we fix those?
…or you could just
<button v-on:click="doThis">
<button onClick={doThis}>
and get your accessibility for FREE
A few last reminders
Freebies
Test everything without a mouse
Ensure focus is visible
Alt text for non-text content
Use skip links
Test, test, and then test some more
Thank you!
Natalie MacLees
natalie.maclees@nsquared.io
@nataliemac