Errors

[accesskey] is a bad idea

Description

The [accesskey] attribute is meant to implement site specific keyboard shortcuts. This is usually a bad idea since keys might be already used by either the operating system, the browser, browser extension and even user's settings…

This is opinionated: in a perfect world, [accesskey] could be used wisely. Yup. Could be.

References

Selector

[accesskey]

Test

<a id="key" name="key" accesskey="1">Skip to this link using <kbd>1</kbd></a>

[width] & [height] attributes

Description

[width] and [height] are presentation informations. Therefore it shouldn't be used in markup, except for <img>. Use CSS instead.

References

Selector

:not(img):not(object):not(embed):not(svg):not(canvas)[width],
:not(img):not(object):not(embed):not(svg):not(canvas)[height]

Test

Damned! I feel sooo strait :(

<p width="20">Damned! I feel sooo strait :(</p>

Disabled button

Description

A <button> styled to be disabled should be disabled for real. Use [disabled] and [readonly].

References

Selector

button[class*="disabled"]:not([disabled]):not([readonly])

Test

<button class="is-disabled" type="button">To be or not to be (disabled)?</button>

Empty button

Description

A <button> should either have content or an [aria-label], [aria-labelledby] or [title]. Those attributes, if present, should not be empty.

References

Selector

button:empty:not([aria-label]):not([aria-labelledby]):not([title])

button:blank:not([aria-label]):not([aria-labelledby]):not([title])

button:-moz-only-whitespace:not([aria-label]):not([aria-labelledby]):not([title])

Test

<button type="button"></button>

Empty button attribute

Description

[aria-label], [aria-labelledby] or [title] on a <button> must not be empty.

References

Selector

button[title=""],
button[aria-label=""],
button[aria-labelledby=""]

Test

<button title="" type="button">Test</button>

Empty [href]

Description

The [href] attribute, if present, should not be empty. A link to something, right?

References

Selector

a[href=""],
a[href=" "]

Test

<a href=" ">Who am I? Where do I link?</a>

Empty link

Description

An empty link should have a label, within [title], [aria-label] or targeted by [aria-labelledby]. By the way, why would you use an empty link?

References

Selector

a:empty[title=""],
a:empty[aria-label=""],
a:empty[aria-labelledby=""],
a:empty:not([title]):not([aria-label]):not([aria-labelledby])

a:blank[title=""],
a:blank[aria-label=""],
a:blank[aria-labelledby=""],
a:blank:not([title]):not([aria-label]):not([aria-labelledby])

a:-moz-only-whitespace[title=""],
a:-moz-only-whitespace[aria-label=""],
a:-moz-only-whitespace[aria-labelledby=""],
a:-moz-only-whitespace:not([title]):not([aria-label]):not([aria-labelledby])

Test

<a href="/" class="inbl w-20" id="empty-link_code"></a>

Incorrect charset

Description

Using utf-8 character encoding is recommended by the W3C itself: it "supports many languages and can accommodate pages and forms in any mixture of those languages".

Moreover it avoids some security issues exploiting some other charsets…

References

Selector

meta[charset]:not([charset="utf-8"]),
meta[charset]:not([charset="UTF-8"]),
meta[charset="utf-8"]:not(:first-child),
meta[charset="UTF-8"]:not(:first-child)

Test

<meta charset="Windows-1252" /><span></span>

Grouping inputs

Description

Inputs with a type of radio or checkbox are usually grouped. The [name] attribute is meant to programmatically associate them, thus is needed.

References

Selector

[type="radio"]:not([name]),
[type="checkbox"]:not(:only-of-type):not([name])

Test

Options

<form action="/">
  <fieldset>
    <legend>Options</legend>
    <p>
      <label for="option-1">Option N<sup>o</sup>1</label>
      <input type="radio" id="option-1"><span></span>
    </p>
    <p>
      <label for="option-2">Option N<sup>o</sup>2</label>
      <input type="radio" id="option-2" name="options">
    </p>
  </fieldset>
</form>

Invalid [dir] attribute

Description

The [dir] attribute — used to specify the element’s text directionality — only accepts three values: rtl, ltr and auto.

References

Selector

[dir]:not([dir="rtl"]):not([dir="ltr"]):not([dir="auto"])

Test

Well, I'm kinda disoriented…

<p dir="wtf">Well, I'm kinda disoriented…</p>

Javascript events attributes

Description

Javascript event attributes (such as [onmouseover]) should not be used. Prefer either CSS pseudo-classes (:hover, :focus, :active, etc.) or JS event listeners.

References

Selector

[onafterprint], [onbeforeprint], [onbeforeunload],
[onerror], [onhaschange], [onload], [onmessage],
[onoffline], [ononline], [onpagehide], [onpageshow],
[onpopstate], [onredo], [onresize], [onstorage],
[onundo], [onunload],
[onblur], [onchage], [oncontextmenu], [onfocus],
[onformchange], [onforminput], [oninput], [oninvalid],
[onreset], [onselect], [onsubmit],
[onkeydown], [onkeypress], [onkeyup],
[onclick], [ondblclick], [ondrag], [ondragend],
[ondragenter], [ondragleave], [ondragover],
[ondragstart], [ondrop], [onmousedown], [onmousemove],
[onmouseout], [onmouseover], [onmouseup], [onmousewheel],
[onscroll],
[onabort], [oncanplay], [oncanplaythrough],
[ondurationchange], [onemptied], [onended], [onerror],
[onloadeddata], [onloadedmetadata], [onloadstart],
[onpause], [onplay], [onplaying], [onprogress],
[onratechange], [onreadystatechange], [onseeked],
[onseeking], [onstalled], [onsuspend], [ontimeupdate],
[onvolumechange], [onwaiting]

Test

Click click click
<span onclick="alert('You clicked!');">Click click click</span>

Namespaces

Description

Did you know that some characters should be avoided as first characters in class names and identifiers? Yep. Digits, two hyphens, or hyphen followed by a digit.

References

Selector

[id^="1"],
[id^="2"],
[id^="3"],
[id^="4"],
[id^="5"],
[id^="6"],
[id^="7"],
[id^="8"],
[id^="9"],
[id^="0"],
[id^="--"],
[id^="-1"],
[id^="-2"],
[id^="-3"],
[id^="-4"],
[id^="-5"],
[id^="-6"],
[id^="-7"],
[id^="-8"],
[id^="-9"],
[id^="-0"],
[class^="1"],
[class^="2"],
[class^="3"],
[class^="4"],
[class^="5"],
[class^="6"],
[class^="7"],
[class^="8"],
[class^="9"],
[class^="0"],
[class^="--"],
[class^="-1"],
[class^="-2"],
[class^="-3"],
[class^="-4"],
[class^="-5"],
[class^="-6"],
[class^="-7"],
[class^="-8"],
[class^="-9"],
[class^="-0"]

Test

Hello! My ID is 2.
<div id="2">Hello! My ID is 2.</div>

form missing an [action]

Description

<form> should do something, isnt'it? Well, [action] is meant to define what.

References

Selector

form:not([action]),
form[action=" "],
form[action=""]

Test

<form>
  <label for="input">Guess what do we do with your datas?</label>
  <input id="input" type="text" />

  <input type="submit" value="No idea, huh?"/>
</form>

Missing alternative for img

Description

An <img> must have an [alt]. Always.

References

Selector

img[alt=" "],
area[alt=" "],
input[type="image"][alt=" "],
img:not([alt]),
area:not([alt]),
input[type="image"]:not([alt])

Test

<img src="static/ffoodd.png" width="144" height="144" /><span></span>

Missing label for [role=img]

Description

[role=img] should either have [aria-label] or [aria-labelledby]. If image is decorative, please use [role=presentation] instead.

References

Selector

[role="img"]:not([aria-label]):not([aria-labelledby]),
svg[role="img"]:not([aria-label]):not([aria-labelledby])

Test

<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1" role="img">
  <rect x="400" y="100" width="400" height="200"
        fill="forestgreen" stroke="darkgreen" stroke-width="10"  />
</svg><span></span>

A label without a [for] attribute

Description

A <label> is labelling something, in theory.

References

Selector

label:not([for]),
label[for=" "],
label[for=""]

Test

<label>Guess what?</label>

Missing some kind of label

Description

How to label a field? You have a few choices: [id], [title], [aria-label] and [aria-labelledby].

References

Selector

input:not([type="button"]):not([type="submit"]):not([type="hidden"]):not([type="reset"]):not([type="image"]):not([id]):not([aria-label]):not([title]):not([aria-labelledby]),
textarea:not([id]):not([aria-label]):not([aria-labelledby]),
select:not([id]):not([aria-label]):not([aria-labelledby])

Test

<input type="text" /><span></span>

No language defined

Description

<html> must indicate to User Agents the human language used in the document.

References

Selector

html:not([lang]),
html[lang=" "],
html[lang=""]

Test

<iframe title="No language defined a11y.css test-case" src="static/no-lang.html" sandbox="allow-same-origin"></iframe>

Empty title tag

Description

The title> tag is the first thing read aloud by screen readers — to announce the page… title — and also the first thing displayed on search engines' results pages.

We also use the :blank pseudo-class for the single space case, but at this time its support is very poor. We also use the :-moz-only-whitespace pseudo-class, which is an alias of :blank.

References

Selector

title:empty
title:blank
title:-moz-only-whitespace

Test

<title></title>

Missing source for img

Description

An <img> must have an [src] or an [srcset], and it should be a valid one. Obviously.

References

Selector

img:not([src]):not([srcset]),
img[src=""],
img[src=" "],
img[src="#"],
img[src="/"],
img[srcset=""],
img[srcset=" "],
img[srcset="#"],
img[srcset="/"],
input[type="image"]:not([src]):not([srcset]),
input[type="image"][src=""],
input[type="image"][src=" "],
input[type="image"][src="#"],
input[type="image"][src="/"],
input[type="image"][srcset=""],
input[type="image"][srcset=" "],
input[type="image"][srcset="#"]
input[type="image"][srcset="/"]

Test

Missing src
<img alt="Missing src" width="144" height="144"/>

iframe without [title]

Description

<iframe> needs a [title] in order to tell the user what to expect inside the iframe.

References

Selector

iframe:not([title]),
iframe[title=" "],
iframe[title=""]

Test

<iframe src="static/no-title.html" sandbox="allow-same-origin"></iframe><span></span>

input without [type]

Description

<input> needs a [type] in order to tell the user what kind of data is wanted.

References

Selector

input:not([type]),
input[type=" "],
input[type=""]

Test

<label for="No-type">No type</label>
<input value="Whatever you want" id="No-type"/><span></span>

Missing a value

Description

How to label a [reset], [submit] or [button] type input? You still have a few choices: [value], [title], [aria-label] and [aria-labelledby].

References

Selector

input[type="reset"]:not([value]):not([title]):not([aria-label]):not([aria-labelledby]),
input[type="submit"]:not([value]):not([title]):not([aria-label]):not([aria-labelledby]),
input[type="button"]:not([value]):not([title]):not([aria-label]):not([aria-labelledby])

Test

<input type="submit" /><span></span>

Form button

Description

A <button> has a defaut [type] value of "submit", in order to send a <form>.

However outside a form, if it's intended to send a form it should mention it with one of those attributes: [form], [formaction], [formtarget].

If it doesn't submit anything, it should have the [type="button"].

References

Selector

button:not([type]):not([form]):not([formaction]):not([formtarget])

Test

<button>I just don't know what todo with myself</button>

Button not submitting

Description

If a <button>'s[type] is either "reset" or "button", it should not use the following attributes: * [formmethod]; * [formaction]; * [formtarget]; * [formenctype]; * [formnovalidate].

References

Selector

button[type="reset"][formmethod],
button[type="reset"][formaction],
button[type="reset"][formtarget],
button[type="reset"][formenctype],
button[type="reset"][formnovalidate],
button[type="button"][formmethod],
button[type="button"][formaction],
button[type="button"][formtarget],
button[type="button"][formenctype],
button[type="button"][formnovalidate]

Test

<button type="button" formmethod="GET">I have a method!</button>

optgroup without label

Description

<optgroup> needs a [label] to explain what's inside the group.

References

Selector

optgroup:not([label])

Test

<form action="/">
    <label for="optgroup-test">Oh, hey</label>
    <select id="optgroup-test">
      <optgroup label="I'm a group">
        <option value="1">I'm an option</option>
        <option value="2">I'm another option</option>
      </optgroup>
      <optgroup>
        <option value="3">I'm an option, but from another group</option>
        <option value="4">I'm another option, still from another group</option>
     </optgroup>
   </select>
</form>

[radio] outside a fieldset

Description

Inputs with a type of radio should be grouped by a parent <fieldset>, described by its <legend>.

Please note that WCAG doesn't actually require this but strongly recommends it: "where the individual label associated with each particular control provides a sufficient description, the use of the fieldset and legend elements is not required."

This test could be a warning…

References

Selector

[type="radio"]

Test

<form action="/">
  <label for="option-1">Option N<sup>o</sup>1</label>
  <input type="radio" id="option-1" name="options"><span></span>
</form>

[role=checkbox] missing state

Description

[role="spinbutton"] requires a few attributes:

References

Selector

[role="checkbox"]:not([aria-checked])

Test

Checkbox
<img src="static/ffoodd.png" alt="Checkbox" role="checkbox" width="36" height="36"/><span></span>

[role=combobox] missing [state]

Description

[aria-expanded] is required on [role="combobox"].

References

Selector

[role="combobox"]:not([aria-expanded])

Test

<input type="text" aria-label="Combobox" role="combobox" id="combobox"><span></span>

[role=scrollbar] required properties

Description

Some properties are required to comply the [role="scrollbar"] pattern:

References

Selector

[role="scrollbar"]:not([aria-controls]),
[role="scrollbar"]:not([aria-valuemin]),
[role="scrollbar"]:not([aria-valuemax]),
[role="scrollbar"]:not([aria-valuenow]),
[role="scrollbar"]:not([aria-orientation])

Test

<div role="scrollbar"></div>

[role=slider] missing attributes

Description

[role="slider"] requires a few attributes:

Also [aria-valuetext] is welcome, even though it's not required by ARIA specification.

References

Selector

[role="slider"]:not([aria-valuemin]),
[role="slider"]:not([aria-valuemax]),
[role="slider"]:not([aria-valuenow])

Test

<label for="slider">Slider</label>
<input id="slider" role="slider" type="range" /><span></span>

[role=spinbutton] missing attributes

Description

[role="spinbutton"] requires a few attributes:

References

Selector

[role="spinbutton"]:not([aria-valuemin]),
[role="spinbutton"]:not([aria-valuemax]),
[role="spinbutton"]:not([aria-valuenow])

Test

<label for="spinbutton">Spinbutton</label>
<input id="spinbutton" role="spinbutton" type="range" /><span></span>

[tabindex] > 0

Description

The [tabindex] attribute should never be greater than 0.

Reference

Selector

[tabindex]:not([tabindex="0"]):not([tabindex="-1"])

Test

<button tabindex="1" type="button">Positive tabindex is bad</button>

table used for layout

Description

<table> may be used for layout if a [role="presentation"] is added. However, semantics tags and attributes aiming to organize datas mustn't be used. Here's a list: * <th>; * <thead>; * <tfoot>; * <caption>; * [axis]; * [scope]; * [headers]; * [colgroup].

References

Selector

table[role="presentation"] th,
table[role="presentation"] thead,
table[role="presentation"] tfoot,
table[role="presentation"] caption,
table[role="presentation"] [axis],
table[role="presentation"] [scope],
table[role="presentation"] [headers],
table[role="presentation"] [colgroup]

Test

I do not mean anything!
It works
<table role="presentation">
  <caption>I do not mean anything!</caption>
  <tbody>
    <tr>
      <td colspan="2">It works</td>
    </tr>
  </tbody>
</table>

Unaccessible viewport attribute

Description

User should be able to zoom in or out the page to improve readability and comfort; this is usually allowed by the viewport <meta>.

References

Selector

meta[name="viewport"][content*="maximum-scale"],
meta[name="viewport"][content*="minimum-scale"],
meta[name="viewport"][content*="user-scalable=no"]

Test

<iframe src="static/viewport.html" sandbox="allow-same-origin" title="Wrong viewport instruction example"></iframe><span></span>