Check out a free preview of the full Web App Accessibility (feat. React) course:
The "Live Regions Exercise" Lesson is part of the full, Web App Accessibility (feat. React) course featured in this preview video. Here's what you'd learn in this lesson:

Students are instructed to use Live Regions to implement accessibility features that announce updates to the shopping cart without moving the user’s keyboard focus.

Get Unlimited Access Now

Transcript from the "Live Regions Exercise" Lesson

[00:00:00]
>> Marcy Sutton Todd: So for this exercise we are going to announce when we add things to the cart. So when you click this button, Add to Cart, we're going to show a little badge up here in the cart. I don't want to move the focus up there because the person might be, maybe they want to add two things to the cart, maybe they wanna do something else.

[00:00:22] I guess that's a design consideration of should we move focus to the cart or not? In this case, I'm gonna say we're not. And instead, we are going to announce that there is a live region. Or using a live region we are going to announce that there's a certain number of products.

[00:00:45]
>> Marcy Sutton Todd: So for this one, let's code it together. Sound good? And then we'll take a break.
>> Marcy Sutton Todd: Okay, so for this, we are in the exercise 4. We have similar to the last exercise, the one we're viewing is the one that doesn't have this supported in it yet.

[00:01:08] So if I click this button, I don't see anything. I think my button might not even be a real button. But what I want to have happen is there'll be a little badge that shows up. So we need some state that is going to show that number and then we're gonna write an ARIA live region that will just announce when that text is appended.

[00:01:30] And we can always look at the more completed example. So I'm gonna come over here to our code. That stuff is all, I think for the most part in the product details. So our button, I was right, it is not a real button. That was something, so this is interesting.

[00:01:53] I don't think axe complained about this button or maybe the version of that page had real buttons in it. But these should be buttons, not divs. I don't think axe will complain at you if you have a click event on a div, because there are valid use cases for click events on divs, but it's also a big accessibility issue.

[00:02:12] So now this is a real button. It has a click event on it with a callback function called onAddToCart. So this product detail page, it knows which product we're looking at, so it's gonna pass its product kind of data object back up to the parent to say, hey this is the one that we're gonna pass up to the shopping cart.

[00:02:35] Cuz the shopping cart is outside of the scope of this, so we have to do a little bit of passing around of state to show that and there's definitely more than one way you could do this for this sort of prototyped product page that's how we're gonna to handle it.

[00:02:54] So on Add to Cart, let's go to trace the trail of where that is. So, if I go up to the top of the page, product details gets that callback function. So product details is added here in the product page. So that's where this onAddToCart callback function, then on the parent side, I can handle what's in the shopping cart.

[00:03:23]
>> Marcy Sutton Todd: So we have state tracking the shopping cart items. We have that callback function that will say, all right, go add this to our cart. And I don't think it's actually doing anything right now. So what I'm gonna do is make another function, we'll called onAddToCart. And I'm gonna update the state in that function.

[00:03:47]
>> Marcy Sutton Todd: Let's call it onAddToCart. It's gonna take in a product,
>> Marcy Sutton Todd: That is going to want a type. Man.
>> Speaker 2: You're missing the arrow function.
>> Marcy Sutton Todd: Yeah, thank you.
>> Marcy Sutton Todd: Okay, so in here we're gonna say updateShoppingCartItems. I am going to combine what was potentially in the shopping cart already.

[00:04:22] Like if I was on another product page or something, I have to, yeah, this is definitely not a data architecture course [LAUGH], so this is what we're doing right now, okay? I'm gonna say, we'll say Items, let's say shoppingCartItems and the new product. We're going to combine those using the spread operator and then we will update our shopping cart state with that combined array of items.

[00:04:50] So there's an array of objects here. So on Add to Cart now has updated those items to do that stuff. So the shoppingCartItems are actually already being passed in to our product header, we just weren't really doing anything to update it. So I'm gonna save that. Let's go over to the product header and our shopping cart icon, I'm going to maybe collapse our banner component.

