#26 HTMHell special: tasty buttons

submitted on by Manuel

The second HTMHell special focuses on another highly controversial pattern in front-end development:

🔥 the burger button. 🔥

The burger button and his tasty friends (kebab, meatball and bento) usually reveal a list of links when activated. According to our studies, these buttons are highly optimized for mouse and touch users, but lack support for keyboard and screen reader users in most cases.

After less than 1 hours of research, HTMHell presents a collection of 18 different bad practices found on real websites.

Pattern 1: the unsemantic burger Pattern 1: the unsemantic burger

<div class="burger">
<span></span>
<span></span>
<span></span>
</div>
.burger {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 30px;
height: 20px;
cursor: pointer;
}

.burger span {
height: 1px;
display: block;
background: #000;
}

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. If you need a button, use the <button> element.
  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 label.
  5. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  6. Screen readers announce: Nothing.

Other variations of this pattern Other variations of this pattern

The empty burger

<div class="burger"></div>

The all div burger

<div class="burger">
<div></div>
<div></div>
<div></div>
</div>

Pattern 2: The classic image burger Pattern 2: The classic image burger

<img class="menu" src="menu.png">

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

  1. A click event on an img triggers only on click. A click event on a button triggers on click and if the user presses the Enter or Space key.
  2. An img isn’t keyboard focusable.
  3. There’s no text alternative for the image. Screen readers may announce the filename instead.
  4. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  5. Screen readers may announce: menu.png, image.”

Pattern 3: The modern image burger Pattern 3: The modern image burger

<svg class="hamburger" viewBox="0 0 20 20" aria-labelledby="title">
<title>Open Navigation</title>
<g>
<rect y="3" width="20" height="2"></rect>
<rect y="9" width="20" height="2"></rect>
<rect y="15" width="20" height="2"></rect>
</g>
</svg>

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

  1. A click event on an svg triggers only on click. A click event on a button triggers on click and if the user presses the Enter or Space key.
  2. An svg isn’t keyboard focusable in most browsers (IE 11 is an exception).
  3. The value of aria-labelledby references elements by their id, not by tag name.
  4. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  5. Screen readers interpret svg elements differently. To get a consistent result the role="img" attribute should be present on the svg.
  6. Screen readers may announce: “Open Navigation, graphic” or “Open Navigation, group.”

Pattern 4: Almost a burger Pattern 4: Almost a burger

<button class="navbar-toggle" type="button">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>

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

  1. There’s no text label.
  2. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  3. Screen readers announce: button.
<a class="menuButton"></a>

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

  1. 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. There’s no text label.
  6. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  7. Screen readers announce: probably nothing.
<a role="button" aria-label="menu" aria-expanded="false" class="burger">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
  1. Do not change native semantics, unless you really have to.
  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. Screen readers may announce: “menu, collapsed, button”

Pattern 7: The unsemantic burger with extra bacon Pattern 7: The unsemantic burger with extra bacon

<span class="mobile-nav" aria-hidden="true">
Show nav
<span></span>
<span></span>
<span></span>
<span></span>
</span>

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

  1. Pretty much the same as Pattern 1: The unsemantic burger except that this button has a label which is good, but it also has aria-hidden="true" which means that it's completely inaccessible to screen reader users.
  2. Screen readers announce: Nothing.
<a class="toggle-link" href="javascript:">
<i class="fa fa-reorder"></i>
</a>
.fa-reorder::before {
content: "";
}

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. 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.
  4. There’s no text label.
  5. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  6. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  7. Screen readers announce: “link”.

Pattern 9: The “I have no idea what I'm doing, let's just add some attributes” burger button Pattern 9: The “I have no idea what I'm doing, let's just add some attributes” burger button

<div class="mega-toggle-block" tabindex="0">
<span class="mega-toggle-label" role="button" aria-expanded="false"> </span>
</div>

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. If you need a button, use the <button> element.
  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. The div is focusable, but it has no role or label.
  4. The span tries to mimic a button, but it's not focusable and it has no label.
  5. You don’t need the tabindex attribute if you use a button. HTML buttons are focusable by default.
  6. There’s no text label.
  7. Screen readers may announce: “group”.
<a href="#menutoggle" id="menutoggle"></a>

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

  1. A screen reader may announce this as trigram for heaven, because ☰ is the unicode character for the trigram for heaven.
  2. The purpose of the icon is decorative, it should be hidden from screen readers. Consider adding decorative images using background properties in CSS.
  3. An anchor link is not a <button>.
  4. An anchor link should not refer to itself.
  5. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  6. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  7. Screen readers announce: “trigram for heaven, link”.

