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>

A label with an empty [for] attribute

Description

A <label> with a [for] attribute should label something with an [id] attribute, obviously.

References

Selector

label[for=""],
label[for=" "]

Test

<label for=" ">Guess what?</label>

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:

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"]):not([charset="UTF-8"])

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>

Charset should come first

Description

As explained on the Mozilla Developer Network:

The <meta> element declaring the encoding must be inside the <head> element and within the first 1024 bytes of the HTML, as some browsers only look at those bytes before choosing an encoding.

It's also meant to prevent an old security exploit using UTF-7. Declaring the charset before the <title> tag is the simplest way to cover it.

References

Selector

head > *:first-child:not([charset]):not([http-equiv="Content-Type"][content*="text/html; charset"])

Test

Charset isn't first child
<title>Charset isn't first child</title>
<meta charset="utf-8" /><span></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>

Nested interactive elements

Description

An interactive element should not be contained in another interactive element (e.g. <a> in <button>).

References

Selector

a a[href],
button a[href],
a audio[controls],
button audio[controls],
a video[controls],
button video[controls],
a button,
button button,
a details,
button details,
a embed,
button embed,
a iframe,
button iframe,
a img[usemap],
button img[usemap],
a label,
button label,
a select,
button select,
a textarea,
button textarea,
a input[type]:not([hidden]),
button input[type]:not([hidden]),
form form,
label label,
meter meter,
progress progress

Test

<a href="https://www.ffoodd.fr">
  <button type=button">Oh wait, what should I trigger?</button>
</a>

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>

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"] colgroup,
table[role="presentation"] [axis],
table[role="presentation"] [scope],
table[role="presentation"] [headers]

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>