[00:05:21] So here's our shopping cart.
>> Marcy Sutton Todd: I've got shoppingCartItems, so if I have shoppingCartItems and just to be extra careful, I'm gonna say if the length is more than 0, then I could output a little badge icon. So, I think I already have the CSS for that, and that will be our little circle with the number up there.

[00:05:58] So just to make sure that I have the CSS for that in here, we have a CSS class, I'm just gonna go and check. I always confuse these two. Okay, custom css. Okay, so we have a badge here, it's got a red background color, it's like a little circle with some text in it.

[00:06:18] So that's in there. I did bail out of Tailwind for this example because it was not working for me for some reason. So we're getting the job done with CSS classes. So we've got a little badge that will output if there's more than zero items. It's like that message notification that won't ever go away otherwise, so we only want to show it if there's actually something in it.

[00:06:42] So then we need to say, shoppingCartItems.length that will show us the number. And for our live region, we want to say like, hey, there's items in there. So we could put our live region right up here in the header. If you had live regions throughout your product or throughout your web app, you might wanna centralize them somehow.

[00:07:06] But for this kinda first iteration, I'm just gonna create it right here in the element, or in the header. So I'm gonna make this a div. I'm gonna say div role status, that will create our live region. I am going to make it visually hidden with the Tailwind sr-only class.

[00:07:28] And let's, we could just say ShoppingCartItems.length, like there are certain number of items in your cart. So, I could say, Let's see, I actually wanna abstract this into a state variable. So I'm gonna say. Let's see, regionAnnouncement, or cartAnnouncement, I think that's what I called it, cartAnnouncementMessage. So I'm going to go define that up here using React state.

[00:08:13] I could say cartAnnouncementMessage, setCartAnnouncementMessage, and I'll say = useState. That will be a string, and I'll start it off as an empty string. So the element will be there, but it won't have any content in it, so nothing will be read aloud. And then when we get shoppingCartItems I can do things, like manipulate what that text looks like.

[00:08:44] I can say, does it have item or items in it? I could have done that in line in the JSX, it just felt like a little too much logic to put down there in my markup. So I'm gonna do, I guess, pick your poison, right? I'm gonna do useEffect instead.

[00:08:59] So when we get that code CartAnnouncementMessage or sorry, the shoppingCartItems, I'm going to create a side effect of generating a live region announcement from it. So, in my dependency array, I'm gonna say shoppingCartItems, so this way I can safely manipulate. I can take that data, manipulate it into the string I want and I can call setCartAnnouncementMessage.

[00:09:27] It will then create this other state variable that I need to use. So let's say, so item or items, gosh naming things is always the hardest, right? So,
>> Marcy Sutton Todd: So if shoppingCartItems.length, if it equals 1, I could say, you have one item in your cart, [LAUGH] otherwise, it'll be items.

[00:09:55] If it's zero items, we aren't gonna see it. But if it's two or more, we'll see items. So then,
>> Marcy Sutton Todd: Get my ternary operator here. So I could say, setCartAnnouncementMessage, I'm going to make a template literal and say, There are shoppingCartItems.length,
>> Marcy Sutton Todd: And then are item or items in your cart.

[00:10:32] So that should be, there are the number of items in your cart. I could wrap this in logic if I really wanted to be extra safe by saying, if shoppingCartItems, probably don't need to do that. But if the length is greater than 0, then run this code. I don't think it'll matter.

[00:10:56] But if you wanna be extra type safe or something, that should give us a string. Let's go see what what happened. So, we will add to the cart, shopping cart items is not alterable. I saw this before. So I think my type script, any, there it is. So this actually should be product, that's a type that this file doesn't know about.

[00:11:27] So I'm gonna go import the type.
>> Marcy Sutton Todd: Product from, where are those types? Types,
>> Marcy Sutton Todd: Okay, seems to be cool with that. Okay, so now, it should be an inerrable. I thought it would be before, but it was mad, big mad. Still not, okay. So, for time purposes, yes.

[00:12:02]
>> Speaker 2: I was gonna say I think I know the problem like further upstream and the parent component, I think the state was initialized as null.
>> Marcy Sutton Todd: Okay. Yes, it was and it's an any here too. So product,
>> Marcy Sutton Todd: Yeah, it's an array of products and I can initialize it as an empty array.

