Web App Accessibility (feat. React)

Accessible HTML Solution: Header & Gallery

Marcy Sutton Todd

Marcy Sutton Todd

Principle Studios
Web App Accessibility (feat. React)

Check out a free preview of the full Web App Accessibility (feat. React) course

The "Accessible HTML Solution: Header & Gallery" 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:

Marcy continues the solution for the Accessible HTML exercise. The ProductHeader and ProductImageGallery components are addressed.


Transcript from the "Accessible HTML Solution: Header & Gallery" Lesson

>> I think what's left is the image alt text and these aria-hiddens. So this one, the goal of aria-hidden is to hide something from the accessibility tree. So the technique on this component that's auto-rotating, Is that there's three slides here. And when you go through the carousel, only one is active at a time.

So the technique that was used was to put aria-hidden on the ones that are off screen. But the problem is there's links in there, and so you can tab on the links, but they aren't gonna have any accessibility information. So that's why Axe is complaining about it. So we're going to use inert instead, which is an HTML attribute that's been proposed.

It's totally awesome, and it will solve this problem. It adds aria-hidden, and it will go into any links, or buttons, or anything in that region, and it'll disable them with tabindex for us. So let's do that. Okay, so instead of aria-hidden, we can even just leave that there right now, I'm gonna say ref, cuz we're in React.

So I'm gonna pass it a node, and within here, I might need to cheat. This is kind of hard to remember. So React currently has an issue with inert that you have to work around. So we're gonna work around it. I'm gonna go up here, we're gonna make a function called set inert, and we're gonna call it for each one of these nodes.

And when one of them is current, we'll turn the inert attribute off. When that slide is active, we'll remove the attribute. It's a Boolean attribute, either it's there or it's not. Okay, so I'm gonna say setInert. Thank you, autocomplete. This is our first call of this. It's the first slide, so I'm gonna pass in the node from the ref API, and I'm gonna just pass it a number and the function will say, all right, if you match this index, set this value.

And we have some state in React that's always keeping track of which slide is current. So we can piggyback on that. Let's do the next one. It is ref=(node) setInert, keeps trying to autocomplete for something that I did not [LAUGH] tell it. So it's going to complain until we go and write this function.

I only had three of these, so I didn't make it too dynamic, I just hard-coded these in here, setInert, slide 3. So our function that we are going to use is called setInert. It takes the node from the React ref, and this is a way to get React to spit out HTML that it doesn't know about, cuz it's valid HTML but React is not supporting it yet.

So it looks for the slide number, which we are tracking with React state. So currentSlideNum, anytime you click those buttons or if it's animating, it will increment that, and then reset back to the beginning. That's how it spins around. And what it does is it moves that element using translate CSS, so it will translate its x position depending on what slide number.

So we're gonna set the inactive ones. We'll set the attribute inert. Just takes an empty string to get it to show up as value. And then when that slide is active, we remove the attribute. So let's go save this and go see how it looked. So I'm gonna inspect, So see if this, I think it worked.

So aria-hidden, I need to take off, actually, one more thing. I am going to delete those, cuz it looked like inert was working, but these old attributes were not, and inert will cover that, so we don't need both. Okay, so here we go. We have our first list item, that's the one that's visible.

And if I scroll, you can see the other two, they are physically off to the side. So if I click this button and it slides the next one in, now this one has already a hidden of false and a inert's gone. And then as we go through again, that will change us.

So what that's doing for us is handling any links that were inside of that. So we don't we just don't have to do anything. We don't have to go step through the DOM and put change tabindex on things, it's a huge pain. But we're keeping screen readers from tabbing on elements that there would be ghost links.

So kind of a hairy problem that inert makes really easy for us. One thing I will say is that I'm using a polyfill so that inert, if we're not in Chrome or it's supported, if say, you're in, I don't know, Edge or I guess that's a chromium, but any browser that doesn't support inert yet, this will still work, because there's a polyphil.

So let's go run Axe again, see if we made it happier. Is there anything we missed? The alt text. And it is still mad, there's some different elements that had aria-hidden on them. So this is a different element, it's these buttons. So those ones should not have aria-hidden on them at all.

This is not the same fix for these. So these ones, for some reason, had aria-hidden of true on them, but they're still focusable, because they should be usable, right? We can tab onto them. We should be able to use a screen reader with them, and aria-hidden is stripping away information that we actually need.

So we can go remove that in our alt text. So those buttons were in the right component. Here's that aria-hidden, let's get that out of there. There's one more down here, get that out of there. And those were real examples, too. I borrowed inspiration from [LAUGH] a real website, so some of these are made up.

Some of them are real, I just found in the wild. So our code bases can be doing all kinds of things that we may or may not have intended. But if we test it and then we can go and fix stuff, we can make it better. So we've got that one, I think our image alt text will be the last bit.

So this is somewhat dynamic. The goal is to make a component that you can just feed it different sets of data. It'll be, voila, I'll show you a gallery, but it's It's kind of half baked, I will admit. So we do have the capability of pulling in alt text.

So let's look at what our dataset looks like. We have an image that we're mapping over a series of gallery images. So these images come from some image data that gets passed in. I'm using TypeScript, so I can actually go and see some of the structure of this data object.

I'm a total TypeScript convert. I'll admit it, it's awesome. [LAUGH] I've held out for so long, I did not love it. But for stuff like this, you can go down the path and see, does my data already have alt text in it? It might or it might not, and you can have a conversation about where's that data gonna come from, question?

>> Why did you have to use the ref attribute?
>> Great question, for the inert. So let me show you, kind of glossed over the fix for that. We'll come back to the alt text. So that was in the header, I'll show you why. So inert, It's this right here.

