When we build apps, we usually focus on delivery rather than covering other aspects like Accessibility and Testing (but testing will be covered in another post). Today, I want to talk about Accessibility. Most of the time, we think accessibility is just to help people with disabilities use our product, but it actually improves the experience for all users.
In December, I spent some time learning about Accessibility, and I highly recommend taking these free courses.
Learn Accessibility: https://web.dev/learn/accessibility
Build more accessible Angular apps https://codelabs.developers.google.com/angular-a11y#3
This weekend, I took time to test the skills I learned by building a small form that includes accessibility from the start, including form setup and validation. Let's do it!
I wanted to create the "Letter to Santa" form where my son can send his name, email, and a message to Santa Claus, but I want to build an accessible form with clear and accessible validations and a notification when the message has been successfully sent.
Finally, I got a form like this:
The main goal of the form is to collect information from users. If forms are not accessible, we exclude a significant portion of people, such as users with visual or motor impairments, or those affected by a temporary accident or with their hands busy.
I started improving accessibility by using semantic HTML elements like <header>
, <main>
, and <footer>
, and organizing content hierarchically with headings (<h1>
to <h6>
). This helps screen readers navigate the page correctly and also improves SEO.
<header>
<h1>Write Your Letter to Santa</h1>
</header>
<main>
<h2>Fill Out the Form</h2>
</main>
<footer>
</footer>
The main goal of the form is to collect information from users. If forms are not accessible, we exclude a significant portion of people, such as users with visual or motor impairments, or those affected by a temporary accident or with their hands busy.
I started improving accessibility by using semantic HTML elements like <header>
, <main>
, and <footer>
, and organizing content hierarchically with headings (<h1>
to <h6>
). This helps screen readers navigate the page correctly and also improves SEO.
<label for="name">Name</label>
<input id="name" name="name" required type="text" />
But forms are more than just input and label; they need validations or error messages that should only appear when relevant, such as after a field has been interacted with (touched
) or when the form is submitted. But how do I notify the user with a message?
First, the notifications should be announced by screen readers using aria-live
with a visually clear design for users who do not use assistive technologies and avoid unnecessarily interrupting the user.
There are different ARIA roles to use with the notifications for specific scenarios, using role="alert"
for critical or high-priority messages, such as errors. It automatically interrupts the user and announces the content immediately, or role="status"
for non-critical updates, such as confirmations or status changes.
<div class="error" role="alert">
<p>Name is required.</p>
</div>
But how are those messages announced? You have assertive
, which stops the current message, or polite
, which waits to announce by screen readers without interruption.
@if ((nameCtrl.invalid && nameCtrl.touched) || (nameCtrl.invalid && isSubmitted)) {
<div class="error" role="alert">
<small>Name is required.</small>
</div>
}
For example, aria-live="polite"
waits to announce the content after completing its current task. It is perfect for notifications like when a message has been sent:
@if (messageSent) {
<div class="notifications" role="status" aria-live="polite">
<p>{{ message }}</p>
</div>
}
But what happens when I want content only for screen readers? We can create a utility class like .visually-hidden
for content that is only meant to be accessible to screen readers and not visible to sighted users.
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
It helps to ensure that essential information is conveyed to users relying on screen readers without affecting the visual layout.
<span class="visually-hidden">Error:</span>
<p>Name is required.</p>
Another key point is also is the color, with adequate contrast, the text should have sufficient contrast with its background according but most easy way is using the color-contrast-checker extension.
input.ng-invalid.ng-touched {
border-color: #e74c3c;
background-color: #fdecea;
}
Perfect we can build our form ready with accesibility!! Hey hold a second ? What happend if tomorrow @Jörgen de Groot add a new feature , how I can control he don’t break the accesibility ?
The es-lint
is your friend, first add using the schematics:
ng add @angular-eslint/schematics
The es-lint provide a set of accessiblity rules like accessibility-label-has-associated-control
to ensures that every label
element has an associated form control (similar to accessibility-label-for
but stricter).
<!-- Incorrect -->
<label>Email</label>
<!-- Correct -->
<label for="email">Email</label>
<input id="email" type="email">
or accessibility-table-scope
, requires data table headers (<th>
) to have a scope
attribute, improving navigation for screen readers.
<!-- Incorrect -->
<th>Name</th>
<!-- Correct -->
<th scope="col">Name</th>
Feel free to read more about accesibility es-lint, add these rules to the file (.eslintrc.json
), adjusting the severity ("warn"
, "error"
, etc.) as needed:
{
"overrides": [
{
"files": ["*.component.html"],
"extends": ["plugin:@angular-eslint/template/recommended"],
"rules": {
"@angular-eslint/template/accessibility-elements-content": "error",
"@angular-eslint/template/accessibility-valid-aria": "error",
"@angular-eslint/template/accessibility-label-has-associated-control": "warn",
"@angular-eslint/template/accessibility-table-scope": "error",
"@angular-eslint/template/accessibility-role-supports-aria": "warn",
"@angular-eslint/template/accessibility-click-events-have-key-events": "warn",
"@angular-eslint/template/accessibility-no-positive-tabindex": "error",
"@angular-eslint/template/accessibility-media-has-caption": "warn",
"@angular-eslint/template/accessibility-valid-tabindex": "error",
"@angular-eslint/template/accessibility-input-label": "warn"
"@angular-eslint/template/accessibility-role-has-required-aria": "error"
}
}
]
}
After run npm run lint
tada!! we have a linter for our code!
Recap
I hope when you start to create your next project, keep in mind these tips to simplify your accesibility and take care to improve our apps for all users.
Top comments (0)