 This is Aria Owens, Let's Make Forms Accessible, and I am Tina Vance, and this is Kat Passen, and we're here to talk about how to make Python forms a little bit more accessible using Flask, WTF Forms, and Jinja, and good old-fashioned accessibility. So how many people were at the last talk by Stephanie? Just a few of you, right? Yeah. So some of this will seem a little bit like old hat, and for those of you that weren't, we're going to go through a little bit of what accessibility is before we get to the meat of the talk, which is how do we do this with Python? Okay? So we have, since all of you can kind of read here, we have a bit of an agenda for the talk, and then also the GitHub repository for the slides. So the intro, this app is really basic. It focuses on accessibility, and a lot of the examples are stripped down to focus on that. So it's pretty simple, but we think you're going to learn something. So what is accessibility? According to the W3C, it means that people with disabilities and impairments can perceive, understand, navigate, and interact with the web, and that they can also contribute. When people that benefit from accessible websites, they use assistive technology like screen readers, which read content out loud or use keyboards to navigate those websites. Assistive technology, screen readers, a lot of the things that Stephanie was talking about, like sip and puff devices, headmouses, things that allow people to navigate the web and use the web in ways that are tailored to their abilities. So there are a lot of different disabilities and impairments that affect access, visual impairments. There's a couple of people in this room that wear glasses. That might be a visual impairment. There is also low vision, color blindness, which is kind of a common thing, right? And that can impair how people can see color, and a lot of designers love to use color, and they're not wrong, color is great. But if you have a problem with seeing color, that can impede how you use the web. Cognitive disabilities, head injuries, ADHD, dyslexia, dyscalculia, a lot of those can really harm how you use the web. Anybody's used any CAPTCHA that says what's one plus one? If you've ever used anything that is extremely wordy in order to get through instructions and user license agreements would be one thing, right? Auditory disabilities, people with low hearing, people with deafness. Those things can make using YouTube a little difficult unless you have captions. Physical disabilities, people that have cerebral palsy, people that have Parkinson's disease, things that make using a hand mouse difficult. It's very difficult to navigate when you can't keep your hand still in order to smoothly move a mouse. People with speech impairments, how many people use Alexa, Siri, Cortana, all of those? If you have a speech impairment, it makes using that technology somewhat difficult. Okay, a lot of these measures, both digital and analog, benefit people without disabilities, like curb cuts, and I know if you were here for Stephanie's, you saw this exact same picture. So running joke here. Curb cuts help a lot of different people. It helps parents with strollers, helps people on bicycles. Whether or not you think people on bicycles should be on a sidewalk, sure. But it still helps them, right? It helps people like me who trip a lot and who love using curb cuts because that's one less thing that I have to lift my legs for, right? So this benefits a lot of people in that same way. Web accessibility benefits a lot of people. How many of us programmers use keyboards to navigate the web? Because using a mouse means taking your hand off the keyboard and kind of interrupting your flow. Well, thankfully with keyboard navigation and keyboard navigation standards, we can do that. So there are a couple of groups that help us with this. These are two of them, and they are WCAG and ARIA. These groups and these standards help us to kind of navigate and know what we need to do to make things accessible. Now WCAG is an acronym for Web Content Accessibility Guidelines. And these are guidelines. They are not the law, but they help us figure out how to do this. We have three different levels of accessibility that WCAG helps us with. And these are level A, which is very basic. Do you have alt attributes? That sort of thing. Level AA, which addresses the biggest barriers to accessibility. And then AAA, which is the highest most complex gold standard in their eyes of accessibility compliance. And these are based on the poor principle, which is perceivable, operable, understandable and robust. Perceivable. Can we see? Can we figure this out? Can we, as the name says, perceive it? Operable. Can we use it? Can we navigate? Can we get to the goal of the page or the app? Understandable. Can we understand it? I mean, is it something that users can actually figure out? Or is it something that's pretty convoluted? And then robust. How does it hold up? Robust also helps with things like cross browser. If your app is robust, it means it's also cross browser, cross platform. How many people can use it? And is it something that they have to depend on an operating system or some other tool, a browser, like specific one to use? Then we have ARIA. ARIA is an acronym for Accessible Rich Internet Applications. And this is essentially a set of attributes that we use that help with assistive technology. So these help to find things like roles, is the role in navigation, is it content, that sort of thing. Or disabled or even ARIA hidden, which you'll see in a little bit. We're going to give you a good example of ARIA hidden and when it should be used. So we're going to get into some of the basics of this in a bit. So now what is Flask? Flask is a Python micro framework. How many of you are familiar with Flask? Awesome. There are some hands. And it depends on workzoic and Jinja, which leads us to what is Jinja. And Jinja is a templating engine. Jinja, since I'm a front-end person, Jinja is my fun, right? It makes it easier to build consistent sites and applications while embedding the Python when we need to. And this allows us for code reusability, right? So here is an example of our simple form. And we've got a sign up for our newsletter. We've got your email, sign me up. And then this lovely emoji because emojis are fun, right? So we have a simple validator in here. We've got a class email-only form. Email with email field. The label, which is your email because that seems pretty logical. Validators. So we're figuring out, hey, validators, you need to have a minimum length of six. And then your email address needs to be six or more characters. And then the email address you entered has a problem, check for typos, for the error message. Now these are all things that Flask and WTF forms give us. We can do that. And now we're putting it into the template with the form. So we've got a form with an ID, method post, all of that good stuff. And then we're putting in the email label form, or form email label for the email. And then the actual form and then errors. And making sure that it's autocomplete off, right? And there we go. So this form is going to get big. Kat's going to cover that. Yeah, so we don't want to write all of that over and over, right? If you've got a form that goes beyond just a couple of inputs, say maybe you've got like a payment form. You're taking somebody's payment information. And you think about, that was like 10 to 15 lines of code, right? You don't want to copy and paste that over and over and over. You'll have a thousand line template in no time. So what we're going to do here is we are going to adhere to the dry principle of don't repeat yourself. And we're going to try to make this code a little bit better. So we're going to use something called template macros. And this kind of works like declaring a Python function in your template. So this macro declaration render inputs as our function and we're passing in a form field. So if the field that we get has errors, we're going to do that same thing that we did before. We're going to create a span for it and give it the error message. And if we don't have errors, then we are going to build a couple of different kinds of fields. So the Boolean field is a checkbox. So we're going to wrap the field itself in its label. And using the, so we have a label for the field ID. Then we create the actual field. And then that field dot label dot text is how we use the text of the label that we gave it earlier without it building the label tag for us. And that way we can do this, wrap it. And now we're doing radio fields. So this is radio buttons. So we're going to make a field set to hold them all to group them together. We have a legend that works the same way that previous label did. So now we can not just have a label tag in there. And each single radio button that we give it is a subfield. So we have our field that is the group of radio buttons. And for each subfield in that field, we're going to put the subfield and its label. And then if we have anything else, for our purposes, we have a select box and then the email input, those don't need anything fancy. So we're going to let it just build the label tag on its own and then put the field in place. And when we use all of this, we need to update our validators accordingly because that's what we're using. So we are importing from WTForms all of this stuff. And we have the hang up here of the email field as an HTML5 input and it does not tell you that you have to import this separately and special. So this is here so that you can all learn. So the email validator is still the same. We've got all of the stuff that we already showed you. But when we're building further, then scroll up. As we build it further, sorry, we pass in our previous forms that we've built. So this way you can chain everything together. So imagine that you've got something that you're reusing over and over. Maybe you've got an email and password form that you want to pass in a couple of times. Then you can just pass that into your next one. And this will append that on top of all of the new stuff. Can you please go forward? So now we're adding on the check box. We have it a label. We're asking are you a robot or not. This is our simple capture. And we're just checking that somebody did check the check box. Now we're building the radio buttons on top of that. So we're making a radio field to ask how often do you want to receive mail from us? And so we're just checking that somebody did check one of them. And you can pass in a whole bunch of different choices that correspond to the value on the back end that you're going to get. And then the label on the front end that you will put in place for that radio button. And now we're putting a select field on top of that. And it looks really similar to the radio buttons just because of how it works. But this will build instead of radio buttons, it will build a select box. And that's what all of this looks like together. And now we've got ourselves a fancy field. And it's a super simple form. And everything's great. Go to the next. We're not doing that yet. So we did it, right? This is A10 plus. Everything's great. We did it. It should be fine. We used all of the simple HTML5 types. Everything is built to follow the standards. This is good, right? We're doing it right. So let's try it out. I'm going to turn on voiceover really quick and test this out to make sure that we made it accessible. So to start with, I'm going to do this wrong to check that everything is fine. So I'm going to leave everything blank to begin with. I don't think we did this right. So we've got a bigger problem than that button. I didn't hear that something went wrong. So we talked about using color as a thing to indicate that things are wrong. And that's not good enough. And this is kind of that way of showing that we did do everything right. We followed the standards kind of, and it passes. So let me show you really quick that this does, in fact, pass the rules. Oops. I need to turn you off. So this is Wave. Wave is a testing tool for accessibility. You'll see that this is the same form that we were just on. There's no errors here. This is correct, but we still have more to do because it didn't tell us the errors and that button didn't work the way we expected it to at all. So let's talk about fixing it. So to start with, plain HTML alone is not accessibility on its own. There is always something that you can do more to help out because this just helps at a bare minimum. Somebody can actually use your stuff, and it's not technically inaccessible, but somebody's still going to have a hard time with it. So what we're going to do is use ARIA to give some extra context to all of the accessible technologies that could be using our page. So what we're going to do is we're going to hide the cosmetic content that they don't actually need to hear. We're going to provide some extra context for elements in the page. We're going to group our errors together with the input so it'll tell you when something is wrong, and we're going to alert people when there are errors so that when the page reloads, if something goes wrong, people will know immediately. So let's talk about hiding stuff first. We mentioned ARIA hidden earlier, and what this does is an attribute that when you put it on a tag, the contents of that tag are no longer read out to a screen reader. They are for all intents and purposes invisible to it, but it's still visible on the page, so this will still show in the button. It's still content that's there, but it's not known to the screen reader, and it's kind of like the equivalent of using CSS content, but this is just something we had in the page already. You can use this to hide things if you're doing a retrofit and say that you have something that is not necessary. It's more of a decoration, but you have to keep it there, but you also don't want people to accidentally read it out because it will confuse them, it doesn't help them at all. You can use this to hide that from them. So now to provide context, we give the form a role of form and an ARIA label. So the role equals form here is really redundant. For the most part, everybody is going to be able to pick up that this is a form unless they're using something really, really old, like they're on Windows XP and their screen reader version is really old. However, this is really powerful if you're doing something like building an app in Angular where you built a whole bunch of things that look pretty, but in reality, you use divs for all of them. And if you give them a role, then the screen reader will think, oh, this div that has role equals checkbox is actually a checkbox. Now, you still have to do all of the extra lifting to give it event handlers and everything to make it act like a proper checkbox, but this is just giving that extra information so that the screen reader knows what's happening. And the ARIA label is something that it will read out, so when I use my shortcut keys to go to the form, it would say newsletter form instead of just form. And this is really handy when you've got a couple different things on the page that all share the same kind of thing. So say you've got a search box on your page that's a form and then you've also got this. If you just are skipping between forms and you can't see, you'll only hear form. How do you know which form you're on? So now, to group the errors, we give the error message that goes with the input and question and ID. We're going to use this ID in a attribute called ARIA described by. And what this does is the described by says, hey, this ID of this element has some extra information that goes with this thing. So when I tab onto the input, it will read out your email, email, edit text, all of that other stuff. But after a pause, it will read out your email address needs to be six or more characters. And you can use this for things other than errors. You can use this if, say, you have a telephone number field and you have a specific format that you have to have it in because you have really weird reasons and requirements. You can have some extra text off to the side of the form that says your phone should be in this format. And you can use the described by to give that information to the screen reader so when somebody moves to that form field, they'll hear your phone number needs to be in this format. We're also adding ARIA invalid equals true and ARIA required equals true to the input. The invalid makes it so that when you tab onto it, you'll hear that it is invalid, something is wrong, so you can use it to indicate errors. And the required tells the user that this field is required. And we can use that in place of HTML5 required for browsers that don't support that and for some other stuff later that I'll talk about. And finally, we're going to alert the user if something is wrong. So this role equals alert is special. What this does is as soon as it is placed in the page, it will shout alert or beep at you or depends on what screen reader you're using does. And then it will read out the contents of the tag that it's on. So this will beep, shout, alert, and say please correct the errors highlighted below. And you can use this for errors or for successes or for anything where you need to immediately inform the user something has changed. So how do we put all of these into our template? So we're going to use keyword arguments to do magic with the JINDA templates here. So when you're normally doing this by hand, say you're just writing plain HTML, you have to do this for every single thing. You have to put required on everything. You have to have a way to put invalid on everything. You need to describe by and everything that needs to describe by. This gets tedious. We don't want to do tedious. We want to do better. So we can pass these ARIA tags through with the keyword arguments and then JINDA and WT forms can do all of the magic necessary for us. And with the macro, this is super easy. So next, this is our macro. This is how we call it. So we import it and then for each field in our form field, we call the macro and pass in through the keyword arguments, ARIA required, ARIA invalid, ARIA described by. And this is for both errors and not errors. And we're going to do some science in the macro to make sure that this acts accordingly. So if we have errors, we want to put in our error message for the associated field. Otherwise, we want to get rid of the invalid in the described by because we have no use for them. I want to take a moment to say I don't know if that quarks.pop is like the correct way to do this. However, this way it does not show up in the template. This is like the easiest thing that I found to do it. This may not be the right way. Your mileage may vary. So now for our fields, if it's a checkbox, we can't autocomplete it. ARIA required is actually an invalid attribute for checkboxes, which seems strange because HTML5 required is not, but I don't make the rules. So we're going to add HTML5 required to help indicate you need to check this checkbox to continue. And then we build the field the same way that we did before. Keyword arguments just get passed through and everything's fine. And for all our other fields, they stay roughly the same. We don't need to do anything because the keyword arguments are taking care of everything for us. So nothing else changed. We just added some extra stuff at the front. So what do we do in our validators to take care of this if anything? So we've got all of this. How do we get all of that into the page? So in our routes, we import our forms and now we can hand these to our templates and have all of that magic happen for us. So in your route declaration, this is me following the conventions in the documentation. I don't know if naming it form is useful to you because it's repetition, but we declare a variable called form. We give it the validator that we want to use and we pass in the request.form. And that way it... And then we pass in to render template the form that we had. So for an error, or rather, this is a success, I'm sorry. Now we're doing a post. So we still have the form. If we validate the form and everything's fine, we're going to do... I will talk about the message flashing in a second, but we're going to give up a success message and say, hey, this is great. Otherwise we're going to say, hey, something's wrong. We're going to give back that form to the template. So this works fine for all of those things where your email is invalid or everything is wrong, but I didn't talk about how to do a message that has no relation to the actual contents of the form. So message flashing is something cool and it is a way to give a generic message to the user. And this can be something like the success saying, hey, you did it, the form is fine, you're golden. Go forth and do things. Or it can be something like, hey, there's a problem and you need to try again later, I'm sorry. So you can render these like macros and the only hang up to this is that you have to have sessions set up first. And it's not clear from the documentation that this is something you need to do, but you will quickly find out if you try that you need a session set up first. So to do this, we do a statement to declare this variable messages and we get the flashed messages from the back end and we're asking for the categories along with them. So this way we can have messages of different kinds. So for all of these, we're going to put them in a div that has roll alert so that way the user knows as soon as something is wrong, hey, this is fixed or rather you have done something, here's your success, here's your error, here's whatever it is. So for each category and message, we are going to build something that puts in the page what it is. So if it's an error, we give it a class of error message and if it's a success, then we're going to give it a success message and if it's something else, then it's just a thing. And there's no extra stuff that we had to do, but this is what it winds up looking like. You've got your wrapper that says, hey, something happened and then you've got any messages that may have come up and all of them, they only differ by what we had for each single message. So now that we have all of this together, we are going to botch the form again. It said botch it, not do it right. Leave a blank. Yes, leave everything blank. Hold on, turn on the voiceover for you. Hold please. So if we did this right, it should tell us that we did something wrong. So that's better. We now know that something has gone wrong, so let's double check that it tells us what was wrong. So that's better. Now we can tell for each field what has gone wrong and if we do it right this time, we can just resubmit this. So that's a lot better. Now somebody knows that they have done things right and it is magical and good. So what if you don't want to do backend validation? What if you want to do client side validation? So as a rule of thumb, when you're doing this, they should be as close as possible, much like asking for an inaccessible experience to be as close as the original experience when you're doing a retrofit. So when you're doing your validation, you don't want to be intrusive because people don't like when you interrupt them, but you also want to warn the user as soon as they have done something wrong and notice that there's a contradiction here because you can't really do both of those very easily without upsetting your user in some form. So when this kind of is a problem, you just want to make it as usable as possible. So what we are going to do is we're going to let the browser do most of the work for us and we are going to... it's on the right side. We're going to not use HTML5 required to start with because in a lot of cases, I have seen that if you have it on a field and a screen reader moves on to it, it will immediately say it's invalid, regardless of if it actually is. And this might be something that has been fixed in newer browser versions, but because in the case that Tina and I are in, we have a lot of people on older browsers, we want to be more accommodating to them and so instead we will just use RE required where we can. We're still going to do some back-end validation on Subbit, but we're also going to do some JavaScript validation on Subbit because our form is simple. We don't need to stop people as soon as they're typing, but if you need to stop them as soon as they're typing, we want to suggest that you validate as soon as the user stops typing. So this would be setting a timeout for listening to make sure that they have finished and before they move out of the field and focus into the next one, you can run a validation on that field real quick and make sure that everything's fine. So we're going to support what's going on in the browser with a bit of our own validation, and then we add in the RE that we need to add in. So we'll stop the Submit like normal, do some validation. If there's problems more in the user and if there's no problems, we'll submit the form and if there's any back-end problems, then it will do what it needs to do. So simple on Submit, clear out any errors that may come from a previous validation, check each field, then if we have any errors, then we'll put in our global error and we're going to focus the first error that is on the page and what this does is it gives somebody the knowledge that something is wrong and it puts them immediately where the problem is and this helps them by making it so that they don't have to go looking. If you noticed on the page refresh, it took us back up to a Skip to Content link and that's the very first thing that's actionable on the page and we would have to go back by tabbing through or using shortcut keys to get back to the form. This way, we would be at the problem spot and if something's wrong, we're just going to submit the form. So something that is common throughout the code is that we have to put in these in valid the described by in the error message and this is for every single thing that we do. But in special cases like the checkboxes, we need to check and see if they are actually checked, we can't just say is there a weird value or did you do something? So we check, if it's checked do all these things and for the radio buttons, we need to just check that one of them is checked so for all of them in the group is something checked, okay, that's great and if it's not then we grab the field set for each of the children that are radio buttons we put in the error messages and then we put in the error message for those before the field set and we still link those to each radio button but this way we don't have an error popping up under each radio button. So now if we do the form wrong again just tap to the submit you should be there oops it's not a good presentation if there's no technical difficulties, right? there we go and that was all frontend stuff, the page didn't reload and we're focused to the email address field that has immediately taken us to the problem child mm-hmm yep and that is the backend doing all of the success because we didn't put anything in for that so everything is still fine and now we have accomplished on the frontend the same thing we set out to do on the backend and everything is golden and that is our form adventure we have some resources for learning more about accessibility in general inclusive design at Microsoft has a lot of good resources the inclusive design principles page has a lot of good resources design resources is mostly geared towards designers and UX as opposed to developers the WCAG 2.0 quick rep is a monster of a site but it is filterable based on what you want to learn so that you can say I only want to learn about things that are single A and refer to PDFs and that way you can get all the things that matter to you really quickly it also helps for building up your accessibility so if you filter out the things that or if you only do the things that are single A and then decide hey I got single A let's get her double so that filtering can also help kind of perform a little bit of a checklist for you as well and then on the next one we have more developer centric resources that's great so we have a couple links to verified accessibility samples run by IBM they have a couple of pages where you can find working examples that you can test against for simple things that are building to meet WCAG building to show proper ARIA use and building for general best practice use there's also inclusive components which is a blog that shows reasons why something might not be accessible and ways how to make things accessible there's the accessibility style guide which does something similar to inclusive components but it's for more basic things like video players and color contrast HTML5 accessibility is a cool site where you can check and see if things are supported in your favorite browsers fun fact edge is the only one right now with 100% compatibility surprisingly and that's all we have so thank you so we've got 15 minutes for questions if anybody has any yes so the question was that we showed voiceover and is there any other assistive technology that we test with screen readers are a solid choice not all of them work the same so it's good to test with multiple it's important to test them on mobile as well keyboard usage on its own is good for testing for basic things those are the main ones that we check against personally I'm not sure if you want to speak more to any of the other things that you may know of for testing since I do a lot more of the front end stuff some of the things that we also use are color contrast checkers and there are a couple of interesting tools out there to check for color blindness and see that designs have a good way to show information without color so there's like grayscale checking and things like that as well but as far as the technical we use a lot with the screen reader not with keyboard navigation those are the big ones that we end up using a lot yes okay the question was are there any examples of something that might be technically correct to a screen reader but may not be quite the best thing to do for a screen reader one of the examples would be one that was actually in our presentation which was the button of doom with the huge ascii clot that was read out and technically that was correct it was totally fine but the screen reader read it out and it was unintelligible and Katz got another example so if you have used an application where you're doing say image manipulation and you try to select the thing and maybe drag and drop it I don't know how you would make it so that the screen reader knows what's going on with that like you can set up maybe a custom keyboard command that says okay and I select this and I want to scoot it around like when you're arrowing something just to get it perfect in Photoshop how do you communicate where you're at in the image though like how do you tell when you get to a border or if you're too like when you hit the edge of whatever you're moving it around in or if you want to like indicate maybe put it here like you don't have enough context and I've talked to some people about this and they're like you just want to give enough information that they know what's going on but how much is too little how much is too much it's a really fine line to walk and sometimes I've been told that the best experience for that is not to provide a exact match where they can try and have the exact same as somebody who cited could but you want to give them maybe just a textual version instead so that they don't have to try and fight with whatever you're doing and instead you just tell them this is what's going on so the question was things to think about when you're building single page applications and I would say that one of the biggest things is to always tell the user when something has happened so something that we didn't cover live regions this is a good way to indicate that the sections of the page have changed and then you can tell somebody that the page has moved to another view or something that they have done has made a change so that's something where you want to communicate as much as possible what's going on to the user so that they can understand that in action that they've taken has made a change notifications become your best friend when you're talking about updating content on one single page because it becomes very difficult to tell especially if you can't see how the content has changed and how it's updated and what their expected actions are next so single pages can be kind of tricky when it comes to here I'll just take that sorry it becomes very tricky when you're talking about a good user experience for people with different abilities any other questions yes there we go there's github and our entire presentation is up there there are some setup instructions because this is all done in Flask but it's all there to follow along yes that's alright we've done some experimentation with that and automated functional tests where we can use Groovy and Jeb to check and make sure that the attributes are there but a lot of this is very much spot checking because not every link needs a described by and not every input needs this or that or the other thing a lot of it is fairly subjective and fairly tied with the user interface and user experience of the page and of the app so sometimes it's going to just take some human checking we can check for some of the level A things like alt attributes if there's an image it's got to have an alt attribute we can also check for labels and inputs are they linked together we can check for that sort of thing and that sort of thing is actually very easily manually checked when you're talking about doing automated functional tests or unit tests but when we're talking about some of the finessing that some of these elements with ARIA roles ARIA described by those take an actual human being a lot of the time it's all well and good if you put the captions in but you have to make sure they're the right captions because we've had that happen any other questions? ok well thank you