Last updated: 13 April 2022
- Presentation slides — PowerPoint (7.3 mb)
- Blog article: The web is inherently accessible (13 April 2022)
- Video: Screen reader demonstration
Recorded for DjangoCon US, 22 October 2021.
Rachele DiTullio: Hello, my name is Rachele DiTullio. Thank you for joining me for my talk The web is inherently accessible.
So let me just tell you a little bit about me before we get started. I’ve been a web developer for two decades. I got my first job as a web developer back in 2000. For several years I worked as a front-end developer for a software company before deciding to go back to grad school and study information science.
I got my master’s degree with an area of specialization in user experience design. And that’s what I focused on for the next few years until I really became interested in accessibility. Between 2019 and 2020, I got a couple of certifications from the International Association of Accessibility Professionals and now I am a Certified Web Accessibility Professional.
And with those credentials and my front-end knowledge,earlier this year I became an Accessibility Engineer. And what an accessibility engineer does on a day-to-day basis is review designs. They can be pre-production or things that are already in production and we’re looking at designs and code whether it’s websites, mobile apps, kiosks—and testing that software for how usable it is for people with disabilities.
So what are we going to go over today? So this talk is called The web is inherently accessible and we’ll talk a little bit about what that means before we really get into it and then we’ll talk a bit about what web accessibility is before actually diving into several code examples around using the proper semantics for certain elements on the page that can easily be done with ARIA or some other code and may break the semantics. So we’ll talk about using native elements whenever we can.
So web accessibility provides an inclusive experience and what we mean by that is that by default the web is accessible if we’re using standard native HTML elements, even ARIA, we can create a very inclusive experience if we are intentional about it. If we’re not intentional about using accessibility best practices, we can very easily introduce several accessibility issues into our code.
So to kind of demonstrate this, I came up with a site called Accessible Web and it’s both a tool that will strip out the CSS and JS from a page so you can just see the HTML elements but it also includes this about page which I’m showing here on the screen.
It says, “Accessible web please disable CSS to view this webpage.”
If we look at this page, you can see, I’m going to hit CTRL+A.
I’m highlighting all the content. There’s no hidden content like the text is the same color as the background or anything like that. It’s programmatically hidden and what I want to do now is start up a screen reader. JAWS is the screen reader I’ll be using and we’ll see we can listen to that screen reader and we can tell that there’s content on this page that for someone who uses assistive technology, like a screen reader, this page is fully accessible.
But if you’re someone who relies on sight, the page is not accessible and that was that was strictly a design choice.
So let me start up JAWS here.
Screen reader: JAWS
Rachele: And I will just have it read some of the page so that you can get an idea of what JAWS sounds like and also get an understanding of how
text can be hidden but accessible.
Screen reader: Accessible web vertical bar About – Google Chrome
Banner region visited same page link skip to content.
Visited heading level one link Accessible Web.
Main region, heading level 2, the web is inherently accessible.
Blockquote, “Your weekly reminder that the web is accessible by default and it’s our design decisions that stop it being accessible #a11y.” link @KevMarvel_CT on December 7th 2020, blockquote end.
Why did you have to disable CSS to view this website? No reason other than a design choice that excludes sighted people. Did you know? This is how many visitors “view” web pages already colon
List of five items. Bullet search engines. Bullet bots, bullet site crawlers, bullet analytics, bullet blind people. List end.
This website is fully accessible to people with link screen readers and braille displays but many websites are not due to poor design choices that exclude some people.
Rachele: Okay so let me turn off the screen reader now.
So we could hear it saying a lot of things and I actually have and we’ll be looking at the contents of this page in a little bit but it’s just a kind of to demonstrate the issues that we can introduce with accessibility and hopefully make you feel a little bit of the discomfort that folks who encounter disabling websites every day feel and how it’s frustrating. It’s frustrating.
When we go to a website, we expect to be able to use it and when we can’t that causes frustration. So let me go back to the presentation.
All right so let’s talk a little bit about what web accessibility is. Web accessibility is the extent to which a website or web application can be used by disabled people. That’s my definition. You’ll see variations on this but essentially it’s important to think of just it can be useful to think of disability and accessibility as a spectrum.
I’m showing on the page here a horizontal continuum from less to more and there’s a marker towards the more end of the spectrum and it says web accessibility and this is just to show that websites are neither accessible or inaccessible.
Every page, every element, every component has accessibility factors to it and the culmination of these and the full experience of using the site determines how accessible it is or not.
We’re always striving to make our site more accessible but even if we check all the check boxes and pass all the criteria, there are those human, subjective things that still may need to be done and can be discovered through testing that would make your site more accessible.
Accessibility is defined by the W3C using the Web Content Accessibility Guidelines which are a set of guidelines that dictate what you should be striving to do to make your site more accessible. And also realize that that is a floor of requirements; it’s not the ceiling. It’s the basic minimum that you should be doing and if you discover other issues in your code through testing or things that are usability issues, you should address those as well.
The Web Content Accessibility Guidelines or WCAG, as you can see in the middle of the chart on the screen. It is made up of four guiding principles and those are that content is perceivable, operable, understandable and robust.
Now the Web Content Accessibility Guidelines have gone through several versions over the years in fact we’re very close to version 2.2 but right now the version that most people test against is WCAG 2.1 and within each version there are success criteria, the discrete things that we’re trying to accomplish on our website to make it accessible, and those success criteria are divided into three levels: A, AA and AAA.
Most folks, and this is largely due to because laws often point to WCAG 2.1 level AA, this is the version and level that most of our clients are testing against and striving for. That doesn’t mean that you can’t also do the success criteria under like the AAA level but those are often seen as going above and beyond the the bare minimum.
So again if you’re trying to make the most accessible site that you can look at those level AAA requirements but in general sites are tested against 2.1 Level AA. And that combination results in 50 success criteria which may sound like a lot but it’s very rare for any one web page to be subjected to all 50 of those success criteria.
If your web page doesn’t have a video, that’s about five success criteria that you can just say are not applicable from from the get-go because you don’t need to worry about captions or audio descriptions, that kind of thing, if you don’t have a video on a web page.
That being said, did you know? The WCAG guidelines were first published way back in 1999 before I even had my first web developer job so no matter what framework you’re using, technology stack, anything these guidelines have been out before that.
They were around when we had HTML and that’s a lot of what guides how accessible a site is is whether we’re using the semantic elements in order to enable people who are using assistive technology, like the screen reader that I demonstrated, to understand the interface in a programmatic way because they can’t necessarily see the color of an element, the shape, the position.
They need that programmatically identifiable information to understand controls on the page.
That being said every semantic HTML mistake introduces accessibility issues into your code. If you are not properly labeling an element, if you’re not using the right input, all of these things have programmatic information that are relayed back to assistive technology users when they’re using something like a screen reader or a braille display.
And when we don’t programmatically use HTML correctly, we get these accessibility issues from semantic mistakes. My goal, and I really hope to convince you as much as you can to use semantic elements to make sure that your interface is as understandable as possible for people who can’t necessarily see it or use it in the way that you might imagine the average user would use your product.
So like I said, we’re going to look at some code examples and I put some code from that Accessible Web about page into a CodePen so you can go to https://tinyurl.com/HTML33T to open up the code example.
So I’m going to switch back over to the web browser here and open up that CodePen and you can already see here on the left in the HTML panel you can see a lot of this HTML that is in there and that can be read by a screen reader but is not available to a sighted person who has CSS enabled on their browser.
So the first thing that we should do in order to see this a little bit better is I want to disable the CSS so that you can see what’s on the page as intended.
Okay so now we can see just the rendered HTML and it’s using the browser’s default user agent style sheet. So this is Chrome. Every browser has a user agent style sheet that does some basic formatting of elements. So headings
<h6> look increasingly smaller and bold.
Links are blue and underlined. Unordered lists have bullet points and so forth. This is just to let you know what is actually on that page and then we’re going to look at some examples further down in the code to talk about the specific semantics.
So let me go back to the presentation real quick. All right so the first semantic element we’re going to talk about are headings, like I just mentioned. Those are the
Headings break up content into meaningful sections and screen readers can also navigate by headings, which is why they’re really really useful. I’ve got example heading tags here on the right in HTML.
So we’ve got an
<h1> “Accessible Web” which is the name of our web page.
Then we have an
<h2> “Semantics” and then under that we have our topics we’re going to cover:
<h3> Form labels,
<h3> Buttons and links,
And then within images we’ve got three sub categories. So we’re down to
<h4>, so, Alt text,
<h4> and Charts and maps
And that gives us a way to break up content into logical chunks and makes it easier for folks who can see; they can easily scan the page and headings will jump out at you and if you’re using a screen reader, like i said, you can also navigate by headings and I’ll demonstrate that when we go back to the code.
So let’s take a look at our example code and we can look over here. So here’s our heading
<h1> “Accessible Web.” Here’s our
<h2> “The web is inherently accessible” and so forth. And we can see here on the content side, this is bigger than this. These are
<h2>s and this is an
<h3> and you can see how the user agent style sheet cascades those down.
When someone is using assistive technology, like a screen reader, those heading levels get announced. So let’s fire up the screen reader again and see how that sounds.
Screen reader: JAWS
[Screen reader jumping through content]
Heading level, main region, heading level 2,
the web is inherently accessible.
Rachele: So you can hear, “The web is inherently accessible, heading level 2.”
Screen reader: Main heading 1, heading level 1, accessible web
Rachele: “Heading level 1, accessible web”
[Screen reader jumping through content]
Web Content Accessibility Guidelines left paren, WCAG, right paren, heading level 3.
Rachele: Okay sorry that was a little bit verbose but now what I’m going to do is I’m going to hit the H key on my keyboard while the screen reader is running, while JAWS is running, and that will show you how the screen reader can switch between headings down a page just like we can scan with our eyes.
Screen reader: Semantics, heading level 2. Headings, heading level 3. Form labels, heading level 3. Buttons and links, heading level 3. Tables, heading level 3. Images, heading level 3. Alt text matters, heading level 4. SVG, heading level 4.
Rachele: Okay, so I’m going to disable JAWS again.
Screen reader: Unloading JAWS
Rachele: So you can see how headings really break up content and also allow navigation to be easier for folks who are using assistive technology and that’s the main reason to use headings because anything that visually looks like a heading should programmatically be marked up as a heading.
And the way you do that are with the
<h6> elements. Best practice is to have one
<h1> on the page just so that that’s the, you know, the overall main heading topic on the page. HTML5 doesn’t require that, again it’s just the best practice to have your single
<h1> that says what the main topic of the page is.
All right, so that’s headings. Now let’s go back to our presentation. And let’s talk a little bit about forms.
So form labels. Labels provide visible and accessible names to form inputs when they are programmatically linked. So a lot of people know about the
<label> element, which you can see here in our HTML example
<label for="email"> and then we have the visible label is “email” and end label.
And then in the
<input> that follows it, we have an
id="email" and that’s what the
for [attribute] in the
<label> is referring to. It’s referencing the ID of the
<input> and that is how we programmatically link an
<input> with a
If we don’t have the
"for" [attribute] or if the
<label> does not surround the
<input>, which is typically what you do with like a radio button or a checkbox, then that semantic link is broken.
And what does that do? Well when a user using a screen reader gets to an
<input>, the screen reader may not announce the name of the
<label>. It’ll just say, “text box editing” and this is because the
id attributes are not aligned. This makes it harder for people who use screen readers to fill out forms or understand what controls mean when they don’t have clear accessible labels.
The other thing that we should note, two other things that we can note on the
<input>, here. So we have
type="email", there are different types, so the default type is
type="text" for something like entering your name but there are special [input] types and “email” is one of them that programmatically indicates the kind of information that this field is looking for and that’s another accessibility requirement.
It makes it easier for folks to to fill out forms. It can trigger a different kind of keyboard in a mobile context, you may have noticed that before, that looks different than the regular just typing keyboard.
And then the other attribute that I want to draw your attention to is the
autocomplete attribute. This is another accessibility requirement. So anytime you are asking for personal information that could be stored in the browser, something that the site could autocomplete because that information has been filled out in the browser before, we can trigger that by saying
So any other fields where they’ve been named email and there’s a value that’s been entered then the browser can remember that and help folks autocomplete future forms with that
And there’s a whole list of those autocomplete attributes for anytime again that you’re asking for personal information, so name, email, address and so forth.
All right, so let’s go ahead and look back at our code again. I’ll scroll down the page here a little bit until we get to the form labels section and I’ll scroll down in our HTML.
Okay, headings, form labels. So here we are, Form labels.
A couple of things that, when these are programmatically linked correctly, some things that you can do. You can click on the label to actually toggle the control and that’s a really big help because these hit areas are usually pretty small on radio buttons and check boxes but if you can click on the label as well, that makes it a lot easier for folks.
And if you’re creating your own custom components, and I’ll talk about this a
couple of times during this presentation, if you’ve got these as a custom component, for some reason you don’t want to use a default checkbox, there are several things that you have to do to make that checkbox work.
- It has to work on mouse click.
- It has to work with the enter or space key on your keyboard.
- It has to be focusable.
These are all things that are natively done when you use out-of-the-box
If I use my keyboard here, you can see there’s a focus indicator when I tab through these with my keyboard. So you have to make sure there’s a focus indicator. Again, if you’re doing anything custom, or you’re messing around with focus indicators, I’m using my space key here to select and unselect that.
That’s an example of form labels. Let’s look at
<input type="text">. Here we’ve got, and I’m going to scroll down so that we can see the HTML as well.
Oh yeah, I want to mention that when you have a group of controls like a list of radio buttons with several options, check boxes with several options, other semantic elements that you want to use are the
<legend> which you can see over here.
<legend> programmatically groups form controls in a way that assistive technologies users can understand that these check boxes are related in some way and not just a series of checkbox inputs with no commonality.
<legend> for our
<fieldset> here is Cat’s Colors and then we see that each of the options is a color. That’s an important thing to do is programmatically group form elements when you can.
Now if we look at some typical just text-based
<input type="text"> where we can just type in something like Cat’s Name again we can see there’s a focus indicator around our field. There’s a cursor that tells us we’re on that field. f I click on Cat’s Name, it’s putting focus into the field, just like it toggled on and off our check boxes. You have that same hit area when you’ve got it programmatically associated. That lets you also tap on this and not necessarily have to tap on the form field.
Just a quick thing that I want to mention: You always want to make sure that these fields are programmatically determined to be required or not. We’ve got a little asterisk which sighted users can see and know that a field might be required, but assistive technology doesn’t necessarily.
So we’ve done two things here. One, we’ve used
aria-hidden="true" to hide that star, that asterisk from screen readers so they don’t hear it, because it’s really just for sighted users, but on the
<input> we’ve done two things. We have
aria-required="true" which will programmatically announce that a field is true [required] and we also have the HTML required attribute which will allow some moderate in-page form validation but don’t rely on it. It’s not always announced correctly by screen readers.You still want to have your own field validation on submit and display error messages and such but this can help users fill out the form more quickly.
Let me start up the screen reader and we can listen to what that required field sounds like.
Screen reader: JAWS
Rachele: As well as let’s listen to these check boxes too.
Screen reader: Group start, black, checkbox, not checked. Group start, Cat’s Colors.
Rachele: So, “Group start, Cat’s Colors.”
Screen reader: Black, checkbox, not checked
Rachele: “Black, checkbox, not checked.”
Screen reader: White, checkbox, not checked. Orange tabby, checkbox, not checked.
Torbie, checkbox, not checked. Gray, checkbox, not checked.
To check press spacebar.
Rachele: Okay now I’m going to tab over to the Cat’s Name required field and let’s hear what that sounds like.
Screen reader: Cat’s name, edit, required, invalid entry, has pop-up, type and text.
Rachele: So you could hear that it said “invalid entry, required” and that lets a screen reader user know that they have to put some kind of information in this field. Now let’s listen to the email one.
Screen reader: Email, edit, required, invalid entry, has pop-up, type and text.
Rachele: So again this one’s required, it just doesn’t have the visible asterisk but it programmatically it’s still being announced as required because we have
Let me turn off JAWS.
So yeah something important to note is a lot of people have heard of ARIA which stands for Accessible Rich Internet Applications. It’s basically a set of attributes that extends HTML’s semantic capabilities for more advanced functionality. Things like a menu that has a pop-up; various kinds of button tasks that happen when you need a button like a date picker or logging out, that kind of thing.
So yeah let’s… That’s form label. These are important. Next, let’s look at another part of forms which… Let’s go back to the presentation.
All right, quickly let’s look at tables. A lot of folks think that they should not use tables, that tables are completely off limits. This is for those of you who are too young to remember. We used to use tables to layout our designs in HTML and that has semantic problems.
If something is a
<table>, it should contain tabular data and nothing else. There are a lot of valid reasons to have tables and tabular data, we just have to make sure that they’re marked up correctly. So we can see here in the HTML, we’ve got a
<table> has a
<caption> , it can be visible or hidden but it’s important for assistive technology users so they understand what the purpose of the
<table> is. So that will be announced at the beginning of the
Then we have our first row in our
<table> and that first row generally contains our column headers and that is a special element called the
<th> element. So it really does matter if you use a
<th> instead of a
<td> for your column headers because for assistive technology users these column headers will be announced periodically depending on the screen reader to help orient as they’re moving through different rows and columns of the
<table> with the arrow keys or tab and it will identify something as a column header and then tell you what the name of the element in that row is.
So we can take a look at have a listen to what that sounds like. Let me go back to our web page. I’ll scroll down here…to Tables. All right, so we can see here, I’ll scroll down this.
So here’s our HTML here
<th> and so forth and this is what it renders as.
<caption> is at the top of the
<table>. Then we’ve got our two table headers which the user agent style sheet bolds to make them look different. And then we’ve got our table rows with our data. So let’s see what that sounds like.
Screen reader: JAWS
[screen reader jumping around the content]
Table with 2 columns and 5 rows. About My Cats.
Rachele: Okay, “Table with two columns and five rows, About My Cats.” Then we’re gonna go to row 1.
Screen reader: Cat’s Name, Cat’s Colors, Luna, torbie, Stellen, black and white, Shadow, tabby, Brienne, black and white, table end.
Rachele: Yep so as I use the virtual cursor to go through the
<table>, you could hear how it actually announces all of the table data cells in the
<table> as I navigate with my arrow keys. There are other ways to navigate tables that’s not just sequentially or tabbing through but we won’t get into that for this talk.
But it’s important to understand that there are definitely times that you need to use tables and HTML tables.
A lot of folks will try to markup a table using ARIA
role="grid". This is only, the
role="grid" is only for interactive tables, if it’s literally something like a spreadsheet that you’re trying to replicate in HTML. Otherwise you want to just use native table layout and it is the most semantically sound thing and it allows users to navigate through the data in ways that they understand how to do with their assistive technology.
All right so let’s go back to…
[screen reader jumping around the content]
Rachele: Oops, let me turn off the screen reader.
Buttons and links
All right so Buttons and Links. Let’s talk about those real quick. A lot of people think these are interchangeable, that a button can be used as a link, a link can be used as a button but again there’s a strong semantic element here and these two things do different things and should be used for their specified purpose only.
A link moves focus to a new URL, whether that’s jumping somewhere place in page with an anchor, like when we use a skip link, or actually taking you to another web page. When that URL changes in the address bar, you should be using a link unless what you’re doing is performing an action.
So for example you might have a form—we all know about buttons on forms—a button can submit a form but buttons should also be used for anything that’s interactive, say opening the menu on the mobile viewport of your site, the hamburger menu.
A date picker, a disclosure widget, any number of things where if you’re clicking an element or focusing and using the keyboard on an element to make it do some kind of action within your application, that should be a button and one of the important ARIA attributes that you want to use on a button if you are doing something like having a menu pop out, you want to use
aria-expanded="false" when the menu is collapsed but as soon as that menu is expanded, you change that attribute to
"true" and again this is a semantic way to express to screen readers the state of the UI.
So the screen reader will say “button, expanded” or “button, collapsed” based on that particular attribute and that’s really useful for folks who can’t see the interface but need to know what is happening and what it’s doing.
So let’s go ahead and look at our code example and we’ll look at some buttons and links.
I mean everyone knows what a link is, they just get misused sometimes. Especially like folks will use an
<a href> element but then they will say
role="button". You don’t want to do stuff like that. If you need a button, use a
<button> [element] as much as you can.
Now I realize there are some framework limitations but as much as possible you should be using that native
<button> element. And what does that do? Well when you have a native
- It already can get keyboard focus for folks who are not using a mouse.
- It can be activated with both the space and enter keys.
- It’s activated on key down.
- It’s activated on mouse click.
And you can give it an accessible name. And you have these helper classes and ARIA like
aria-expanded [attribute] to help identify for folks who can’t see the screen what’s going on with the application.
But if you say take a
<div> or some other unsemantic element and you add that
role="button", you have to programmatically make sure all of those things are true: It has to be focusable with the keyboard; It has to work with space and enter keys and so forth. And you save yourself all of the time and hassle of doing that if you just use the native
<button> to begin with.
All right, lastly let’s look at the semantics around images.
For every image or graphical object, you want to provide an accurate text equivalent. This includes things like icons, SVG charts, maps, other graphics. Anything that’s conveying information to the user and that is somewhat subjective but if you are looking at an image and it’s doing anything other than just providing decoration, you should have an appropriate text equivalent.
So let’s look at our two code examples here. The first is the standard HTML image element and if people know anything about accessibility, it’s generally that images need alt text. And so this is what that looks like.
<img> element has an
alt attribute. Our example is “two cats on an easy chair under a blanket” and we’ll look at that in the code example.
But what if you have like an inline SVG that does not support the alt attribute? So if you were loading an SVG with the
<img> tag, an SVG file, so say
cats.svg, that would still support the alt attribute.
However if it’s inline SVG in the page where you’re doing some kind of dynamic code switching, we have to label it a little bit differently. There is a
<title> element that goes in the
<svg> element that holds the name of your label, and in this case it’s “purple hexagon”.
What we do is we give a unique ID to that
<title> element. Here it is
id="svg-title". Then we go up to our
<svg> element and we can say
aria-labelledby="svg-title" and the screen reader will announce that it’s a graphic and the name of that graphic, whatever we’ve given it in
So let’s go ahead and go over to our code example and see what that’s like.
So here’s our image example. We can see here and there’s two cats under a blanket. Then we’ve got the purple hexagon. We can see our inline
<svg> here with our polygon and fill and then a more complex image down below.
So I’m going to go ahead and start JAWS and then we will hear what these sound like.
Screen reader: JAWS
[screen reader jumping around the content]
Two cats on an easy chair under a blanket, graphic.
Purple hexagon, graphic.
A cat lying on a bed that is divided up into areas, graphic.
Rachele: So for all those we could hear essentially a text equivalent that describes them in some way. Our third example, the more complex example of a cat on a bed that’s divided up into areas, below that we have some text, and this can be visible or hidden, best practice is to make it visible to everybody but it’s a longer description that further describes a complex image.
If this were some kind of chart or graph, you might have a data table that lists out the specific data points within the graph and some kind of summary explaining what the data is showing.
For this one, what we have if we go over and look at the HTML here on the left the image is marked up in a
<figure> element and the longer description is marked up as a
<figcaption> element with a unique ID. The
<img> element can then use the
aria-describedby attribute to reference “cat-map” and what that allows to happen is both the alt attribute value is announced by the screen reader as well as the longer description from the
So let’s go ahead and listen to that real quick.
[screen reader jumping around the content]
Screen reader: A cat lying on a bed that is
divided up into areas, graphic.
Cat bed never used. Pillows show wait for food here and purring zone. Other areas on the bed are listed as grooming salon, sleeping area, launch pad to…
Rachele: And so forth. so you get the idea. So I’m going to go ahead and shutdown JAWS.
Screen reader: Unloading JAWS
Rachele: Okay. So let’s go back to the presentation.
And that’s all I have for today.
Thank you very much.