Yes, this includes animating the closing of the element too!
Introduction
Animating the <details>
HTML element using only pure CSS can be a challenging task, as many articles, posts, and tutorials either rely on JavaScript or offer incomplete solutions that are unable to animate the closing of the element or produce a smooth animation.
However, with a bit of creativity, it is possible to create a fully-animated <details>
element using pure CSS.
This solution reproduces the example from Chris Coyier's CSS Tricks post, but achieves the same smooth animation without using any JavaScript – only CSS – for both opening and closing the element.
The CSS Solution
The key is to start the page with the details
element in its open state (<details open>
), then change its max-height
value by toggling a checkbox, and use a transition effect to animate that change.
details {
max-height: 4rem; /* Set a max-height value just enough to show the summary */
overflow: hidden; /* Hide the rest of the content */
transition: max-height 400ms ease-out; /* Animate the change */
}
To control the change of the element's max-height
value, you will need a checkbox and associate it with the <details>
element. You can choose from two approaches to do this, and both work great:
- Using the
+
adjacent sibling combinator - Using the
:has()
pseudo-class
input:checked + details,
details:has(input:checked) {
max-height: 576px; /* Set a max-height value enough to show all the content */
}
In the CSS, the selector input:checked + details
selects a <details>
element that immediately follows a checked input element. On the other hand, the selector details:has(input:checked)
selects a <details>
element that has a checked input element as its descendant.
The HTML
The difference between the two approaches in HTML is that the checkbox input must come immediately before the <details>
element in the first approach, and the checkbox must be a descendant of the <details>
element in the second approach.
Inside the <summary>
tag of the <details>
element , place a <label>
that toggles the checkbox:
<!-- approach #1 (checkbox comes immediately before the details element) -->
<input type="checkbox" name="approach-one" id="approach-one" />
<details open>
<summary>
<label for="approach-one">This one uses the adjacent sibling combinator (+) approach</label>
</summary>
<div class="rest-of-the-content">...</div>
</details>
<!-- approach #2 (checkbox is a descendant of the details element -->
<details open>
<summary>
<input type="checkbox" name="approach-two" id="approach-two" />
<label for="approach-two">This one uses :has() pseudo-class approach</label>
</summary>
<div class="rest-of-the-content">...</div>
</details>
A note about Firefox browser
One caveat to consider when using the :has()
approach is that in Firefox the user must explicitly enable this feature (see caniuse.com and MDN Web Docs).
Final result
Top comments (4)
Update on June 2023: check the version that works in Safari and Firefox on macOS.
dev.to/jgustavoas/solved-how-to-fu...
Don't try this at home, kids! Checkbox hack are never a good idea, and the method has several shortcomings.
dev.to - A constructive and inclusive social network for software developers. But the idea shown here excludes people who don't use vision and mouse/touch. Very sad.
I appreciate you taking the time to share your thoughts and detailed feedback. This code was created as an experimental response to a code challenge, and it was never intended to exclude or inconvenience anyone.
A few months after this post, I made an updated version. In it, I moved away from the
checkbox
hack. Thedetails
element now changes its state naturally, and I’ve incorporatedaria-details
androle
attributes to enhance accessibility. I’d greatly appreciate it if you could review that updated version and share your thoughts about it too.Sadly this solution doesn't work in Safari. It seems to be because of poor support of that browser for the
<summary>
element and the::marker
pseudo-element.It is also odd to note that in Firefox on MacOS this solution doesn't work at all, unlike other OS, where only the approach with the
:has()
pseudo-class doesn't work in Firefox by default.