Make it accessible.
Make it fancy.
Make sure the fancy doesn’t break accessibility.
— Morten Rand-Hendriksen
TLDR; See how accessibility helps improve all user’s experience. WAI’s “bad” demo is a great way to learn about accessibility by example.
Table of Contents
POUR Principles
Ensure content is always:
- Perceivable. Available to sight, hearing, and/or touch.
- Operable. Usable forms, controls, and navigation.
- Understandable. UI shows intent.
- Robust. ie. Assistive technologies friendly.
Usability
- Support keyboard navigation, and interaction as much as on the mouse.
- Show user location when using keyboard navigation.
- Add links to opt-out keyboard navigation on certain sections. eg.
navbars.
Structured Pages
NOTE Code examples are in Slim to keep the focus on the elements, attributes, and values rather than HTML’s verbosity.
- Use HTML5 semantically.
- Set descriptive
titlemeta tag for user agents. sectiongroups elements into units of information.- Use
divprimarily for:- Grouping what should be styled together
- Enforcing layouts, instead of being their foundation.
headerfor page headlines, and occasionally forsections.navfor major navigation links for a site. Often aheaderorasidechild.footerfor info about a page, orsectioneg. copyright.asideIndependent info related to the surrounding content. eg. sidebar, pull-quotes.sectionContent-focused element for grouping independently consumable parts of a page.articleSelf-contained content that could be consumed independently from a page.- Style headings(
hX), don’t skip them. - Set text tone with
strongto add / show importance.emfor emphasis. ie. tone.
- Id content in different languages with the
langattribute. - Style text inline.
markto highlight content.smallfor small print.abbrfor abbreviations.spanfor any other styling. eg. icon fonts. (idoesn’t stand for icon.)
Beware that some elements, such as abbr we’ll need to set complimentary attributes to make full use of them.
<abbr title="Accessible Rich Internet Applications">ARIA</abbr>
Although, the title property on abbr is well supported on by screen readers, we shouldn’t rely on it for any other elements.
In general, a page’s header and headings should make a nice table of contents. (Comments beginning with // refer to the line below.)
doctype 5
html lang="en" / Language codes linked in the references
head
meta charset="UTF-8" / Help display text properly
// Use only when site is responsive
meta name="viewport" content="width=device-width, initial-scale=1.0"
// Tell Microsoft's browers we expect them to behave like others
meta http-equiv="X-UA-Compatible" content="IE=Edge"
title Short & Meaningful for screen readers
meta name="description" content="This text shows up in search engines"
body
header role="banner" / Role for main header only
nav role="navigation"
main role="main"
section
article
header
h1 Title
form role="form"
/ ... forms are covered below
p
| This is how we'll
em.tone-class
| add
strong
| text
| in
span#template-id
| Slim
aside role="complementary"
form role="search" / Role for search boxes only
footer role="contentinfo" / Role for main footer only
Since landmark roles are essential for keyboard navigation we included the most basic set in the code sample above.
Although there is apparent redundancy for main and form it’s actually meant to reinforcing semantics. For instance, main role is a non-obtrusive alternative to “skip to main content” links
. It’s not exclusive of the main html element.
Images
- Only add relevant alternative info. eg. don’t describe logos.
- The
altattribute only works onimg,area,input. - Use
aria-labelto add alternative info anywhere. - Set purely decorative images as CSS backgrounds.
- Use
aria-hiddenattribute for decorations such as icon fonts. - When referring to images from the text we can use
figure, and optionallyfigcaptionto describe the diagram.
alt text
alt text is a description of an image for those who can’t see it. Hence,
- Describe the image.
- don’t SEO it.
- no place for attributions.
alttext depends on context.- Keep it short.
- Don’t start with ‘image of’, or ‘photo of’. Screen readers already say it.
- End with a period.
Even when no alt text is needed we need to use it with an empty attribute for:
- repeated images. eg. profile pictures in a feed.
- icons with text labels.
- linked images with caption. ie. newspaper style.
Invoke purely decorative images, such as backgrounds, in CSS to avoid using alt altogether.
SVG images can be called using an img tag, in which case we can use the alt text to describe it. When, for reasons, we need to embed the image directly into and svg element we’ll need to use role="img", and aria-label="alt text here" to make it accessible.
Tables
- Use CSS grid instead of tables for tabular layouts.
- Avoid nesting tables.
captiontables to associate them with their descriptions.- Set header scope.
- Use proportional (
remorem) rather than absolute sizing. thead,tfoot, andtbodygroup cells semantically.tr,th,tdmake data navigation easier.
table
caption
| Shopping List
thead
tr
th scope="col"
| Description
th scope="col"
| Price
th scope="col"
| Quantity
tbody
tr
th scope="row"
| Phone
td 5
td 1
tr
th scope="row"
| Computer
td 8
td 2
tfoot
tr
th scope="row"
| Total
td 21
td 3
Forms
- Ensure they’re keyboard accessible.
- Organize and label fields clearly.
fieldset,section, anddivdelimit form space.- Associated elements have matching
id, andfor. - Associate
labels extend selection area. - Use
fieldsetwithlegendfor specificity. - Avoid
<select multiple>menus. - Avoid empty:
valueattributes.buttoncontents.
- Don’t replace
labels withplaceholders.
A few ways we can associate form elements:
form id="pizza-order" role="form"
fieldset
legend
| Toppings:
input id="ham" type="checkbox" name="toppings" value="ham"
label for="ham"
| Ham
input id="pepperoni" type="checkbox" name="toppings" value="pepperoni"
label for="pepperoni"
| Pepperoni
input id="mushrooms" type="checkbox" name="toppings" value="mushrooms"
label for="mushrooms"
| Mushrooms
input id="olives" type="checkbox" name="toppings" value="olives"
label for="olives"
| Olives
label for="city"
| Choose your delivery city
select id="city" name="delivery-city"
optgroup label="Asia"
option value="HK"
| Hong Kong
option value="TK"
| Tokyo
optgroup label="Europe"
option value="AM"
| Amsterdam
option value="BA"
| Barcelona
optgroup label="North America"
option value="MX"
| Mexico City
option value="NY"
| New York
optgroup label="South America"
option value="SP"
| Sao Paulo
input for="pizza-order" type="submit" name="pizza-order" value="Order"
input for="pizza-order" type="reset" name="cancel" value="Cancel"
Inputs
Note: Most browsers support autocomplete for various input elements. While this may arguably be good from an accessibility standpoint, keep in mind it isn’t from a privacy, and security perspective.
Common Attributes
Mandatory form fields:
input[type="text" required]
Types
Check out W3 School’s input types list for a the complete set of input types and attributes.
Text & Patterns
We can add simple validation patterns to type="text" inputs. Most common browser style failure to match the require pattern.
input[
type="text"
pattern="^(\d{3}-?\d{4})$"
]
These validations are meant to improve usability, not security. If security is a concern we must always do it at the server level.
Some common patterns are:
// Generic text. No special characters
input[
type="text"
pattern="[a-zA-Z0-9]+"
]
// Username. 2-20 characters long
input[
type="username"
pattern="^[a-zA-Z][a-zA-Z0-9-_\.]{1,20}$"
]
// Password. Upper, lower cases, numbers, special characters, min 9 chars
input[
type="password"
pattern="(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$"
]
For security reasons, never style type="password" other than when missing in register forms.
Web Linking
Web pages are linked through a elements. We can tell user agents such as bots, browsers, and screen readers, how a website relates to ours through the rel attribute. Here’s a basic list. (hrefs omitted for simplicity)
Access
a rel="contents" / as in TOC
a rel="home"
a rel="first"
a rel="last"
a rel="prev"
a rel="glossary"
a rel="help"
a rel="alternate" / page's alt delivery mechanism eg. Atom feed.
Attributions
a rel="author"
a rel="license content-license" / link to data license
a rel="content-repository" / link to data store
a rel="code-license"
a rel="code-repository"
Privacy
a rel="noopener noreferrer"
a rel="nofollow"
a rel="privacy-policy"
a rel="terms-of-service"
The first setting,noopener noreferrer, helps protect users from tabnabbing attacks without damaging the site’s SEO:
- Use
nofollowfor:- Sites whose content we usually don’t refer to.
- Non-endorsed sites.
- Paid referrals.
Main Nav
Considering semantic HTML, landmark roles, and web linking, a simple main navigation bar could look like:
nav role="navigation"
ul
li
a[
rel="noopener noreferrer"
href="https://avoid.empty.href.com"
]
| 'a' elements shouldn't be empty, ever.
li
a[
rel="nofollow noopener noreferrer"
href="https://non.endorsed.page.com"
]
| Profile in unrelated site
li
a[
rel="noopener noreferrer"
href="/about"
]
| noopener noreferrer are for visitors' benefit
The use of ul, and li elements is merely as an example. Nowadays, is easy to control navigation bar’s layout with CSS grid or flexbox, if needed. That’s beyond the scope of this cheat sheet, though.
Fancy Meets Accessible
Dynamic Content
Hide anything visually, as well as from screen readers, and other user agents:
.a { visibility: hidden }
.b { display: hidden }
.c { display: none }
Avoid hidding HTML elements through the hidden attribute. It creates a dependency on ECMAScript (JS), which not all user might have access to, specially on mobiles.
Hide anything only visually by:
.tucked-away {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
.camouflaged {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
opacity:0;
}
.unapparent {
position: absolute;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
margin: -1px;
padding: 0;
}
- Ensure hidden content announces itself upon display.
- When necessary, ensure dynamic content is keyboard accessible.
Colors
- Avoid using only color to convey information ie. single green/red dot.
- Set the contrast ratio between elements so everyone can distinguish them.
- Rely on contrast ratio calculators.
Online contrast ratio checkers:
- Colorable. Applies color combo.
- Contrast Ratio Displays colors side by side.
Accessible color combinations:
- Accessible Color Pallete Builder
- Colors A11y
- Colors. For the actual color hex numbers.
- BassCss’ Color Combos
Resources
Tools & Code
- WAI “bad” demo
- Interactive POUR Recommendations
- Accessible Color Pallete Builder
- WAVE browser extensions
- Tota11y.js
- Tenon Accessibility Analyzer
- The A11y Project
- CSS Grid
- Flexbox
- 18F Accessibility Checklist
- Colorable.
- Contrast Ratio Calculator
- Contrast Checker.
- Colors.
- Colors A11y.
- BassCss’ Color Combos
References
- Creating Accessible Forms
- WebAIM’s WCAG 2.0 Checklist
- the-accessibility-cheatsheet
- Creating Accessible Tables
- HTML For Icon Font Usage
- Text For Screen Readers Only
- autofill-what-web-devs-should-know-but-dont
- W3 School’s input types list
- Vox Accessibility Guidelines
- Aria Web Standards
- Using Aria
- Aria Landmark Roles
- Accessibility Best Practices
- HTML Language Code Reference
- Understanding Microsoft’s meta-tag
- Web Linking (RFC5988bis) Updated version of RFC5988 (referenced at the top as obsolete).
- Creating Accessible Menus
- tabnabbing attacks
- rel=”noreferrer noopener” SEO Issues?
- Link Relation Types
- HTML5 Link Type Extensions
- 7 Habits of Highly Effective Media Queries
- Media Queries: Width vs Device Width
- Hiding Content for Accessibility
- alt text
- MDN Learn Accessibility
- W3 Web Accessibility Tutorial
- Tips on Developing for Web Accessibility
- Using the HTML title attribute