The details element
Accordions, I hardly new ye> TL;DR Jump to the final product
Ever since we started creating websites, we wanted a way to hide information until it was needed. We came up with the accordion, the revealer or the so called "twisty". These comprised of a heading or summary with a little arrow nearby that, when clicked, revealed a section of content - often with a little animation flourish.
Now this required javascript to maintain and toggle its state. Good examples removed the invisible content from the DOM but often times this was forgotten leading to the focus getting lost and often times, no thought was given to accessibility at all.
A proper accessible accordion required using aria tagging to indicate the open state, the control relationship and the expected outcome. Still, despite all of these issues, the accordion proved its popularity.
Now available in native HTML
The web standards committee listened and gave us the The Details disclosure element. Verbose name aside, this is a semantic accessible accordion with no javascript or additional aria tagging required.
<details>
<summary>This is my amazing accordion</summary>
And this is the content
</details>
This is my amazing accordion
And this is the contentIt can even be open by default using the open attribute.
<details open>
<summary>This is my second most amazing accordion</summary>
And this content is shown on page load
</details>
This is my second most amazing accordion
And this content is shown on page loadLet's make it look good
While perfectly fine as is, it can definitely be improved upon. Animations can help inform users of what just happened as long as they are used sparingly and with purpose. This is a perfect case for a quick little slide animation with complimentary spin of the arrow.
We have the following selectors available to us
details- The overall elementsummary- The clickable heading that is always shownsummary::marker- The default arrowdetails::details-content- The shadow DOM of the details contentdetails[open]- The selector when the details element is open
Lets start by animating the details element opening and closing. The best way I have found is to animate the size of the details-content from 0 to auto using the amazing interpolate-size: allow-keywords property. This allows us to automatically animated between 0 and content size without having to rely on max-height workarounds or expensive javascript based height calculations. Alternately, we can animation from 0 to calc-size(auto, size) if we prefer a more local approach.
:root {
interpolate-size: allow-keywords;
}
details::details-content {
display: block;
block-size: 0;
overflow: hidden;
transition-property: block-size, content-visibility;
transition-duration: 300ms;
transition-behavior: allow-discrete;
}
details[open]::details-content {
block-size: auto;
}
I'm using block-size but feel free to use height if you prefer. We are also animating the "unanimatable" property content-visibility which we can do by setting the transition-behavior to allow-discrete. This hides the content at the end of the close animation and shows it at the start of the open animation. So convenient. We also need to prevent overflow during the animation but this is not hard to do.
When applying padding or margin to the content it is important to not put these of the details-content as this will still be visible in the closed state. You cannot select children of shadow DOM elements so this should be done from the parent details element.
details > * {
padding-block-end: 1rem;
padding-inline: 2rem 1rem;
}
Animating the arrow
The default marker cannot have transforms or rotations applied to it which is a shame. We can fix this by hiding the default marker and substituting our own.
summary {
padding-inline-start: 2rem;
position: relative;
padding-block: 1rem;
&::marker {
content: none;
}
&::before {
content: '';
position: absolute;
display: block;
border-block: 0.35em solid transparent;
border-inline-start: 0.6em solid;
top: 1.25em;
left: 0.75em;
transition: rotate 300ms;
}
}
details[open] summary::before {
rotate: 90deg;
}
Here I am creating a little simple border based arrow as I have been doing since the Internet Explorer 5 days but you could use a svg icon or character of your choosing. Then it is positioned just so and rotated through 90 degrees when the details are open.
Grouping details
A bonus function on the details element is they can be grouped together using the name property. Detail elements that share the same name will only permit one details being open at a time.
<details open name="example">
<summary>Detail 1</summary>
I can only see this
</details>
<details open name="example">
<summary>Detail 2</summary>
Or this
</details>
Detail 1
I can only see thisDetail 2
Or thisThe final product
Wrapping it all up, we have an easy to use, performant, semantic, accessible and nice to use accordion with no need for complicated calculations or javascript.
Please let me know if you found this approach useful or other clever uses of details you may have used.
-
← Previous
Some really good ways to switch themes