HTMHell supports Black Lives Matter. ✊🏾 Code is always political.

#20 HTMHell special: close buttons

submitted on by Manuel

This first HTMHell special inspects one of the most complicated and most controversial patterns in front-end development:

🔥 the close button. 🔥

In modals, ads, and other overlays you often find a button with a close symbol that allows users, or at least some of them, to close the overlay. This functionality is often limited to mouse users, because most implementations of close buttons suck.

After less than 2 hours of research, HTMHell presents a collection of 11 different bad practices.

Pattern 1: div and background image Pattern 1: div and background image

<div class="close"></div>
.close::after {
background: url("close.png");
content: "";
}

Issues and how to fix them Issues and how to fix them

  1. The <div> element is an element of last resort, for when no other element is suitable. Use of the <div> element instead of more appropriate elements leads to poor accessibility.
  2. A click event on a div triggers only on click. A click event on a button triggers on click and if the user presses the Enter or Space key.
  3. A div isn’t keyboard focusable.
  4. There’s no text alternative for the background image.
  5. Screen readers announce: Nothing.

Pattern 2: div and icon Pattern 2: div and icon

<div class="close">

</div>

Issues and how to fix them Issues and how to fix them

  1. ✕ doesn’t represent close or crossed out, it’s the multiplication sign, like in 2 ✕ (times) 2. Don’t use it for close buttons.
  2. See Pattern 1 for details about the <div>.
  3. Screen readers may announce: something like “multiplication x” or “times”.

Pattern 3: Font Awesome icons Pattern 3: Font Awesome icons

<div class="close">
<i class="fas fa-times"></i>
</div>
.fa-times::before {
content: '\f00d';
}

Issues and how to fix them Issues and how to fix them

  1. Screen readers may announce CSS generated content.Footnote1
  2. Font Awesome advises to hide icons semantically by settings aria-hidden="true" on the <i> element.
  3. Font Awesome adds Unicode content via the ::before pseudo element. Assistive technology may announce the Unicode equivalent, which in this specific example would be “times” since fa-times is not a cross but a multiplication sign. (Please note: Talkback and VoiceOver didn’t announce anything in this example.)
  4. The i element represents a span of text in an alternate voice or mood, or otherwise offset from the normal prose in a manner indicating a different quality of text.Footnote2 If you just want italic text, use font-style: italic; in CSS.
  5. See Pattern 1 for details about the <div>.
  6. Screen readers may announce: “times”.
<a href="#" class="close">
</a>
a::after {
font-size: 28px;
display: block;
content: "×";
}

Issues and how to fix them Issues and how to fix them

  1. If the a element has an href attribute, it represents a link to another resource like a page or a PDF document.
  2. The purpose of the element in this example is to trigger an action on the same page with JavaScript. The button element with the type button is more suitable because it has no default behaviour and it’s designed to trigger actions on user input.
  3. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  4. Screen readers may announce CSS generated content.Footnote1 ✕ doesn’t represent close or crossed out, it’s the multiplication sign, like in 2 ✕ (times) 2. Don’t use it for close buttons.
  5. Screen readers may announce: “link, times”.
<a href="#" class="close">
Close
</a>
.close::before {
content: "\e028";
}

Issues and how to fix them Issues and how to fix them

  1. Nice try, but it’s still a link and not a button.
  2. See Pattern 4 for details about <a> and CSS generated content.
  3. Screen readers may announce: “link, times close”.
<a class="close" onclick="close()">×</a>

Issues and how to fix them Issues and how to fix them

  1. Another nice try, but a link without href is still not a button.
  2. If the <a> element has no href attribute, then the element represents a placeholder for where a link might otherwise have been placed.Footnote2
  3. If you’re adding a click event to a placeholder link, you probably don’t want to use a placeholder link, but an actual link with an href attribute or a <button>, depending on what's happening on click.
  4. Placeholder links aren't focusable.
  5. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  6. Screen readers may announce: “times, clickable.”
 <a onclick="close();">
<img src="close.png">
</a>