Pattern 11: The bento checkbox button Pattern 11: The bento checkbox button

<div class="bento">
<input type="checkbox" id="bento">
<label for="bento"></label>
</div>
.bento label {
background-image: url(data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxwYXRoIGQ9J00zIDNoNHY0SDN6bTE0IDBoNHY0aC00em0tNyAwaDR2NGgtNHptLTcgN2g0djRIM3ptMTQgMGg0djRoLTR6bS03IDBoNHY0aC00em0tNyA3aDR2NEgzem0xNCAwaDR2NGgtNHptLTcgMGg0djRoLTR6JyBmaWxsPScjQTdBQUIyJy8+PC9zdmc+);
width: 30px;
height: 30px;
display: block;
}

.bento input {
position: absolute;
opacity: 0;
pointer-events: none;
left: -9999px;
top: -9999px;
}

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

  1. A checkbox is not a <button>.
  2. The default key events on a checkbox and a button are not the same.
  3. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  4. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  5. This “button” has no label.
  6. Screen readers may announce: “unticked tickbox”.
<ul class="offcanvas-icon">
<li>
<a href="#" class="menu-button">
<span class="dot1"></span>
<span class="dot2"></span>
<span class="dot3"></span>
<span class="dot4"></span>
<span class="dot5"></span>
<span class="dot6"></span>
<span class="dot7"></span>
<span class="dot8"></span>
<span class="dot9"></span>
</a>
</li>
</ul>

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

  1. Use <ul> to group and list related items, not as a button wrapper.
  2. An anchor link is not a <button>.
  3. CSS is quite powerful, you don't need 9 spans to display 9 dots.
  4. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  5. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  6. This “button” has no label.
  7. Screen readers may announce: “list 1 item”.

• • •

(Note: There was a click event on the link that prevented default.)

<a href="https://example.com/">• • •</a>

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

  1. A link is not a <button>.
  2. The default key events on a link and a button are not the same.
  3. If you’re not sure when to use <a> or <button>, watch The Links vs. Buttons Showdown by Marcy Sutton.
  4. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  5. This “button” has no label.
  6. Screen readers may announce: “link, bullet bullet bullet”.

Pattern 14: The kebab div button Pattern 14: The kebab div button

<div class="menu" title="Menu">
<i></i>
<i></i>
<i></i>
</div>

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

  1. Pretty much the same as Pattern 1: The unsemantic burger.
  2. 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.
  3. Screen readers may announce: nothing.

Pattern 15: The hamburger Pattern 15: The hamburger

<button class="nav__toggle-button" aria-label="Hamburger Menu">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" fill="currentColor"></path>
</svg>
</button>

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

  1. Not as bad as all the others, but the term “Hamburger” might confuse users.
  2. The “button” should indicate whether the element it controls is currently expanded or collapsed (aria-expanded="false" if collapsed).
  3. Screen readers may announce: “button, Hamburger Menu”.

Pattern 16: The chatty burger button Pattern 16: The chatty burger button

<button aria-label="All Company expand to see list of Company products and services" aria-expanded="false"></button>

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

  1. Not as bad as all the others, but the label is too long. Keep it short and simple.
  2. Screen readers may announce: “button, All Company expand to see list of Company products and services, expanded”.

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" aria-expanded="false">
Menu
</button>

  1. Text only: easy to implement and comprehensible.
  2. Screen readers may announce: Menu, 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" aria-expanded="false">
<svg aria-hidden="true"></svg>
Menu
</button>

  1. If you want to use an icon, hide it from screen readers by wrapping it in a span with aria-hidden="true".
  2. Screen readers may announce: Menu, 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" aria-expanded="false">
<span class="sr-only">Menu</span>
<svg aria-hidden="true"></svg>
</button>
.sr-only {
position: absolute;
white-space: nowrap;
width: 1px;
height: 1px;
overflow: hidden;
border: 0;
padding: 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: Menu, button”.

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

<button type="button" aria-label="Menu" aria-expanded="false">
<svg aria-hidden="true"></svg>
</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: Menu, button”.

Wanna learn accessibility testing? Wanna learn accessibility testing?

Are you interested in learning how to discover these accessibility issues and a lot more? Then join me for a Smashing Magazine workshop starting on November 4th.

Deep Dive On Accessibility Testing
Workshop, 5×2.5h + Q&A
Thu & Fri, November 4–18 202109:00 – 11:30 AM PT (Pacific, US) • 18:00 – 20:30 CET (Europe)

Use this link for a special -15% discount.