Skip to content

Commit a39c066

Browse files
committed
Add a single backdated blog post
1 parent 345355b commit a39c066

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
layout: post
3+
title: 'Common Accessibility Pitfalls for Single Page Apps, and How to Avoid Them'
4+
author: gwtrev
5+
tags:
6+
- react
7+
- accessibility
8+
- frontend
9+
team: web
10+
---
11+
12+
[//]: # TODO This snippet needs links which reference other blog posts
13+
14+
> *Preface: Although unrelated to this article, folks at Scribd have already
15+
> shared some wonderful information about accessibility, varying from the
16+
> anatomy of an accessible ebook reader to small code snippets that can vastly
17+
> improve the accessibility of your website with pretty minimal effort. Check
18+
> those out if you’re interested in learning more!*
19+
20+
If you’re a front-end developer in today’s world, you’ve probably used the
21+
various JavaScript tooling available (React, Vue, Webpack, etc) to build a
22+
single page application (SPA). SPAs are easy, fast, and most importantly,
23+
provide a concise way to manage your front-end assets. What’s more, you can
24+
handle your page changing using client-side tools like `react-router-dom`! What
25+
more could you ask for?
26+
27+
Alongside the myriad benefits that come along with SPAs comes one of the most
28+
crucial parts of your site taking the biggest hit: accessibility.
29+
30+
Features that came out-of-the-box with more traditional tech stacks and server
31+
based routing now require special handling, since all or most of the routing
32+
happens client side. Two big issues that are easy to overlook when working with
33+
client-side routing of SPAs include:
34+
35+
1. Managing the focus of each page’s main header (h1 tag); and
36+
1. Providing the correct page title and description between pages (and in general, meta information)
37+
38+
Let’s take a look at some easy ways to handle these issues with React.
39+
40+
41+
## Managing the page header
42+
43+
Let’s paint a picture. When a user with assistive software (in particular, a
44+
screen reader) has arrived at your website, they interact with it like any
45+
other site at first: navigating links and listening to what is available on the
46+
page — including links to other pages.
47+
48+
Then the user selects a link to a different “page” on the site and… uh-oh.
49+
nothing happens! To them, anyway.
50+
51+
To sighted users, clearly the content has changed. But to a user without the
52+
ability to see the content, and without any handling of how that content has
53+
changed, they’re left confused.
54+
55+
What’s worse: suppose the anchor link that navigated to this new page isn’t on
56+
the page anymore. Now focus has moved back to the page <body>. The frustration
57+
grows!
58+
59+
This isn’t an uncommon issue with SPAs. There are fewer page refreshes when you
60+
handle routing purely in the client, and thus fewer changes in context unless
61+
you account for them explicitly.
62+
63+
It’s a jarring user experience, to say the least.
64+
65+
Here’s a simple React component that handles these frustrations for us. Notes
66+
afterward.
67+
68+
What is this little component doing? The important part is what happens with
69+
its state. Here’s a quick overview:
70+
71+
1. When the component mounts, it does a check against a prop called
72+
`lastLocation`. This is provided by a higher order component we’re using called
73+
`withLastLocation`, from the npm package
74+
[react-router-last-location](https://www.npmjs.com/package/react-router-last-location).
75+
The package borrows state from your React Router wrapper to tell us the last
76+
location that was visited by the user.
77+
78+
1. If `lastLocation` is null, nothing happens and the component renders without
79+
side effects. This is the behavior expected from first page load.
80+
81+
1. If `lastLocation` provides an object, we can infer the current page the user
82+
is viewing is not the first page they’ve seen since opening the website. We
83+
then set the `tabIndex` of the header to -1 and focus the header to set the
84+
context for the user. Once the header loses focus, we reset the `tabIndex` back
85+
to `null`.
86+
87+
And that’s it! You can drop this component anywhere — just remember:
88+
89+
1. [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) (Web Content
90+
Accessibility Guidelines) recommends you have only one h1 on any given page.
91+
If you include this component twice, whichever component renders last will
92+
command focus, so best to avoid that altogether by only rendering it once.
93+
1. You’ll need `react-router-dom` (4.x.x and greater) for the above
94+
implementation to work, as the package `react-router-last-location` requires it
95+
as a [peerDependency](https://yarnpkg.com/lang/en/docs/dependency-types/).
96+
97+
## Managing the site title and meta description
98+
99+
Similar to the page header, your site title and meta description should update
100+
across pages if the context of the page has sufficiently changed. Assistive
101+
technologies will need to know that the page is different at the highest level
102+
so screen readers can announce it. The perk of this step will be more than
103+
accessibility, though; it’s also great for SEO (when combined with server side
104+
rendering).
105+
106+
For this example, we’ll do another simple React component using
107+
[react-helmet](https://www.npmjs.com/package/react-helmet) to
108+
modify the content of the document head. Again, notes are after.
109+
110+
The component is pretty straightforward, and can be used similar to the
111+
`AccessibleHeader` component above. Drop it anywhere on the page, with `title` and `description`
112+
props defined, and `react-helmet` replaces the existing definitions
113+
in your document head. Similar to your h1, you should only have one of each
114+
title and meta description on each page.
115+
116+
Let’s also look at that `titleTemplate` prop that React Helmet accepts — this is
117+
exactly what it sounds like. It’s a base template that takes a placeholder
118+
option, `%s`, that is added in addition to whatever your new title text happens
119+
to be. If your title was `Shopping Cart`, for example, the end result would be `My
120+
Site | Shopping Cart`. Easy!
121+
122+
As an aside, be aware of titles and descriptions you’ve added to your base
123+
`index.html` (assuming you’re using `html-webpack-plugin`). Sometimes React Helmet
124+
will duplicate one or both of these tags, which isn’t desirable.
125+
126+
---
127+
128+
I hope this was helpful! As discussed, it’s vital to ensure your SPA has proper
129+
handling of context between pages to provide the most valuable information
130+
possible to users visiting with assistive technologies. You’re also improving
131+
the SEO and general user experience of your app by including these changes, so
132+
it’s a win-win all around!
133+
134+
**If you want to work on changing how the world reads, come join us! Learn more
135+
at [scribd.com/careers](https://www.scribd.com/careers)!***

0 commit comments

Comments
 (0)