HTML lists are boring. They don’t do much, so we don’t really think about them despite how widely used they are. And we’re still able to do the same things we’ve always done to customize them, like removing markers, reversing order, and making custom counters.
There are, however, a few “newer” things — including dangers — to know when using lists. The dangers are mostly minor, but way more common than you might think. We’ll get to those, plus some new stuff we can do with lists, and even new ways to approach old solutions.
To clarify, these are the HTML elements we’re talking about:
- Ordered lists
<ol>
- Unordered lists
<ul>
- Description lists
<dl>
- Interactive lists
<menu>
Ordered lists, unordered lists, and interactive lists contain list items (<li>
) which are displayed according to what kind of list we’re dealing with. An ordered list (<ol>
) displays numbers next to list items. Unordered lists (<ul>
) and menu elements (<menu>
) displays bullet points next to list items. We call these “list markers” and they can even be styled using the ::marker pseudo-element. Description lists use description terms (<dt>
) and description details (<dd>
) instead of <li>
and don’t have list markers. They‘re supposed to be used to display metadata and glossaries, but I can’t say I’ve ever seen them in the wild.
Let’s start off with the easy stuff — how to correctly (at least in my opinion) reset list styles. After that, we’ll take a look at a couple of accessibility issues before shining a light on the elusive <menu>
element, which you may be surprised to learn… is actually a type of list, too!
Resetting list styles
Browsers automatically apply their own User Agent styles to help with the visual structure of lists right out of the box. That can be great! But if we want to start with a blank slate free of styling opinions, then we have to reset those styles first.
For example, we can remove the markers next to list items pretty easily. Nothing new here:
/* Zap all list markers! */
ol, ul, menu {
list-style: none;
}
But modern CSS has new ways to help us target specific list instances. Let’s say we want to clear markers from all lists, except if those lists appear in long-form content, like an article. If we combine the powers of newer CSS pseudo-class functions :where()
and :not()
, we can isolate those instances and allow the markers in those cases:
/* Where there are lists that are not articles where there are lists... */
:where(ol, ul, menu):not(article :where(ol, ul, menu)) {
list-style: none;
}
Why use :where()
instead of :is()
? The specificity of :where()
is always zero, whereas :is()
takes the specificity of the most specific element in its list of selectors. So, using :where()
is a less forceful way of overriding things and can be easily overridden itself.
UA styles also apply padding to space a list item’s content from its marker. Again, that’s a pretty nice affordance right out of the box in some cases, but if we’re already removing the list markers like we did above, then we may as well wipe out that padding too. This is another case for :where()
:
:where(ol, ul, menu) {
padding-left: 0; /* or padding-inline-start */
}
OK, that’s going to prevent marker-less list items from appearing to float in space. But we sort of tossed out the baby with the bathwater and removed the padding in all instances, including the ones we previously isolated in an <article>
. So, now those lists with markers sorta hang off the edge of the content box.
Notice that UA styles apply an extra 40px
to the <menu>
element.
So what we want to do is prevent the list markers from “hanging” outside the container. We can fix that with the list-style-position
property:
Or not… maybe it comes down to stylistic preference?
Newer accessibility concerns with lists
Unfortunately, there are a couple of accessibility concerns when it comes to lists — even in these more modern times. One concern is a result of applying list-style: none;
as we did when resetting UA styles.
In a nutshell, Safari does not read ordered and unordered lists styled with list-style: none
as actual lists, like when navigating content with a screen reader. In other words, removing the markers also removes the list’s semantic meaning. The fix for this fix it to apply an ARIA list
role on the list and a listitem
role to the list items so screen readers will pick them up:
<ol style="list-style: none;" role="list">
<li role="listItem">...</li>
<li role="listItem">...</li>
<li role="listItem">...</li>
</ol>
<ul style="list-style: none;" role="list">
<li role="listItem">...</li>
<li role="listItem">...</li>
<li role="listItem">...</li>
</ul>
Oddly, Safari considers this to be a feature rather than a bug. Basically, users would report that screen readers were announcing too many lists (because developers tend to overuse them), so now, only those with role="list"
are announced by screen readers, which actually isn’t that odd after all. Scott O’Hara has a detailed rundown of how it all went down.
A second accessibility concern isn’t one of our own making (hooray!). So, you know how you’re supposed to add an aria-label
to <section>
elements without headings? Well, it sometimes makes sense to do the same with a list that doesn’t contain a heading element that helps describe the list.
<!-- This list is somewhat described by the heading -->
<section>
<h2>Grocery list</h2>
<ol role="list">
<!-- ... -->
</ol>
</section>
<!-- This list is described by the aria-label -->
<ol role="list" aria-label="Grocery list">
<!-- ... -->
</ol>
You absolutely don’t have to use either method. Using a heading or an ARIA label is just added context, not a requirement — be sure to test your websites with screen readers and do what offers the best user experience for the situation.
In somewhat related news, Eric Bailey wrote up an excellent piece on why and how he considers aria-label
to be a code smell.
<menu>
is a list, too?
Wait, OK, so, you’re likely wondering about all of the <menu>
elements that I’ve been slipping into the code examples. It’s actually super simple; menus are unordered lists except that they’re meant for interactive items. They’re even exposed to the accessibility tree as unordered lists.
In the early days of the semantic web, I mistakenly believed that menus were like <nav>
s before believing that they were for context menus (or “toolbars” as the spec says) because that’s what early versions of the HTML spec said. (MDN has an interesting write-up on all of the deprecated stuff related to <menu>
if you’re at all interested.)
Today, however, this is the semantic way to use menus:
<menu aria-label="Share article">
<li><button>Email</button></li>
<li><button>Twitter</button></li>
<li><button>Facebook</button></li>
</menu>
Personally, I think there are some good use-cases for <menu>
. That last example shows a list of social sharing buttons wrapped up in a labeled <menu>
element, the notable aspect being that the “Share article” label contributes a significant amount of context that helps describe what the buttons do.
Are menus absolutely necessary? No. Are they HTML landmarks? Definitely not. But they’re there if you enjoy fewer <div>
s and you feel like the component could use an aria-label
for additional context.
Anything else?
Yes, there’s also the aforementioned <dl>
(description list) element, however, MDN doesn’t seem to consider them lists in the same way — it’s a list of groups containing terms — and I can’t say that I’ve really seen them in use. According to MDN, they’re supposed to be used for metadata, glossaries, and other types of key-value pairs. I would just avoid them on the grounds that all screen readers announce them differently.
But let’s not end things on a negative note. Here’s a list of super cool things you can do with lists:
- Create a table of contents
- Visualize a timeline of events
- Convert a list into a horizontal comma-separated list
- Style the marker (I personally don’t like this, but it’s still kind of cool)
I have been using
<dl>
list for 20 years now. It’s common for professional devs to utilize the full catalog of HTML tags, and not stick to 4-5 knows tags and care not to expand the mind about the vast beautiful range of semantic possibilities.<dl>
lists are handy for lists with titles per-item, such asFAQ
sections for example.I’ve also used
<dl>
to mark-up tabs before. Not sure how a11y compliant it was but it felt pretty semantic.For FAQ I have used and as some answers to questions are too long to let them be all opened in the browser when the page is displayed
Wait, so what’s the difference between these two examples in a practical sense? I suspect I’m like a lot of people who didn’t know
<menu>
existed and I’m still trying to grok it.Thank you for writing this article!
Just a thought…
Anything can be a list.
If we undo
ul
,ol
andli
built in styles anyway, why not use custom elements?Pragmatically, which would would require more styling? Which would be more DRY for reusable components?
Feels like the dependency argument in micro. The moment we use a dependency is the moment we limit ourselves to it’s limits and (eventually, if not immediately) spend more time coding around it.
In the practical sense, not much.
<menu>
is more semantic when using buttons (vs. links) because it’s supposed to indicate a list of interactive elements. In an ideal world, search engines would ignore them (or maybe they do already) and screen readers would announce them differently and consistently. IMO, it doesn’t hurt to use them.Yeah Daniel it certainly “feels” like
<menu>
is more semantic in some cases but I was a bit thrown by the article’s line saying “They’re even exposed to the accessibility tree as unordered lists”. I was thinking “does the browser make a distinction between these things at all then?”.Sorry Glenn, I should have been more clear.
<menu>
s are unordered but not the same as<ul>
s, if that makes sense? Screen readers announce them as menus, unlike<dl>
s which are rather inconsistent among screen readers.I see. Thanks for the clarification Daniel.
I’ve been thinking dl might make sense for metadata 1 key to many values pairs like tags: foo bar, category: Baz, author: gaz
Not sure though.
I feel like menu is maybe only useful in a non app website for forms.
I feel like 90% of the time aria-labelledby is preferable to aria-label
Here’s a
<dl>
or two in the wild for you:https://atomicarchive.com/resources/documents/effects/glasstone-dolan/glossary.html
https://meyerweb.com/eric/css/
Both great examples!
Realy nice tutorial.
Thankyou
Most of the websites I visit to learn new things about web dev, the <dl> stands for definition list and for definition term. What I want to know is if they’re synonymous with description list and term?
Officially they’re called description lists.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl
I think definition list is just a popular misconception.
The spec for
<dl>
changed. It used to bedefinition list
when it was meant for glossaries. After the use cases expanded, it was changed todescription list
.Some notes (this is a 4 item bullet list but CSS-Tricks styles eat the bullets, ironically for this post):
Probably do not add ARIA list roles for Safari/VO users unless the users themselves indicate they want them.
Probably do not add
aria-label
to<section>
elements unless you want a<section>
to be a region, in which case usearia-labelledby
to reference the heading (which you should have added).The
<menu>
element is exposed as an unordered list; no different accessibility APIs, no different platform APIs, nothing different in the browser. You can safely ignore<menu>
unless as a developer you prefer it.Description lists are indeed announced differently across browser and screen reader pairings, but
<dl>
support is generally good (with Safari being the outlier), even if you may not like how it is supported.“We can fix that with the list-style-position property” .. If you use
list-style-position: inside;
, the content of the list items expands all the way to the left on multiple lines. It would be much better to just apply the padding on these and leave the position on default in my opinion.