[00:12:26] Thank you.
>> Marcy Sutton Todd: Save us a little bit of time. Now, it is mad. If shoppingCartItems,
>> Marcy Sutton Todd: And, it's interesting that it complained in the browser but not in line.
>> Marcy Sutton Todd: Okay, let's see if that worked.
>> Marcy Sutton Todd: No, okay, fun, it's only mad when I click that button. So I'm gonna open the full page in another tab.

[00:13:06]
>> Speaker 2: That one worked.
>> Marcy Sutton Todd: Okay, so we have the badge there. I'm not really sure what's going on, but we'll figure it out. Okay, so we have a badge. Woohoo, it worked. But what did that live region do? That's the bigger question because that's what we're really focused on, it kind of motivated the thought process of, I click here, something else is happening on the screen.

[00:13:29] So, we're going to fire up VoiceOver and see if it worked. So, function command F5.
>> Speaker 3: Announcements, banner, announcements, list two items, link, clear gifts for everyone on your list, shop our gift guide. Next slide.
>> Marcy Sutton Todd: We're gonna fix that carousel next.
>> Speaker 3: Button, banner.
>> Marcy Sutton Todd: Next exercise.

[00:13:45]
>> Speaker 3: You are currently on a button.
>> Marcy Sutton Todd: Actually-
>> Speaker 3: To click this button, press add to cart, button, main, two items in your shopping card. You are currently on a button to click this button, press control, option, space, three items in your shopping cart.
>> Marcy Sutton Todd: So see how it waited until everything else was done reading?

[00:14:02] Might be fine, I mean, that's really, you start to get into user research territory.
>> Marcy Sutton Todd: I think it's probably fine to just leave it that way. But if you're working on a design system or something, some of these patterns would really benefit from testing with users. And these are compound components, so some of these flows and patterns, we can make a best guess, but I just wanna say like, I'm bringing my own developer bias to this solution.

[00:14:36] So ask people, even if you can just, I don't know, pay somebody to consult for an hour to explain the thing they might even give you some ideas. But, so yeah, it worked though. We've got three items in our shopping cart and I'm still in my navigating, going through the page my focus is still on the button so that's how live regions work.

[00:15:00] They're really awesome. Let's see what it looks like- [INAUDIBLE]
>> Marcy Sutton Todd: If we make it assertive since we're talking about whether to interrupt or not, let's try that. So in this solution we have rule of status. If I say rule of alert that will interrupt.
>> Marcy Sutton Todd: I'm very curious what I messed up on that other page.

[00:15:26] So let's fire VoiceOver back up.
>> Speaker 3: Announcements, banner, announcements. List two items, link, clear gifts for everyone on your list, shop, add to cart, button, main, four items in your shopping cart. You are currently on a button, to click this button.
>> Marcy Sutton Todd: So, assertive might be better, I'm not sure.

[00:15:43] Did you hear the little [SOUND]? It makes a little interrupt noise. So I guess to get back to that question on whether you should play a noise, there already is a noise [LAUGH] for the assertive alerts.
>> Marcy Sutton Todd: Yeah, sort of might be okay. I guess you have to think about what other announcements could.

[00:15:59] Possibly be running, it could be okay, it could be annoying. I'm not really sure.
>> Speaker 3: Six items in your shopping cart.
>> Marcy Sutton Todd: But I think for this type of thing, I guess assertive when I think through the kind of user impact of it. You wanna make sure that the button worked, and if, I think assertive actually might, now that I'm just kind of talking out loud, I think that might be the right choice.

[00:16:26] Because the consequence is that if you don't immediately know that it worked, you might keep clicking. And then as a blind screen reader user, then how do you go remove it from your card? Then that's a whole other can of worms, then you have to go to another page, you have to do all the stuff, so I think assertive is my that's my final answer.

[00:16:47] Was there a question?
>> Speaker 2: Does it make sense to set roll alert for any kind of error notifications that could pop up on the site?
>> Marcy Sutton Todd: Potentially, so this is an area we wanna, definitely verify that. Anytime we're talking about live regions, I'm just like, hold the phone [LAUGH] Because we wanna be, careful with them.

