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.
nav
bars.
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
title
meta tag for user agents. section
groups elements into units of information.- Use
div
primarily for:- Grouping what should be styled together
- Enforcing layouts, instead of being their foundation.
header
for page headlines, and occasionally forsection
s.nav
for major navigation links for a site. Often aheader
oraside
child.footer
for info about a page, orsection
eg. copyright.aside
Independent info related to the surrounding content. eg. sidebar, pull-quotes.section
Content-focused element for grouping independently consumable parts of a page.article
Self-contained content that could be consumed independently from a page.- Style headings(
h
X), don’t skip them. - Set text tone with
strong
to add / show importance.em
for emphasis. ie. tone.
- Id content in different languages with the
lang
attribute. - Style text inline.
mark
to highlight content.small
for small print.abbr
for abbreviations.span
for any other styling. eg. icon fonts. (i
doesn’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
alt
attribute only works onimg
,area
,input
. - Use
aria-label
to add alternative info anywhere. - Set purely decorative images as CSS backgrounds.
- Use
aria-hidden
attribute for decorations such as icon fonts. - When referring to images from the text we can use
figure
, and optionallyfigcaption
to 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.
alt
text 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.
caption
tables to associate them with their descriptions.- Set header scope.
- Use proportional (
rem
orem
) rather than absolute sizing. thead
,tfoot
, andtbody
group cells semantically.tr
,th
,td
make 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
, anddiv
delimit form space.- Associated elements have matching
id
, andfor
. - Associate
label
s extend selection area. - Use
fieldset
withlegend
for specificity. - Avoid
<select multiple>
menus. - Avoid empty:
value
attributes.button
contents.
- Don’t replace
label
s withplaceholder
s.
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. (href
s 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
nofollow
for:- 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