DEV Community

Gustavo Alexandrino
Gustavo Alexandrino

Posted on

How to fully animate the details HTML element with only CSS, no JavaScript

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 */
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Using the + adjacent sibling combinator
  2. 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 */
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
jgustavoas profile image
Gustavo Alexandrino

Update on June 2023: check the version that works in Safari and Firefox on macOS.

dev.to/jgustavoas/solved-how-to-fu...

Collapse
 
rolfb profile image
Rolf-B

Don't try this at home, kids! Checkbox hack are never a good idea, and the method has several shortcomings.

  • The animation of max-height works well only if the max-height is near the real height. Otherwise you'll see a delay when closing
  • The max-height animation relies on a fixed summary height of 4rem. But you can never know if your user scales the viewport so that this won't fit.
  • The details element stays open the whole time. Any assistance tool will announce it as open, although its content is hidden. Which makes the details element inaccessible.
  • The label inside the summary refers the click to the checkbox. By this, you smuggle interactive content into an element with button semantics. The spec forbids interactive content in buttons.
  • In consequence, the tab key activates the summary element, not the checkbox. You try to tab to the element and press SPACE, but seemingly, nothing happens (because that toggles the open status of details-element and not the checkbox). Which makes the details element inoperable.

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.

Collapse
 
jgustavoas profile image
Gustavo Alexandrino

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. The details element now changes its state naturally, and I’ve incorporated aria-details and role attributes to enhance accessibility. I’d greatly appreciate it if you could review that updated version and share your thoughts about it too.

Collapse
 
jgustavoas profile image
Gustavo Alexandrino

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.