[00:17:17] As I mentioned, they can be super annoying, like screen reader users, and many people will say that they're annoying. So I think for error validation, like form validation I do think it's appropriate. You can use ARIA invalid for actual form validation and send focused to the errors, like say in a form context, there's some form fields that you missed, or aren't filled in correctly or something.

[00:17:48] And with client-side validation, when you hit submit, you're doing the action of clicking a button. So it's appropriate to move focus to the first error message, I think. So, live region may or may not be that useful in that context. Where they are useful is where you are not going to move focus.

[00:18:06] If you did have some sort of an alert error banner at the top, I guess I just said the word alert, that does kind of sound like an RA live -region situation. I think where we wanna get careful Is so that it's not repeatedly announcing [LAUGH] the ten minutes ago, five minutes ago, error, error, error, sounds annoying because it would be.

[00:18:30] So we wanna be careful that we're not just making annoying announcements that we don't realize are pummeling our users. Coz if you don't have the screen reader running, you probably have no idea. So if it's re-rendering, like, automatically or something, that's what we wanna be careful about. I saw one technique for live regions that makes me think of, I think this was pre-React.

[00:18:55] But their solution to client-side routing and screen reader announcements, was to wrap the main content in a live region, so anytime that updated, it would just announce it. Seemed kind of innovative at the time, but now that I'm thinking about, like, I just worked on a page that had like a, some sort of a, use effect, run amok, and it was just like re-rendering.

[00:19:21] And the only way I knew was that the console was just like adding, adding, adding, adding the terminal was spinning. I think that page would be re-rendering, so if it had a live region on it would just be constantly announcing. So I mean, that's a like deeper bug than just the live region, but it might kind of illustrate, when it's re rendering the screen reader might pick it up.

[00:19:45] I don't know you have to test it, coz there are some other settings that we didn't actually talk about. If I go back to live regions there are, do I have it in here? Yes ARIA atomic and ARIA irrelevant, theoretically will change whether it will announce additions or removals like what kind of, how granular is the live region.

[00:20:09] It hasn't been super reliable cross-platform but hopefully with a live region if it literally is announcing the same or re-rendering the same thing over and over again. It shouldn't just announce it over and over but, again it's like this is an area that we do wanna test in a screen reader.

[00:20:29] So hopefully, you see some motivation to add that in your workflow if you have a use case for live regions. I do wanna see if I can fix our TypeScript error. So that one was in product header, what I'm gonna do is
>> Speaker 2: Someone said, I think it might have to do with the default value, maybe null.

[00:20:53]
>> Marcy Sutton Todd: Yes we changed that, I thought.
>> Speaker 2: Okay, sorry.
>> Marcy Sutton Todd: So shoppingCartItems, we have an array, array of products.
>> Marcy Sutton Todd: We have shoppingCart, wait, maybe I didn't update that page. I think I might have fixed the wrong one.
>> Marcy Sutton Todd: Maybe that was it, I swear I edited that in the right place.

[00:21:26]
>> Marcy Sutton Todd: I remember doing it, that's the problem of having multiple versions of the same thing. Well, it worked, but my styling is off.
>> Speaker 2: [LAUGH]
>> Marcy Sutton Todd: I'm gonna call that a win [LAUGH].
>> Speaker 2: [LAUGH]
>> Marcy Sutton Todd: So yes, demo inside of another React, the rest of the React site, so, some styling.

[00:21:47] But at least the bones are there, the functionality works. And the live region should announce there, so one thing I didn't show you is that you can inspect just like any other element with a live region. So I guess it would be up here in the header just to make sure it rendered, like sometimes we're using react where you only output an element if a certain condition is true.

[00:22:15] Sometimes you'll be like, well, why didn't that element render? It could be something with your state variables or something, so you can always go to the dev tools and see like, it's not announcing. Is that for some other reason or is it because my live region didn't actually end up in the rendered document?

[00:22:33] So it's there, if it doesn't announce, even though you see it in there, it might be for some other reason. Like maybe display none, or I'm not sure, maybe the maybe the messages aren't making it to the right element or something.