Errors

Attribute should not contain whitespace

Description

Some HTML attributes should not contain any whitespace —namely [id], [lang] and map[name].

Reference

Selector

[id*=" "],
[lang*=" "],
map[name*=" "]

Test

This is my beautiful element with an ID

<p id="my id">This is my beautiful element with an ID</p>

[tabindex] > 0

Description

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

References

Selector

[tabindex]:not([tabindex="0"], [tabindex^="-"])

Test

<button tabindex="1" type="button">Positive tabindex is bad</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

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

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], [aria-label], [aria-labelledby])

Test

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

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] without [aria-hidden=true] should either have [aria-label] or [aria-labelledby]. If image is decorative, please use [role=presentation] instead.

References

Selector

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

Test

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

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], [srcset]),
img[src=""],
img[src=" "],
img[src="#"],
img[src="/"],
img[srcset=""],
img[srcset=" "],
img[srcset="#"],
img[srcset="/"],
input[type="image"]:not([src], [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"/>

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>

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"], [type="submit"], [type="hidden"], [type="reset"], [type="image"], [id], [aria-label], [title], [aria-labelledby]),
textarea:not([id], [aria-label], [aria-labelledby]),
select:not([id], [aria-label], [aria-labelledby])

Test

<input type="text" /><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], [title], [aria-label], [aria-labelledby]),
input[type="submit"]:not([value], [title], [aria-label], [aria-labelledby]),
input[type="button"]:not([value], [title], [aria-label], [aria-labelledby])

Test

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

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], [aria-labelledby], [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>

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], [form], [formaction], [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:

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>

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], [readonly])

Test

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

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>

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>

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 srcdoc="<!DOCTYPE html><title>Missing [title]</title>"></iframe><span></span>

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>

No valid 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 example" srcdoc="<!DOCTYPE html>
  <html>
    <head>
      <meta charset='utf-8'/>
      <title>Je ne parle pas Français</title>
      <style>html{font:.75rem sans-serif}</style>
      <link rel='stylesheet' type='text/css' href='https://ffoodd.github.io/a11y.css/css/a11y-en_errors-only.css'>
    </head>
    <body>
      <ul>
        <li>Speak French?</li>
        <li>Habla usted francés?</li>
        <li>Sprechen Sie Französisch?</li>
    </ul>">
</iframe>

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:

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>

[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, object, embed, svg, canvas)[width],
:not(img, object, embed, svg, canvas)[height]

Test

Damned! I feel sooo strait :(

<p width="20">Damned! I feel sooo strait :(</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>

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 could use the :blank pseudo-class, but at this time its support is very poor. We could also use the :-moz-only-whitespace pseudo-class, but support is limited to Firefox and thus could invalidate our selectors stack…

References

Selector

title:empty

Test

<title></title>

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 title="Wrong viewport instruction example" srcdoc="<!DOCTYPE html>
  <html lang='en'>
    <head>
      <meta charset='utf-8'/>
      <title>Wrong viewport instruction</title>
      <meta name='viewport' content='user-scalable=no'/>
      <style>html{font:.75rem sans-serif}</style>
      <link rel='stylesheet' type='text/css' href='https://ffoodd.github.io/a11y.css/css/a11y-en_errors-only.css'>
    </head>
    <body>
      <h1>Wrong <code>viewport</code> instruction</h1>">
</iframe>

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

Test

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

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])

Test

<iframe title="Late charset example" srcdoc="<!DOCTYPE html>
  <html lang=en>
    <head>
      <title>Oups !</title>
      <style>html{font:.75rem sans-serif}</style>
      <link rel=stylesheet type=text/css href=https://ffoodd.github.io/a11y.css/css/a11y-en_errors-only.css>
    </head>
    <body>
      <h1>How am I encoded, buddy?</h1>">
</iframe>

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"], [dir="ltr"], [dir="auto"])

Test

Well, I'm kinda disoriented…

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

[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

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

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, [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>

[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


Trying within a fieldset, shouldn't match

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

  <br>

  <fieldset>
    <legend>Trying within a fieldset, shouldn't match</legend>
    <p>
      <label for="option-2">Option N<sup>o</sup>2</label>
      <input type="radio" id="option-2" name="options"><span></span>
    </p>
  </fieldset>
</form>

[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>

[role=checkbox] missing state

Description

[role="checkbox"] requires an attribute: [aria-checked]

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>

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>