Merge pull request #99 from smhigley/lesson-a11y-updates

Accessibility updates in lessons 3-7
pull/100/head
Jen Looper 4 years ago committed by GitHub
commit d090e15ce7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -167,7 +167,7 @@ Add those plant images into two columns between the `<body></body>` tags:
With this markup, the plants now show up on the screen. It looks pretty bad, because they aren't yet styled using CSS, and we'll do that in the next lesson.
Each image has an alt tag that will appear even if you can't see or render an image. This is an important element to include for accessibility. Learn more about accessibility in future lessons; for now, remember that the alt attribute provides alternative information for an image if a user for some reason cannot view it (because of slow connection, an error in the src attribute, or if the user uses a screen reader).
Each image has alt text that will appear even if you can't see or render an image. This is an important attribute to include for accessibility. Learn more about accessibility in future lessons; for now, remember that the alt attribute provides alternative information for an image if a user for some reason cannot view it (because of slow connection, an error in the src attribute, or if the user uses a screen reader).
✅ Did you notice that each image has the same alt tag? Is this good practice? Why or why not? Can you improve this code?
@ -175,7 +175,7 @@ Each image has an alt tag that will appear even if you can't see or render an im
## Semantic markup
In general, it's preferable to use 'semantics' when writing HTML. What does that mean? It means that you use HTML tags the way they were designed: to represent its data; so an H1 tag should always be present on a page
In general, it's preferable to use meaningful 'semantics' when writing HTML. What does that mean? It means that you use HTML tags to represent the type of data or interaction they were designed for. For example, the main title text on a page should use an `<h1>` tag.
Add the following line right below your opening `<body>` tag:
@ -183,9 +183,9 @@ Add the following line right below your opening `<body>` tag:
<h1>My Terrarium</h1>
```
Using semantic markup such as having headers be `<h1>` and unordered lists be rendered as `<ul>` helps screen readers navigate through a page. In general, buttons should be written as `<button>` and lists should be `<li>`. While it's _possible_ to use specially styled `<span>` elements with click handlers to mock buttons, it's better for differently-abled users to use technologies to determine where on a page a button resides, and to interact with it, if the element appears as a button. For this reason, try to use semantic markup as much as possible.
Using semantic markup such as having headers be `<h1>` and unordered lists be rendered as `<ul>` helps screen readers navigate through a page. In general, buttons should be written as `<button>` and lists should be `<li>`. While it's _possible_ to use specially styled `<span>` elements with click handlers to mock buttons, it's better for disabled users to use technologies to determine where on a page a button resides, and to interact with it, if the element appears as a button. For this reason, try to use semantic markup as much as possible.
✅ Take a look at a screen reader and [how it interacts with a web page](https://www.youtube.com/watch?v=OUDV1gqs9GA). Can you see why having non semantic markup might confuse the user?
✅ Take a look at a screen reader and [how it interacts with a web page](https://www.youtube.com/watch?v=OUDV1gqs9GA). Can you see why having non semantic markup might frustrate the user?
## The terrarium

@ -10,10 +10,10 @@
<h1>Practice your typing</h1>
<div>Click start to have a quote displayed. Type the quote as fast as you can!</div>
<h2 id="quote"></h2>
<h2 id="message"></h2>
<p id="quote"></p>
<p id="message"></p>
<div>
<input type="text" id="typed-value" />
<input type="text" aria-label="current word" id="typed-value" />
</div>
<div>
<button id="start" type="button">Start</button>

@ -85,10 +85,10 @@ Create a new file named **index.html**. Add the following HTML:
<body>
<h1>Typing game!</h1>
<p>Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!</p>
<h2 id="quote"></h2> <!-- This will display our quote -->
<h2 id="message"></h2> <!-- This will display any status messages -->
<p id="quote"></p> <!-- This will display our quote -->
<p id="message"></p> <!-- This will display any status messages -->
<div>
<input type="text" id="typed-value" /> <!-- The textbox for typing -->
<input type="text" aria-label="current word" id="typed-value" /> <!-- The textbox for typing -->
<button type="button" id="start">Start</button> <!-- To start the game -->
</div>
<script src="script.js"></script>

@ -46,12 +46,12 @@ Browser extensions are fun to develop, too. They tend to manage a finite number
Before you start building, take a look at the process of building and deploying a browser extension. While each browser varies a bit in how they manage this task, the process is similar on Chrome and Firefox to this example on Edge:
![install a browser extension](images/install-on-edge.png)
![screenshot of the Edge browser showing the open edge://extensions page and open settings menu](images/install-on-edge.png)
In essence, the process will be:
- build your extension using `npm build`
- navigate in the browser to the extensions pane using the `...` icon on the top right
- navigate in the browser to the extensions pane using the "Settings and more" button (the `...` icon) on the top right
- if it's a new installation, choose `load unpacked` to upload a fresh extension from its build folder (in our case it is `/dist`)
- or, click `reload` if you are reloading the already-installed extension
@ -87,11 +87,11 @@ src
This extension has two views. One to gather the API key and region code:
![extension form](images/1.png)
![screenshot of the completed extension open in a browser, displaying a form with inputs for region name and API key.](images/1.png)
And the second to display the region's carbon usage:
![carbon usage](images/2.png)
![screenshot of the completed extension displaying values for carbon usage and fossil fuel percentage for the US-NEISO region.](images/2.png)
Let's start by building the HTML for the form and styling it with CSS.
@ -103,12 +103,12 @@ In the `/dist` folder, you will build a form and a result area. In the `index.ht
<h2>New? Add your Information</h2>
</div>
<div>
<label>Region Name</label>
<input type="text" required class="region-name" />
<label for="region">Region Name</label>
<input type="text" id="region" required class="region-name" />
</div>
<div>
<label>Your API Key from tmrow</label>
<input type="text" required class="api-key" />
<label for="api">Your API Key from tmrow</label>
<input type="text" id="api" required class="api-key" />
</div>
<button class="search-btn">Submit</button>
</form>

