Building an accessible dialog using React Aria

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone devin here uh just wanted to record a quick demo to show how you could take an existing component that you might already have and make it more accessible using react aria so here i'm i'm i've got a basic setup already um so you can see i've got some i'm using tailwind css um for the styling in this case and i've got some components that i've already made um so when i when i click this button you can see i've got a dialog here with kind of a sign up form and and some buttons and stuff so i'm just a basic basic dialog and but what you'll notice is there's a few a few issues here um with accessibility that i'd like to point out and then we can try to fix so first off um you can uh notice that if i if i open this dialog you can see the focus ring is actually still back there on this button so focus isn't really moving into the into the dialog properly uh when it opens um and so if i and if i tab uh tab forward you know i'll get i'll get there but it should it should move into the dialog as soon as it opens automatically um now the other issue here is that the focus doesn't um isn't contained within the dialog so if i tab forward here i actually ended up down here out of the dialog or you can see if i tab backward um actually get back to this button that's kind of outside the dialog here as well so um it's generally recommended that dialogues contain focus within them so that the user can more easily navigate around without kind of going to things that are kind of hidden or outside the dialogue so those are the first couple issues that we need to solve so here i show you um basically the setup we've got here is like we've got an application um i'm like this dialog trigger component that's basically this button has this label for the for the button there and then you pass a dialog component as a child and that's what shows up in here so i've made this dialog component it's got like a title and a label for the sign up button down here and then just some contents a couple text fields and a checkbox so take a look at that dialog component um so a couple things we've got here um a an outer div which is kind of this fixed position wrapper thing that's taking over the whole page here um we've got this is a backdrop so this is that um this kind of grayish back background color that's kind of covering the whole page um and then this is our actual dialog here um with this rectangle um and then we've got our we got our title which is just an h3 um and we pass in our children which is this whole area down here and then at the bottom we have a couple buttons um and when these are pressed it just triggers this on close prop um on the dialogues props and that ends up closing the dialog so the state for that is actually managed by the dialog trigger which renders this button out here and then it has like just a piece of regular react state here to keep track of whether the dialogue is open or not when you press the button sets it to open um when it's open it um takes the the children uh clones those so that it can add uh this is open prop and an on close prop and and that that's how the the button here can um can um pass to can close the dialog from within it and that just calls set open to set to false um and this dialog is also rendered into a portal which puts it at the end of the document body so if i were to inspect this you would see this dialog here is here at the end of the document body um and then my actual app is inside this root element back here so we're getting a nice portal there which will make sure that we're outside of any overflow behavior that's going on all right so let's see if we can fix a couple of these problems because i mentioned before uh the focus doesn't go into the dialog and then it doesn't it's not really contained inside there either which would be a good feature for accessibility so um the way we're going to do this is using some components and hooks from react aria which is a nice library of of of hooks to make a building accessible components more easy so first what we're going to need is the focus scope component so i'm going to import that focus scope from react aria slash focus um and focus scope um essentially you wrap some some children within it and it will make sure that focus is either contained within there or and that's kind of the main feature but it can do a couple other things as well so let's do that first so we'll say focus scope um contain and we'll just wrap our dialog element in that so i'll save that and now uh when i open this dialogue up you can see once i've put focus in here when i tab through when i tab to the end goes back to the beginning and if i shift tab it'll go back where it's so now we've got focus containment in our dialog with just one line of code but i mentioned the other thing was we wanted focus to move into the dialog when it first opens and actually we'd like this first input really to receive focus so we can do that by adding the auto focus prop to the focus scope as well and then and then when the when the dialog opens it'll auto focus the first focusable thing within it so now if i click on the on the button again you can see focus immediately moved to the first input within it so um already doing pretty well here improving some accessibility pretty easily okay so um one other thing that's important with dialogs is for keyboard users to be able to close them easily and usually this is done using the escape key um so right now i'm pressing the escape and nothing's happening so we need to wire this up now i could do this manually it could add a keyboard listener and all that stuff but um react aria has a hook for this that will also help us with another feature which is kind of nice which is when you want to click outside on this backdrop be nice if the dialogue closed as well so to do that we're going to use the use overlay hook use overlay from react aria overlays okay um and so what we need to do and the general pattern with these react aria hooks is you pass it some props um and it and maybe a ref and it gives you back some other props that you can apply to your specific dom elements so i'm going to say let's see let overlay props equal use overlay press the props in and i'm going to need a ref uh let's see so breath equals use ref and i need to import that and i'm going to apply the ref here to um to our dialog element so that use overlay has access to it and you can see here i've got i've called use overlay this says it provides the behavior for overlays and pop-overs menus those type of things um and handles the escape key um so perfect so all i need to do now is take this props that it's returned um and apply this as well to our dialog element so if i just spread that spread that on there and i save all this now when i open up the dialog again and i press escape dialog closes and if i click click outside dialog also closes super nice okay now one other thing that would be good was is when i when i hit escape or when i close the dialogue in any way like focus doesn't really go anywhere like now i don't know where focus went it kind of went like to the page i think you can see kind of in firefox shows you the little outline there um but it'd be nice uh and generally what's recommended is for um when you close the dialog for focus to be restored back to whatever opens the dialog in this case this button right here um and luckily focus scope makes this quite easy as well so we can just put the restore focus prop on focus scope here and it'll take care of that so now if i open this back up and i hit escape again you can see focus was restored back to this button right here in the and the focus ring showed up so great another another feature down okay so let's take a look at some accessibility properties here and we we need to use firefox has this built-in accessibility inspector that's really good so we can right click on the dialog here and and select inspect accessibility properties so you can see um this is showing the accessibility tree and you can kind of ignore the outer ones because there's this iframe here but for this frame you can see the accessibility tree um so let's see we've got we've got things inside the dialog here we've got um we've also got this section out here uh oh and that's our button behind the dialog okay okay well the other problem here is that like there's no nothing indicating to like a screen reader or anything that's going to use the accessibility tree here like uh that this is a dialogue at all um so we kind of need to we need to fix that problem first so these semantics are really important and providing semantic elements is really important because it helps screen readers and other assistive technology make sense of this kind of div soup that we've created here yeah you see we've got all these divs but like they don't really have any meaning associated with them so it's really hard for a screen reader to kind of like know what's going on here so um we need to apply some aria attributes and arya is the accessible rich internet application spec and it has it provides a bunch of attributes to html elements to provide them with semantics um so react aria implements a lot of this um and and will help us help us out here so uh let's close that for a second and i'm gonna um i'm gonna import um use dialog from react aria dialog all right and then i need to kind of do the same thing as i did with overlays here um for the dialog so let's see dialog props equals use dialog and i need to pass it the props and the ref again all right and then i just kind of also spread those props onto the dialog element okay so i'll open up the dialogue again and maybe inspect the preference expect this again oh check this out now we have a dialogue uh so now it knows uh the accessibility tree is showing that uh it knows that this is a dialog so how does it know that well let's just inspect element here and see what's going on so you can see it's added a role equals dialog attribute here and also a tab index to make sure that this dialog is actually focusable and and this role here is what's really giving it that semantics so in the accessibility tree you can see that it knows it's a dialogue but we do have one issue showing up here which is that we need a label so when it reads the dialogue it needs to know like a name for it so that it knows um what to say when it's reading the dialogue so it says dialogue should be labeled luckily we have an element already that's the title here um in the dom but we just need to associate it with that dialog so it knows that's the name of the dialog luckily use dialog makes this pretty easy and it actually returns a title props um as well and we just need to take this title props here and assign and assign this to our um our h3 here to associate the title with the dialog all right so title props actually okay so now if i open up this dialog again you can see we've got uh dialog and it's labeled now as sign up um so how is it doing that um if we inspect element again uh you'll see that the dialog here has an aria labeled by attribute it's got this long random id string here but you can see that this id matches the id attribute that is placed automatically on the h3 for us so just by just by taking this title props here and applying it to the h3 we automatically get this nice aria association between the dialog and the title so we've already improved our accessibility quite a bit just by applying a couple lines of code okay so one other problem here that we notice when we look at the accessibility tree is that this this button that's outside the dialog is actually still visible uh even while the dialogue's open even and and that's kind of going to be frustrating for a screen reader user because they're going to be navigating around with kind of a virtual cursor as they move around and and they're going to navigate to this which is outside the dialog but they don't really know they're not really going to know that it's outside the dialog and that it shouldn't really be accessed and you know what cited users can't uh can't interact with that while the dialog's open so so neither should screen reader users right so what we need to do is actually make this dialog modal um and and modal a modal means that the user can't interact with anything outside that that dialog while it's open um so uh there's a couple steps to do this um first of all we need to import uh use uh modal from react aria slash overlays i think i guess i could put it up here as well combine the imports okay so to do this i just gonna do the same pattern again modal props equals use modal and actually this doesn't take any arguments in this case um and then we just need to apply this as well to our dialog element now at this point it's not actually going to do anything because there's a couple other things that we need to set up so um in order to implement this what we actually have to do is apply this aria hidden attribute to everything outside of the dialog um and this is kind of backwards from the way that react components typically work you know and in react typically you you're only affecting things within your your component but in this case we actually have to affect stuff outside of our dialogue um in order to hide it um and so this is implemented by react aria using a context um and so when used modal is called it actually tells the parent providers on the page um hey i've got a modal open now and and then that will take care of hiding everything else so we need to we need to import a couple other components here to handle that so so first off we actually need to replace this this portal down here with uh with an implementation of the same thing in react-arya that also handles this modal aspect and so that's the overlay container component i'm sorry that's actually from the overlays package and so we'll just we'll just replace the portal here with this overlay container and that's what's going to communicate with the the outer uh outer providers to indicate that it's within a modal so we got that and now what we need to do is wrap our application actually inside of um a overlay provider so i need to import overlay provider from react res overlays and then i just need to wrap wrap this application in that and save this and so now when a modal opens uh this used modal is going to indicate to uh to to the overlay provider that hey there's a modal open now um and then this overlay container is going to keep track of where the boundary is between um between the modal and the rest of our app so with this we should be able to open our open our our dialog again and inspect accessibility properties and now you see that section that we had that's outside the dialog is empty now so uh so that that button is being hidden so let's take a look at what happened in our dom structure if we inspect element again uh you'll see we've got our dialogue here still and that's all all the same but if we go to our application you'll see that it's got this aria hidden attribute on it and that's the overlay provider element handling doing that and then if i close my dialog you'll see aria hidden got removed automatically from this div and you know my dialog is gone so now when i inspect accessibility properties again you'll see there's my push button so handling hiding everything outside of the dialog pretty simply by adding a couple wrappers around our dialog so now we have a pretty much fully accessible dialogue component here uh you know we can tab through it and it makes sure that the focus is contained within the dialog and auto focuses it on on open we can hit escape or click outside and it restores focus back to the button and we made sure that for screen readers we're hiding everything outside the dialog and we made sure that this thing has semantics and is connected to the title and is labeled properly so now we should uh screen reader users should be able to use our dialog pretty easily as well as keyboard users um so that's kind of how a look at how how it works to take an existing uh html structure that you might already have in an application and and make that fully accessible uh using reactdaria i hope you enjoyed please let me know what you thought thanks
Info
Channel: devongovett
Views: 1,281
Rating: undefined out of 5
Keywords:
Id: osmCsB9DCNI
Channel Id: undefined
Length: 20min 53sec (1253 seconds)
Published: Wed Oct 07 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.