The very recent1 <details> tag (and its <summary> companion)
lets us create collapsible content in plain HTML. In plain HTML! Though
you're likely to want to add some CSS styling to make it look remotely
palatable, JavaScript may very well be kept at bay entirely. Let's explore a
bit and see what can be done with it.
Obligatory TL;DR
A menu bar in ~20 lines of CSS, fully navigable by keyboard, Web
crawlers and screen
readers, without a hint of
JavaScript in sight.
I'll section this article in three parts, where we'll go over the templating aspects, the styling quirks and possibilities, and finally a practical example, in that order.
The DOM markup logic
In the relevant MDN
docs,
the <details> element is introduced as such:
The
<details>HTML element creates a disclosure widget in which information is visible only when the widget is toggled into an open state. A summary or label must be provided using the<summary>element.
Consider the following HTML snippet:
This is the collapsible content. It can include text,
images—anything you want.
I do my best to render it below as "default" as it gets, except for some background colour and centring, to keep the article comfortable:
Yeah, that's pretty bare—but, crucially, functional. You may click on the label to collapse and expand the piece of content it's encapsulating.
The <summary> in the <details>
Something's iffy, however: just like the
devil, wasn't the
<summary> prescribed to belong as well in the <details>? The MDN quote
above mentions that a label must be provided using the <summary> element,
yet we didn't include one in the previous snippet.
To me today, that label reads "Details". While being fairly standard across Web browsers in an English-language context (as far as I could tell), that value still is left to the discretion of the user agent (i.e., the client's Web browser).
I went and tracked down the specification
documents
at the WHATWG to ascertain either way, and sure enough, it is fairly
ambiguous, in stating both that the Content Model of <details> shall include:
One
<summary>element followed by Flow Content.
Yet also (and conversely), that:
The first
<summary>element child of the element, if any, represents the summary or legend of the details. If there is no child summary element, the user agent should provide its own legend (e.g. "Details").
In any case, there isn't a single example there of a <details> element without
its <summary> "firstborn" (> summary:first-child), and for what it's worth,
the W3C's Markup Validation Service
is aligned to the WHATWG specification in disallowing the omission of the
<summary> child element in <details>.
I conclude that we'd do well to always include a <summary> element as
the first child of our <details>, to ensure consistent behaviour across user
agents—we'll likely wish to have some control over the label anyway.
In summary, you may customise the label of a <details> element by providing it
with a <summary> child:
Woof!
🐶
Woof!
🐶
The [open] attribute and the [name] linking
I lingered far too long on the <summary> element, so I'll keep this one
straight to the point. There's only a couple more things to know about the
DOM structure:
- an expanded
<details>element gets an additional[open]attribute, and - several distinct
<details>sharing the same[name]attribute get their expanded/collapsed states linked together, so that opening any forcibly closes the others.
<!-- note this <details> being [open] -->
Quack!
🦆
Moo!
🐮
Meow!
🐱
[open] attribute makes the 🦆 start out expandedQuack!
🦆Moo!
🐮Meow!
🐱That's it! There isn't much more to the <details> from the perspective of its
mark-up. Let's get on now with the matter that is becoming most pressing, I'm
sure: its styling possibilities.
The styling guidebook
First thing first: you don't need to keep that odd triangle marker that
the user agent places before the <summary> text. You can style it away
or replace it with something more fitting to your design, by targeting its
::marker pseudo-element, in either of its two possible states, via the :open
pseudo-class on the parent <details>:
Peekaboo?
I see you!
Peekaboo?
I see you!In practice, support for the :open
pseudo-class on the <details> and
for the ::marker pseudo-element on
<summary> is not quite universal yet: Safari is the notable bad apple on that
front—ha!
-
To circumvent the lacking support for
:open, you can simply target the[open]attribute on<details>instead, and -
to play nice with the poor
::markersupport, the WebKit-based browsers (read: Safari) can target it with::-webkit-details-markerinstead (which will only work with those Browsers).The
::-webkit-details-markerselector only supports tweaking very few properties, such ascolorandfont-*, which is regrettably largely inadequate for many purposes—notably that of proper internationalisation. In short, Safari's support for remotely serious uses of the::markerpseudo-selector, for<li>just as for<summary>is not there yet. However, in September 2024, theGoodFirstBugkeyword was added to the tracker for that issue upstream at WebKit, so you may have at it ;)
In any case, bending that marker to your specific will would often entail
disabling it entirely and working rather with your own ::before and
::after additions—just as you would with <li> elements in lists:
Subject: 🎉💰 URGENT: You've Won $1,000,000 Today! 💰🎉
Congratulations, lucky friend!
Click HERE to claim your prize NOW! 💷💶💵💴
Subject: 🎉💰 URGENT: You've Won $1,000,000 Today! 💰🎉
Congratulations, lucky friend!
Click HERE to claim your prize NOW! 💷💶💵💴In any case, this is still workable, without even forgoing Safari users! Without further ado, onto the pièce de résistance we go.
A practical drop-down example
Let us, arrive at the crown jewel of this article: a drop-down menu bar,
entirely in HTML and CSS:
File
Open
Save
Exit
Undo
Redo
Font style
Normal
Bold
Italic
Alignment
Left
Center
Right
Yep, it really is that simple. Some ~20 lines of CSS, and a stunningly
sane HTML template. Here's a live rendering of the above—with adjustments
to the colours to fit this blog's scheme:
You could consider adding
user-select: noneto the<summary>elements to prevent accidental text selection when the user may be frantically clicking around, especially in these sorts demonstrations.
Of course this menu I suggest would rely on buttons that cannot work without JavaScript, but you'll note that I use essentially the same template on this very blog to collapse the navigation items on mobile devices, where space comes at a premium.
Using bare <a> links and CSS media queries, I can have a fully functional
navigation menu that works responsively across all devices, without a single
line of JavaScript. And now, so can you!
-
Available across browsers since 2020. ↩