Issues and how to fix them Issues and how to fix them

  1. There’s no text alternative for the image. Screen readers may announce the filename instead.
  2. See Pattern 6 for details about placeholder links.
  3. Screen readers may announce: “close.png, image.”

Pattern 8: Radio button Pattern 8: Radio button

 <label class="close" for="close">
<svg></svg>
</label>
<input id="close" type="radio">
[type="radio"] {
display: none;
}

Issues and how to fix them Issues and how to fix them

  1. When accessibility advocates say “Just use button” they mean the button element, not radio buttons.
  2. Radio buttons are used in radio button groups describing a set of related options.
  3. The SVG has no text alternative. Read Creating Accessible SVGs by Carie Fisher to learn how to make SVGs accessible.
  4. display: none on the input make the label inaccessible, too.
  5. Screen readers may announce: Nothing.

Pattern 9: Button with icon Pattern 9: Button with icon

<button class="close" type="button">
×
</button>

Issues and how to fix them Issues and how to fix them

  1. ✕ doesn’t represent close or crossed out, it’s the multiplication sign, like in 2 ✕ (times) 2. Don’t use it for close buttons.
  2. Screen readers may announce: “times, button”.

Pattern 10: Button with svg Pattern 10: Button with svg

<button class="close">
<svg></svg>
</button>

Issues and how to fix them Issues and how to fix them

  1. The SVG has no text alternative. Read Creating Accessible SVGs by Carie Fisher to learn how to make SVGs accessible.
  2. Screen readers may announce: “button”.

Pattern 11: The good ol' X Pattern 11: The good ol' X

<div role="button" tabindex="0">X</div>

Issues and how to fix them Issues and how to fix them

  1. Setting button semantics explicitly using the role attribute isn’t necessary, there’s an element for that (button).
  2. You don’t need the tabindex attribute if you use a button. HTML buttons are focusable by default.
  3. See Pattern 1 for details about the <div>.
  4. The letter X is not a close icon.
  5. Screen readers may announce: “X, button”.
Using "x" for your close buttons is like using salt in your coffee cause it looks the same as sugar.
Max Böck

You can find all bad practices on CodePen.

Alternatives Alternatives

Solution 1: A button with visible text and no icon. Solution 1: A button with visible text and no icon.

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

  1. Text only: easy to implement and comprehensible.
  2. Screen readers may announce: “Close, button”.

Solution 2: A button with visible text and only visually accessible icon. Solution 2: A button with visible text and only visually accessible icon.

<button type="button">
Close
<span aria-hidden="true">×</span>
</button>
  1. If you really have to use the times icon, hide it from screen readers by wrapping it in a span with aria-hidden="true".
  2. Screen readers may announce: “Close, button”.

Solution 3: A button with hidden text and only visually accessible icon. Solution 3: A button with hidden text and only visually accessible icon.

<button type="button">
<span class="sr-only">Close</span>
<span aria-hidden="true">×</span>
</button>
.sr-only {
position: absolute;
white-space: nowrap;
width: 1px;
height: 1px;
overflow: hidden;
border: 0;
padding: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
margin: -1px;
}
  1. Unfortunately, there’s no native way of hiding content only visually.
    The .sr-only class makes sure that content is visually hidden but still accessible to screen reader users.
  2. Screen readers may announce: “Close, button”.

Solution 4: A button with hidden text and only visually accessible icon. Solution 4: A button with hidden text and only visually accessible icon.

<button type="button" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
  1. If you don’t want to show text on screen, provide a text alternative for your icon or SVG by adding aria-label to the button.
  2. Screen readers may announce: “Close, button”.

Solution 5: Font Awesome Solution 5: Font Awesome

For the sake of completeness, a close button with a Font Awesome icon.

<button type="button" class="close" aria-label="Close">
<span class="fa fa-times" aria-hidden="true"></span>
</button>

General remarks General remarks

Sometimes it makes sense to use more descriptive labels like “Close dialog”, “Close gallery”, or “Close ad”.

If you use third party solutions for modals, dialogs, etc., please check how they’ve been implemented before you add them to your site. Don’t rely on others for code quality and accessibility.

You can find all close button best practices on CodePen.