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
<div class="close"></div>
.close::after {
background: url("close.png");
content: "";
}
Issues and how to fix them
- 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. - A click event on a
div
triggers only on click. A click event on abutton
triggers on click and if the user presses the Enter or Space key. - A
div
isn’t keyboard focusable. - There’s no text alternative for the background image.
- Screen readers announce: Nothing.
Pattern 2: div and icon
<div class="close">
âś•
</div>
Issues and how to fix them
- ✕ doesn’t represent close or crossed out, it’s the multiplication sign, like in 2 ✕ (times) 2. Don’t use it for close buttons.
- See Pattern 1 for details about the
<div>
. - Screen readers may announce: something like “multiplication x” or “times”.
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
- Screen readers may announce CSS generated content.Footnote1
- Font Awesome advises to hide icons semantically by settings
aria-hidden="true"
on the<i>
element. - 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.) - 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, usefont-style: italic;
in CSS. - See Pattern 1 for details about the
<div>
. - Screen readers may announce: “times”.
Pattern 4: A close link
<a href="#" class="close">
</a>
a::after {
font-size: 28px;
display: block;
content: "Ă—";
}
Issues and how to fix them
- If the
a
element has anhref
attribute, it represents a link to another resource like a page or a PDF document. - 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. - If you’re not sure when to use
<a>
or<button>
, watch The Links vs. Buttons Showdown by Marcy Sutton. - 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.
- Screen readers may announce: “link, times”.
Pattern 5: A close link with text
<a href="#" class="close">
Close
</a>
.close::before {
content: "\e028";
}
Issues and how to fix them
- Nice try, but it’s still a link and not a button.
- See Pattern 4 for details about
<a>
and CSS generated content. - Screen readers may announce: “link, times close”.
Pattern 6: A close link without the href
attribute
<a class="close" onclick="close()">Ă—</a>
Issues and how to fix them
- Another nice try, but a link without
href
is still not a button. - If the
<a>
element has nohref
attribute, then the element represents a placeholder for where a link might otherwise have been placed.Footnote2 - 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. - Placeholder links aren't focusable.
- If you’re not sure when to use
<a>
or<button>
, watch The Links vs. Buttons Showdown by Marcy Sutton. - Screen readers may announce: “times, clickable.”
Pattern 7: Placeholder link and img
<a onclick="close();">
<img src="close.png">
</a>
Issues and how to fix them
- There’s no text alternative for the image. Screen readers may announce the filename instead.
- See Pattern 6 for details about placeholder links.
- Screen readers may announce: “close.png, image.”
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
- When accessibility advocates say “Just use button” they mean the
button
element, not radio buttons. - Radio buttons are used in radio button groups describing a set of related options.
- The SVG has no text alternative. Read Creating Accessible SVGs by Carie Fisher to learn how to make SVGs accessible.
display: none
on theinput
make thelabel
inaccessible, too.- Screen readers may announce: Nothing.
Pattern 9: Button with icon
<button class="close" type="button">
Ă—
</button>
Issues and how to fix them
- ✕ doesn’t represent close or crossed out, it’s the multiplication sign, like in 2 ✕ (times) 2. Don’t use it for close buttons.
- Screen readers may announce: “times, button”.
Pattern 10: Button with svg
<button class="close">
<svg> … </svg>
</button>
Issues and how to fix them
- The SVG has no text alternative. Read Creating Accessible SVGs by Carie Fisher to learn how to make SVGs accessible.
- Screen readers may announce: “button”.
Pattern 11: The good ol' X
<div role="button" tabindex="0">X</div>
Issues and how to fix them
- Setting button semantics explicitly using the
role
attribute isn’t necessary, there’s an element for that (button
). - You don’t need the
tabindex
attribute if you use abutton
. HTML buttons are focusable by default. - See Pattern 1 for details about the
<div>
. - The letter X is not a close icon.
- 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
Solution 1: A button with visible text and no icon.
<button type="button">
Close
</button>
- Text only: easy to implement and comprehensible.
- Screen readers may announce: “Close, button”.
Solution 2: A button with visible text and only visually accessible icon.
<button type="button">
Close
<span aria-hidden="true">Ă—</span>
</button>
- If you really have to use the times icon, hide it from screen readers by wrapping it in a
span
witharia-hidden="true"
. - Screen readers may announce: “Close, button”.
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;
}
- 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. - Screen readers may announce: “Close, button”.
Solution 4: A button with hidden text and only visually accessible icon.
<button type="button" aria-label="Close">
<span aria-hidden="true">Ă—</span>
</button>
- 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. - Screen readers may announce: “Close, button”.
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
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.