Property inert does not exist on the HTML. So this is a TypeScript error, basically saying that React and TypeScript don't think that's a real attribute, even though it is. So like any developer, I went looking around to go and see, can I change my TypeScript config? I tried all of it.

There's supposedly a work around for this by defining a Typescript, creating a definition for this so React knows about it. I couldn't get it to work. So I think when you want to have an end result of your markup being crafted in a certain way, my own personal view is, do it by any [LAUGH] means necessary.

And I had to use the ref API to do it. It's not quite as elegant, because I'm having to go and call Dom APIs. So this node.setAttribute, that's a bit of a smell. That's not the ideal way to do it. The React way and why I love React is that normally, I would have just swapped out inert for aria-hidden with our original code and it would have just been one and done, easy, but it just didn't work.

So if you wanna learn more, here is the discussion in React not supporting inert. People are a little cranky about it, understandably so. I don't know what's gonna happen with it. My hope is that they'll just fix this and then we can use inert, maybe with a polyfill still, but not have to do all this hacky ref node stuff.

That is a really great question, though, I'm glad you asked it. So keep an eye on this one, I've got it in the codebase. So I'll make sure it's in the slides too, cuz it's something we'll have to keep checking on, cuz this is so helpful. For modals for, I mean, even something as small as this little announcements carousel.

It can be a very helpful tool that we should be able to use cuz it's valid. It's in the HTML standard. So yeah, that's why, question?
>> Speaking on type scripts, how useful do you think it is for catching some basic accessibility issues? Because I know for the example you were showing a little bit earlier, we defined the property using the wrong name, that feels like something that tested wouldn't be able to catch but I don't know from your experience how reliable that is.

>> It usually can, so it sort of makes me wonder about the tool set of how the site was the original. [LAUGH] The site that I was inspired by. You can kind of learn, I don't know, some interesting things about codebase just from playing with it a lot, and so I do think that some tools would be able to catch it.

Probably the ESLint plugin for JSX, I bet that would have caught it. Cuz yeah, a lot of these things, it is surprising to see some of them, because we have a lot of tools that we'll point it out in development cuz that would be ideal. Yeah, and things like the, I mean, this inert example.

That is potentially saving us from something really tricky. I just happen to know that it shouldn't be failing. So that's where it gets a little tricky is like, is that valid? Can I use that? You have to dig in a little bit more. And if you read that issue or the pull requests on React, there's some grumpy developers and they're going like, it's 2023 and we should be able to use this.

You can kinda get the gist that it should be something that should be in there. But something like aria-roll, that's never gonna be valid. So I think the tooling can potentially save us from stuff, but then here's the trade off, is that it's a little bit too strict sometimes.

But yeah, so inert, and if you need to do things that React and TypeScript aren't very pleased about, the ref API, you can do some trickery here. useEffect and the cleanup function that comes with it can also be little back doors to try and use some DOM APIs.

I've done that a lot when you click outside to close something. You have a component that you wanna be able to click outside of it and have it collapse things, you can use useEffect and document.addEventListener to mount and unmount some events in the component. So we can kind of get little escape hatches in React, but you definitely wanna use caution so that you're outputting valid markup, and you're not creating any memory leaks, cuz I think that's some of the risks with some of this stuff.

Cool, image alt text, that's our last bit. So we were talking about TypeScript, trying to figure out what data we have available to us. So our image data, we are using the galleryImages. So I wanna go deeper into where's that type? So I could go into my ImageData, galleryImages comes from image and it does have an alt in this type.

And so we happen to be looking at TypeScript. I think when you're working with teams, you could ask a person that [LAUGH] you're working with. The code might not always tell you or maybe typescript is just too much. I totally understand that. Wherever you have a source of information of where the image asset's coming from, you could ask about alt text kind of in that same realm.

You sometimes just have to make it up, write the alt text. That's not the best, but better than none. But for this, we should get all text. So our data is coming from, A file. So here's where this comes from. It is strongly typed, which is really nice, I've found, but our galleryImages do not have alt text.

So if this was coming from a backend API or something, I probably wouldn't be able to edit that. There is alt text for the top image, the main image, so we could pull that in. And every image under that, I guess that would be a conversation to be like, hey, where's the rest of the alt text gonna come from?

And I'll tell you what the actual implementation had. It had that top level alt text for every single image. So they wrote it in a way that was generic enough that, I guess, they thought it could apply to every single image. It's not very informative. If you're blind and you're trying to learn about a product, you kinda wanna know what capabilities it has, how are they showing it?

So that's not a very accessible solution, but this main image, at least. So this is a Chakra image component. The ones down below in the gallery are just plain HTML elements. They have pretty seamless APIs for the props. So this imageSource, not all caps, I can say alt, and then I could pass it, imageData.mainImage.alt and it has it, so I can bring that in.

That's only gonna solve it for this top one. So I think what I was saying the real site did was they just used the same alt text. So if you needed to do something, you could do that in a pinch while you also go have a conversation with your team about where's the real alt text is [LAUGH] gonna come from?

Hopefully, you can get a better answer. But this is kinda an example of how some things can't be caught with automated tools. We caught that there wasn't an alt text present, but it would be fine with this. Let's go see what this looks like, actually. We'll go over to the browser, I'm gonna rerun scan.

We fixed everything. It's like, all right, cool, except we use the alt text for one image for every image. So that's not that great. Let's see what the images are. So there's a detail closeup of the colors. There's a handle on top, you can see this cute dog goes on a stand up paddleboard.

I mean, there's so much detail missing, right? So it kinda illustrates that we can only fix so much with code. There's other conversations that we have to have and it has to be planned for. We're code technicians, we can do some things to push pixels around and add labels, but it needs to be a requirement, too.

So just know that it's not all on you. We need our teams to support us in getting the right content into the right place.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now