@ -16,7 +16,7 @@ It remains to manage some background tasks, including refreshing the color of th
The topic of how to make your web sites blazingly fast on all kinds of devices, for all kinds of users, in all kinds of situations, is unsurprisingly vast. Here are some points to keep in mind as you build either a standard web project or a browser extension.
The first thing you need to do to ensure that your site is running efficiently is to gather data about its performance. The first place to do this is in the developer tools of your web browser. In Edge, you can select the three dots on the top right of the browser, then navigate to More Tools > Developer Tools and open the Performance tab.
The first thing you need to do to ensure that your site is running efficiently is to gather data about its performance. The first place to do this is in the developer tools of your web browser. In Edge, you can select the "Settings and more" button (the three dots icon on the top right of the browser), then navigate to More Tools > Developer Tools and open the Performance tab. You can also use the keyboard shortcuts `Ctrl` + `Shift` + `I` on Windows, or `Option` + `Command` + `I` on Mac to open developer tools.
The Performance tab contains a Profiling tool. Open a web site (try, for example, https://www.microsoft.com) and click the 'Record' button, then refresh the site. Stop the recording at any time, and you will be able to see the routines that are generated to 'script', 'render', and 'paint' the site:

@ -18,12 +18,12 @@
<h2>New? Add your Information</h2>
</div>
<div>
<label>Region Name</label>
<input type="text" required class="region-name" />
<label for="region">Region Name</label>
<input type="text" id="region" required class="region-name" />
</div>
<div>
<label>Your API Key from tmrow</label>
<input type="text" required class="api-key" />
<label for="api">Your API Key from tmrow</label>
<input type="text" id="api" required class="api-key" />
</div>
<button class="search-btn">Submit</button>
</form>

@ -1,5 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Space Game</title>
</head>
<body>
<p>Image to use:</p>
@ -11,3 +15,6 @@ Your browser does not support the HTML5 canvas tag.</canvas>
<script src="app.js">
</script>
</body>
</html>

@ -55,20 +55,20 @@ We're giving it an `id` to make it easier to locate it with JavaScript later.
> Tip: since the content of this element will be replaced, we can put in a loading message or indicator that will be shown while the app is loading.
Next, let's add below the HTML template for the login page. For now we'll only put in there a title and a section containing a button that we'll use to perform the navigation.
Next, let's add below the HTML template for the login page. For now we'll only put in there a title and a section containing a link that we'll use to perform the navigation.
```html
<template id="login">
<h1>Bank App</h1>
<section>
<button>Login</button>
<a href="/dashboard">Login</a>
</section>
</template>
```
Then we'll add another HTML template for the dashboard page. This page will contain different sections:
- A header with a title and a logout button
- A header with a title and a logout link
- The current balance of the bank account
- A list of transactions, displayed in a table
@ -76,7 +76,7 @@ Then we'll add another HTML template for the dashboard page. This page will cont
<template id="dashboard">
<header>
<h1>Bank App</h1>
<button>Logout</button>
<a href="/login">Logout</a>
</header>
<section>
Balance: 100$
@ -189,22 +189,22 @@ function updateRoute() {
}
```
Here we mapped the routes we declared to the corresponding template. You can try it that it works correctlt by changing the URL manually in your browser.
Here we mapped the routes we declared to the corresponding template. You can try it that it works correctly by changing the URL manually in your browser.
✅ What happens if you enter an unknown path in the URL? How could we solve this?
## Adding navigation
The next step for ou app is to add the possibility to navigate between pages without having to change the URL manually. This implies two things:
The next step for our app is to add the possibility to navigate between pages without having to change the URL manually. This implies two things:
1. Updating the current URL
2. Updating the displayed template based on the new URL
We already took care of the second part with the `updateRoute` function, so we have to figure out how to update the current URL.
While the HTML anchor element [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) can be used to create hyperlinks to different URLs, we can't use that here as it will make the browser reload the HTML.
We'll have to use JavaScript and more specifically the [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) that allows to update the URL and create a new entry in the browsing history, without reloading the HTML.
Instead we'll have to use JavaScript and more specifically the [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) that allows to update the URL and create a new entry in the browsing history, without reloading the HTML.
> Note: While the HTML anchor element [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) can be used on its own to create hyperlinks to different URLs, it will make the browser reload the HTML by default. It is necessary to prevent this behavior when handling routing with custom javascript, using the preventDefault() function on the click event.
### Task
@ -235,17 +235,26 @@ function updateRoute() {
If a route cannot be found, we'll now redirect to the `login` page.
Let's complete the navigation system by adding bindings to our *Login* and *Logout* buttons in the HTML.
Now let's create a function to get the URL when a link is clicked, and to prevent the browser's default link behavior:
```js
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
```
Let's complete the navigation system by adding bindings to our *Login* and *Logout* links in the HTML.
```html
<button onclick="navigate('/dashboard')">Login</button>
<a href="/dashboard" onclick="onLinkClick()">Login</a>
...
<button onclick="navigate('/login')">Logout</button>
<a href="/login" onclick="onLinkClick()">Logout</a>
```
Using the [`onclick`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick) attribute bind the `click` event to JavaScript code, here the call to the `navigate()` function.
Try clicking on these buttons, you should be now able to navigate between the different screens of your app.
Try clicking on these links, you should be now able to navigate between the different screens of your app.
✅ The `history.pushState` method is part of the HTML5 standard and implemented in [all modern browsers](https://caniuse.com/?search=pushState). If you're building a web app for older browsers, there's a trick you can use in place of this API: using a [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) before the path you can implement routing that works with regular anchor navigation and does not reload the page, as it's purpose was to create internal links within a page.
@ -274,7 +283,7 @@ Here's a refresher video on arrow functions:
[![Arrow Functions](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "Arrow Functions")
Now try to use the back and forward buttons of your browsers, and check that the displayed is correctly updated this time.
Now try to use the back and forward buttons of your browsers, and check that the displayed route is correctly updated this time.
---

@ -30,10 +30,10 @@ The `<form>` element encapsulates a section of an HTML document where the user c
There are a lot of different [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) of `<input>`, for example to create a field where the user can enter its username you can use:
```html
<input name="username" type="text">
<input id="username" name="username" type="text">
```
The `name` attribute is used to identify the control and will be used as the property name when the form data will be sent over.
The `name` attribute will be used as the property name when the form data will be sent over. The `id` attribute is used to associate a `<label>` with the form control.
> Take a look at the whole list of [`<input>` types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) and [other form controls](https://developer.mozilla.org/en-US/docs/Learn/Forms/Other_form_controls) to get an idea of all the native UI elements you can use when building your UI.
@ -55,20 +55,20 @@ Let's start by adding a form to the `login` template. We'll need a *username* fi
<section>
<h2>Login</h2>
<form id="loginForm">
<label for="user">Username</label>
<input name="user" type="text">
<label for="username">Username</label>
<input id="username" name="user" type="text">
<button>Login</button>
</form>
</section>
</template>
```
If you take a closer look, you can notice that we also added a `<label>` element here. `<label>` are used to add a caption for UI controls, such as our username field. Labels are important for the readbility of your forms, but also comes with additional benefits:
If you take a closer look, you can notice that we also added a `<label>` element here. `<label>` elements are used to add a name to UI controls, such as our username field. Labels are important for the readability of your forms, but also comes with additional benefits:
- By associating a label to a form control, it helps users using assistive technologies (like a screen reader) to understand what data they're expected to provide.
- You can click on the label to directly put focus on the associated input, making it easier to reach on touch-screen based devices.
> [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) on the web is a very important topic that's often overlooked. Thanks to [HTML5 semantic elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) it's not difficult to create accessible content if you use them properly. You can [read more about accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) to avoid common mistakes and become a responsible developer.
> [Accessibility](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/What_is_accessibility) on the web is a very important topic that's often overlooked. Thanks to [semantic HTML elements](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) it's not difficult to create accessible content if you use them properly. You can [read more about accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) to avoid common mistakes and become a responsible developer.
Now we'll add a second form for the registration, just below the previous one:
@ -77,13 +77,13 @@ Now we'll add a second form for the registration, just below the previous one:
<h2>Register</h2>
<form id="registerForm">
<label for="user">Username</label>
<input name="user" type="text">
<input id="user" name="user" type="text">
<label for="currency">Currency</label>
<input name="currency" type="text" value="$">
<input id="currency" name="currency" type="text" value="$">
<label for="description">Description</label>
<input name="description" type="text">
<input id="description" name="description" type="text">
<label for="balance">Current balance</label>
<input name="balance" type="number" value="0">
<input id="balance" name="balance" type="number" value="0">
<button>Register</button>
</form>
```
@ -121,7 +121,7 @@ Add `action` and `method` properties to the registration form:
Now try to register a new account with your name. After clicking on the *Register* button you should see something like this:
![](./images/form-post.png)
![A browser window at the address localhost:5000/api/accounts, showing a JSON string with user data](./images/form-post.png)
If everything goes well, the server should answer your request with a [JSON](https://www.json.org/json-en.html) response containing the account data that was created.
@ -239,12 +239,14 @@ Before sending data to a server it's a good practice to [validate the form data]
### Task
There are 2 required fields to create a valid new account, the username and currency, the other fields being optional. Update the form in the HTML to reflect that:
There are 2 required fields to create a valid new account, the username and currency, the other fields being optional. Update the form's HTML, using both the `required` attribute and text in the field's label to that:
```html
<input name="user" type="text" required>
<label for="user">Username (required)</label>
<input id="user" name="user" type="text" required>
...
<input name="currency" type="text" value="$" required>
<label for="currency">Currency (required)</label>
<input id="currency" name="currency" type="text" value="$" required>
```
While this particular server implementation does not enforce specific limits on the fields maximum length, it's always a good practice to define reasonable limits for any user text entry.
@ -252,11 +254,11 @@ While this particular server implementation does not enforce specific limits on
Add a `maxlength` attribute to the text fields:
```html
<input name="user" type="text" maxlength="20" required>
<input id="user" name="user" type="text" maxlength="20" required>
...
<input name="currency" type="text" value="$" maxlength="5" required>
<input id="currency" name="currency" type="text" value="$" maxlength="5" required>
...
<input name="description" type="text" maxlength="100">
<input id="description" name="description" type="text" maxlength="100">
```
Now if you press the *Register* button and a field does not respect a validation rule we defined, you should see something like this:

@ -113,7 +113,7 @@ navigate('/dashboard');
## Update HTML to display data
Now that we have the user data, we have to update the existing HTML to display it. We already now how to retrieve element from the DOM using for example `document.getElementById()`. After you have a base element, here are some APIs you can use to modify it or add child elements to it:
Now that we have the user data, we have to update the existing HTML to display it. We already know how to retrieve element from the DOM using for example `document.getElementById()`. After you have a base element, here are some APIs you can use to modify it or add child elements to it:
- Using the [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) property you can change the text of an element. Note that changing this values removes all the element's children (if there's any) and replace it with the text provided. As such, it's also an efficient method to remove all children of a given element by assigning an empty string `''` to it.
@ -157,6 +157,12 @@ Now if you try to login with an invalid account, you should see something like t
![Screenshot showing the error message displayed during login](./images/login-error.png)
Now we have error text that shows up visually, but if you try it with a screen reader you'll notice that nothing is announced. In order for text that is dynamically added to a page to be announced by screen readers, it will need to use something called a [Live Region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). Here we're going to use a specific type of live region called an alert:
```html
<div id="loginError" role="alert"></div>
```
Implement the same behavior for the `register` function errors (don't forget to update the HTML).
## Display information on the dashboard
@ -194,10 +200,10 @@ Let's start by replacing the "Balance" section in the HTML to add placeholder el
We'll also add a new section just below to display the account description:
```html
<section id="description" aria-label="Account description"></section>
<h2 id="description"></h2>
```
As there is no text label here to explain what this section is about, we use the `aria-label` attribute to give an accessibility hint. Learn more about [ARIA attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) to make sure your web apps are accessible to everyone.
Since the account description functions as a title for the content underneath it, it is marked up semantically as a heading. Learn more about how [heading structure](https://www.nomensa.com/blog/2017/how-structure-headings-web-accessibility) is important for accessibility, and take a critical look at the page to determine what else could be a heading.
Next, we'll create a new function in `app.js` to fill in the placeholder:
@ -213,7 +219,7 @@ function updateDashboard() {
}
```
First, we check that we have the account data we need before going futher. Then we use the `updateElement()` function we created earlier to update the HTML.
First, we check that we have the account data we need before going further. Then we use the `updateElement()` function we created earlier to update the HTML.
> To make the balance display prettier, we use the method [`toFixed(2)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) to force displaying the value with 2 digits after the decimal point.

@ -7,6 +7,7 @@ Using everything that you've learnt in the four previous lessons, implement an "
- Add an "Add transaction" button in the dashboard page
- Either create a new page with an HTML template, or use JavaScript to show/hide the dialog HTML without leaving the dashboard page (you can use [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) property for that, or CSS classes)
- Make sure you handle [keyboard and screen reader accessibility](https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/) for the dialog
- Implement an HTML form to receive input data
- Create JSON data from the form data and send it to the API
- Update the dashboard page with the new data

@ -1,5 +1,5 @@
{
"name": "squirrel-banking-api",
"name": "bank-api",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,

@ -23,23 +23,23 @@
<div class="login-content">
<h2 class="text-center">Login</h2>
<form id="loginForm" action="javascript:login()">
<label for="user">Username</label>
<input name="user" type="text" maxlength="20" required>
<div id="loginError" class="error"></div>
<label for="username">Username</label>
<input id="username" name="user" type="text" maxlength="20" required>
<div id="loginError" class="error" role="alert"></div>
<button>Login</button>
</form>
<p class="login-separator text-center"><span>OR</span></p>
<h2 class="text-center">Register</h2>
<form id="registerForm" action="javascript:register()">
<label for="user">Username</label>
<input name="user" type="text" maxlength="20" required>
<label for="currency">Currency</label>
<input name="currency" type="text" maxlength="5" value="$" required>
<label for="user">Username (required)</label>
<input id="user" name="user" type="text" maxlength="20" required>
<label for="currency">Currency (required)</label>
<input id="currency" name="currency" type="text" maxlength="5" value="$" required>
<label for="description">Description</label>
<input name="description" type="text" maxlength="100">
<input id="description" name="description" type="text" maxlength="100">
<label for="balance">Current balance</label>
<input name="balance" type="number" value="0">
<div id="registerError" class="error"></div>
<input id="balance" name="balance" type="number" value="0">
<div id="registerError" class="error" role="alert"></div>
<button>Register</button>
</form>
</div>
@ -52,7 +52,7 @@
<section class="dashboard-page">
<header class="dashboard-header">
<img class="dashboard-logo" src="logo.svg" alt="Squirrel Banking Logo">
<span class="dashboard-title hide-xs">Squirrel Banking</span>
<h1 class="dashboard-title hide-xs">Squirrel Banking</span>
<button onclick="logout()">Logout</button>
</header>
<div class="balance">
@ -62,7 +62,7 @@
</div>
<div class="dashboard-content">
<div class="transactions-title">
<div id="description" aria-label="Account description"></div>
<h2 id="description"></h2>
<button onclick="addTransaction()">Add transaction</button>
</div>
<table class="transactions-table" aria-label="Transactions">
@ -82,14 +82,14 @@
<h2 class="text-center">Add transaction</h2>
<form id="transactionForm" action="javascript:void(0)">
<label for="date">Date</label>
<input name="date" type="date" required>
<input id="date" name="date" type="date" required>
<label for="object">Object</label>
<input name="object" type="text" maxlength="50" required>
<input id="object" name="object" type="text" maxlength="50" required>
<label for="amount">Amount (use negative value for debit)</label>
<input name="amount" type="number" value="0" step="any" required>
<div id="transactionError" class="error"></div>
<input id="amount" name="amount" type="number" value="0" step="any" required>
<div id="transactionError" class="error" role="alert"></div>
<div class="dialog-buttons">
<button class="button-alt" formaction="javascript:cancelTransaction()" formnovalidate>Cancel</button>
<button type="button" class="button-alt" formaction="javascript:cancelTransaction()" formnovalidate>Cancel</button>
<button formaction="javascript:confirmTransaction()">OK</button>
</div>
</form>

Loading…
Cancel
Save