HTML: The Bad Parts

by Mayank published on

You've probably heard statements along the lines of "HTML is already accessible by default" or "You don't need to reinvent this perfectly fine HTML control". I consider these to be more of general claims rather than universal truths. It's extremely important for web developers to recognize gaps in the platform. To that end, I've decided to collect a few instances where HTML falls short, through accessibility issues or usability issues.

This is not a comprehensive list, and it does not include gaps in ARIA. I wanted to find a balance between widely known issues and more frequently encountered (but lesser known) ones, while making sure to include some things that we take for granted. In each section, I will include its severity, alternate suggestions, and links where you can find more detailed information.

<select multiple>

Let's start with an easy one. The multiple attribute on <select> should pretty much never be used. It's like the polar opposite of single <select>, where instead of universal familiarity, it has universal unfamiliarity. Perhaps its only saving grace is that I have yet to encounter this attribute in any codebase.

To quote a screen-reader user (from Sarah Higley's excellent article linked below):

This is a broken listbox. This is entirely broken, I don’t see any way of doing anything with this.

Impact: High. There is a good chance most of your users will not know how to navigate this monstrous control.

What to use instead: A list of checkboxes is a totally fine alternative; or you could build a custom multi-selectable listbox.

Further reading: <select> your poison part 2: test all the things (article by Sarah Higley).

<i> (for icon)

Let's do another easy one to warm up. The <i> element is semantically insignificant, so it's not necessarily “bad” if used for regular text content. More frequently though, it is used for icon fonts.

I wouldn't even normally include this in the list, since this is more of a developer error than a platform issue. However, it is 2023 and I still regularly encounter the <i> element being used with something like FontAwesome. This is problematic mainly because it involves using a web font to visually substitute text characters (which may have one meaning) with icons (which are completely unrelated to the text characters). As a result, the text characters, even though they are generated in CSS, are part of the page's content and will be included in the accessibility tree by default. In addition to that, it looks bad, breaks often (and in hilarious ways 🐴), and leads to other accessibility issues. You can learn more about all of these issues in Tyler Sticka's post linked below.

Impact: Low to medium. You could make it less inaccessible by excluding it from the accessibility tree (using aria-hidden) and providing a text alternative (using visually hidden text), but it will still remain problematic.

What to use instead: SVGs! They are well supported, and better in many important ways. Unlike icon fonts, SVGs are properly designed to display graphics, so they will not result in unexpected text being added to the accessibility tree. There are tons of different ways to use them too, so you can have your pick, whether you like inlining, using sprites, or keeping it within CSS (using mask).

When using <svg> elements, a reliable pattern is to use aria-hidden alongside an additional text alternative (which may be visually hidden if needed). For example, the markup for a button might look like this:

<button>
<svg aria-hidden="true"><use href="#heart" /></svg>
<span>Favorite</span>
</button>

Further reading: Seriously, Don't Use Icon Fonts (article by Tyler Sticka)

<ul> and <ol>

I can't make a list (heh!) of problematic HTML and not include lists in there.

I'm not going to say “don't use lists”; lists are very useful in many scenarios. But developers have a tendency to overuse lists, often just to remove list styles anyway. Sometimes we may even slap an ARIA role on it (thus overriding the list semantics), or worse, add non-<li> children for styling purposes or otherwise (thus producing invalid markup). In all of these scenarios, we are losing any benefit provided by list elements, and potentially introducing new issues.

Safari has noticed this overuse of lists, dubbing it "list-itis", and has started removing list semantics in certain cases; lists that are outside a nav and are styled with list-style: none are not considered to be lists by Safari. While this change was surely done with good intentions, it has the unfortunate effect of making even legitimately styled lists purely presentational.

In addition to all of this, ordered lists have their own oddities. Browsers consider the item numbers to be "presentational", so when selecting and copying a list, the numbers are not included in the plain-text clipboard entry. This breaks user expectations, especially when the numbers are important, as in the case of legal documents.

Impact: Low to medium. I think it's totally fine to let Safari/VoiceOver do its thing; in fact, their users are likely used to it and may even expect an almost list-free web. The clipboard issue with ordered lists can be quite serious, but there are workarounds.

What to use instead: If you really need to preserve list semantics, role="list" can be added to <ul>. If you're already using a different role, then plain <div>s should work fine. <dl> is another potential alternative for key-value pairs.

<ul role="list">
<li></li>
<li></li>
<li></li>
</ul>

If the list item numbers are important, consider using unordered lists where the numbers are part of the text content.

Further reading: "Fixing" Lists (article by Scott O'Hara), Twitter thread by Apple's James Craig, and "I Blame the W3C's HTML Standard for Ordered Lists" (article by Sibylla Bostoniensis).

title attribute

The title attribute is the one that frustrates me the most, particularly because of its long history. It is so bad that the HTML spec explicitly warns against using it. And yet I've seen this attribute used in almost every codebase I've worked with.

Some of the many issues with title include:

  • Sighted keyboard users have absolutely no way to access the title content.
  • Touch-screen users also cannot access the title content.
  • The default tooltip styling cannot be changed in any way.
  • The trigger needs to be hovered for an arbitrarily long delay before it appears.
  • The tooltip cannot be dismissed without moving the pointer, and therefore can obscure other parts of the page.
  • The tooltip cannot be hovered over, meaning the mouse needs to keep hovering the trigger element. As a result, the tooltip could become obscured by the mouse pointer, its text will not be selectable, and screen-magnifier users may find it impossible to view it.
  • The text inside the tooltip does not scale with the user's zoom level.
  • Long text inside the tooltip cannot be made to wrap; in some browsers, it will become as wide as the screen itself (even escaping the browser window bounds) before it starts wrapping.
  • There is no way to indicate if the title content should be part of the element's accessible name or description (or neither).
  • Screen-readers do not consistently announce the title content on all elements.

Impact: High. By relying on this attribute, you are making the intentional choice of leaving out a large percentage of your users.

What to use instead: In many cases, including the text as part of the element's content can suffice; this text could be visually-hidden if necessary. In other cases, aria-labelledby or aria-describedby will work better than title. One exception is iframes, which need to be labeled using title.

In almost all cases, a custom tooltip, in addition to the techniques described above, would also work better than title. However, tooltips come with their own set of problems, so an even better solution would be to redesign the interface to avoid needing tooltips altogether. For example, you could always show the text or use a disclosure pattern.

Further reading: The Trials and Tribulations of the Title Attribute (article by Scott O'Hara), and Using the HTML title attribute (article by Steve Faulkner).

<datalist>

At first, the datalist element sounds like a promising alternative to custom comboboxes. The more you use it though, the more disappointed you'll find yourself.

  • This element has very questionable default styling.
    • On Chrome desktop, it appears as a weirdly offset popover that can drastically shift its position while the user is typing; at the same time, it becomes detached from the input when the user scrolls away.
    • On Chrome Android, it appears above the virtual keyboard (rather than on the page) and only in portrait mode.
  • Its appearance cannot be customized at all. It does not even respect the color-scheme.
  • It does not respect the user's font size or zoom level.
  • It generally has good support on desktop screen-readers, but on mobile it requires navigating to the very end of the page to interact with any options.
  • It does not work on Firefox Android at all!

Impact: Medium to high. At best, this element can be useful as an optional hint for input fields that would still be perfectly usable without the added help from autocompleting options.

What to use instead: <select> or a custom combobox. 🤷

Further reading: Under-Engineered Comboboxen? (article by Adrian Roselli)

<input placeholder>

Placeholder text is intended to help the user by providing a hint when an input is empty. In practice, it often ends up doing the opposite.

Placeholder text is usually prone to color contrast issues. If you try fixing color contrast, it may end up looking indistinguishable from real input values. If you put important information in it (like password requirements, or god forbid, the label itself), the input value will hide this important information as soon as the user starts typing.

Impact: Medium to high. At best, it may be ignored. At worst, it may frustrate your users and impact your revenue.

What to use instead: Nothing? A prominently visible label may be sufficient. If you do need hint text, consider placing it outside the input, and associating it using aria-describedby.

<label for="phone">Phone number</label>
<input id="phone" aria-describedby="phone-hint" />
<div id="phone-hint">Example: 123-456-7890</div>

Further reading: Don't Use The Placeholder Attribute (article by Eric Bailey)

<input type=number>

Let's cut to the chase: number inputs will increment/decrement when using scroll wheel or gesture or arrow keys, making it extremely prone to accidents. Yikes!

And that's in addition to its other accessibility, consistency, and styling issues.

  • Some browsers will silently discard non-numeric user input without any feedback.
  • Other browsers will allow entering arbitrary numbers but fire events with empty values, thus losing user-entered data.
  • The "spin" buttons are extremely small and difficult to click.
  • The "spin" buttons cannot be styled (beyond simple color scheme).

Impact: High. The scrolling issue alone is enough to make this a dealbreaker, as users could enter the wrong number without even realizing.

What to use instead: <input inputmode="numeric" pattern="[0-9]*">. The inputmode attribute helps show a nicer keyboard on touchscreen devices, and the pattern attribute helps with validation.

Further reading: Why the GOV.UK Design System team changed the input type for numbers

<input type=date>

Native date pickers have many browser inconsistencies, with some parts of them being inaccessible.

  • On Chrome desktop, the calendar button cannot be tabbed to.
  • On Safari desktop, the date picker cannot be closed using keyboard.
  • On Safari desktop, the date picker is minuscule and cannot be scaled up.
  • On Chrome and Firefox Android, the date input cannot be typed into, forcing the use of the clunky calendar UI even if the date is familiar to the user.

In all environments, the date format is shown as placeholder text which, as we've already established above, is problematic. The browser determines the date format, and it may choose something strange (like the US date format), leading to unnecessary confusion.

Impact: High. These inconsistencies are not just minor differences, they introduce real barriers to access.

What to use instead: Honestly, use simple text inputs, or even selects in some cases.

You can also use separate inputs for date/month/year and group them with a fieldset. This fieldset could even be styled to look like one contiguous form field. If you choose to hide the input labels though, I would also suggest showing the date format outside the inputs (as part of the legend or below the inputs).

Custom date pickers are notoriously hard to get right, so if you choose to one, it should be optional and in addition to the text inputs.

Further reading watching: What makes an accessible date picker? Is it even possible? (talk by Russ Weakley)

<menu>

Just don't. Whatever you expect this element does… it probably doesn't. The <menu> element was originally intended to be a "context menu" but it never got anywhere, and now it is replaced by <ul>.

Impact: Low. <menu> is simply remapped to <ul>.

What to use instead: Because the term “menu” is so overloaded, it depends on what you're trying to accomplish. Some alternatives include:

  • <ul>(or role="list") when you have a list.
  • role="menu" when you have a desktop-like menu.
  • role="listbox" when you have something resembling a custom <select>.
  • <dialog> or role="dialog" when you have a generic popover (perhaps with a role="list" inside).

Do note that some of these roles require custom JavaScript interactions to be wired up.

Further reading: Be Careful Using 'Menu' (article by Adrian Roselli)

<button disabled>

Disabled buttons are strangely overused, perhaps due to their familiarity. Two common scenarios for disabled buttons include:

  • when an action is temporarily unavailable, perhaps due to requiring some prerequisite task (such as selecting an item or filling a form correctly).
  • when submitting a form, to prevent duplicate submissions.

However, the disabled attribute does more than just prevent clicks. It also prevents hover and focus capabilities. This has many negative ramifications. Disabled buttons cannot show tooltips (which may be important for explaining why the button is disabled). Keyboard users cannot Tab to disabled buttons. If a button suddenly becomes disabled (e.g. when submitting a form), focus could get lost and create a confusing experience. Additionally, disabled buttons are exempt from color contrast requirements, increasing the likelihood that it will have poor contrast.

Impact: High. Disabled buttons selectively serve a small percentage of your user-base (and not very well either).

What to use instead: Use aria-disabled="true" and manually disable clicks. If possible though, consider reworking the design to avoid needing disabled buttons altogether.

Further reading: Making Disabled Buttons More Inclusive (article by Sandrina Pereira)

<video>

The native video player still has many accessibility and usability issues. For keyboard users, focus is a recurring issue across browsers, because not all controls can be reached, and the controls disappear once the video starts playing. For screen reader users, the experience is lacking in some areas, especially for more "advanced" features like picture-in-picture mode.

I highly recommend checking out Adrian's article below for a deep-dive on support across various browsers and screen-reader pairings.

Impact: Medium to high.

What to use instead: Unsure. Maybe a custom video player, if it's built with accessibility in mind. Regardless of what you use, please include captions and a transcript, and provide a way to download the video for offline viewing in the user's player of choice. Oh and please for the love of your users, do not use autoplay.

Further reading: Browser Video Players Review (article by Adrian Roselli)

Links on the web can have a non-http protocol like mailto: or tel:. These protocols are commonly used as a supposed way to help the users (or spammers) send an email or dial a number. However, this makes a very bold assumption is that the user is visiting the webpage on the same device that will be used to send the email or dial a number and wants to use the default application to do so. This can lead to frustration when the user is just looking for a way to copy the number or dial it from a different device. To make it worse, the actual address/number often gets hidden underneath the link, instead displaying something useless like "Email me".

Impact: Low to medium. It's confusing and annoying, but not the end of world I suppose. Users will (reluctantly) find workarounds.

What to use instead: Just show the actual email address or phone number. It is trivial to copy or manually transfer it over to the desired device. Additionally, many devices will automatically display a prompt when selecting text that can be recognized as an email/number.

Further reading: tel:me.about.it. (article by Brian Kardell)


Wrapping up

In closing, I just want to reiterate that accessibility is not as simple as "just use HTML". Even with best intentions and pristine HTML source, we may end up with inaccessible UIs if we don't do our due diligence.

I'm sure this list will look different (hopefully shorter) 10 years from now. As more people start to take interest in these things, it will create more awareness. It will push standards bodies and browser vendors to improve the platform and get us closer to a more "accessible by default" web. You can personally help move the needle by opening new bugs or by commenting on existing ones:

About Mayank

Mayank is a design engineer who cares deeply about accessibility and inclusivity. They enjoy working with modern web technologies to solve complex problems. In their free time, they run their mouth on their personal blog.

Website/Blog: mayank.co