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

5 min readJun 29, 2023

Now it works in Safari and in Firefox on macOS too!

Summary

  • Introduction
  • The issues
  • Finding the solution
  • The HTML
  • The CSS
  • Final result
  • Final words
  • Notes

Introduction

Front-end development can be challenging when a webpage fails to render or work as intended in some major browser or operating system. It’s a problem you can’t ignore. It might affect many users and have a negative impact on your brand.

In my previous post, I showed a trick to animate <details> element with pure CSS, without any JavaScript. But that solution has two problems:

1. It doesn’t work in Safari and
2. It doesn’t work in Firefox on macOS.

I am glad to have solved those issues, and now the animation works in all major browsers, either on Windows, Linux, Android, macOS, or iOS.

Plus, the final result is notably better and simpler than the original one.

In this post, I used the same design as the original to compare the changes in the code.

The issues

Safari browser
Safari has limited support for <summary> element¹ and ::markerpseudo-element². In the former, display:flex does not work, and you need to use summary::webkit-details-marker { display: none}to remove the default triangle icon³. As for the latter, that browser’s support is limited to color and font-size, that is to say no animation. 😒

The issue regarding ::marker also applies to ::before and ::after pseudo-elements. So, we won’t see the decorative triangle icon rotating smoothly in Safari, just an abrupt rotation⁴. 👎

Worse, for some reason I couldn’t figure out, the original trick using <label> and <input type="checkbox"> does not change <details> element’s height. I then had to discard it and invent a new one. 😞

My bigger surprise, though, was with Firefox.

Firefox on macOS
I already expected that the approach with :has()pseudo-class wouldn’t work because Firefox still doesn’t give default support for that useful CSS feature⁵ (isn’t this embarrassing for Mozilla?). But also the adjacent sibling combinator (+) approach (which does work on Windows and Linux) has failed on macOS. 😰

Expectation:

How it should be

Reality:

How it is in Safari and Firefox on macOS

Finding the solution

I use Linux to develop, so I needed to work directly on a macOS to see the result immediately as I coded to make the tweaks.

I’ve got that, and after some trials, I found a solution, which consists of these main changes to the original HTML structure:

1. Starting <details> element without the open attribute (this was the trick then, but now it is not necessary). 👍

2. Discarding <input type="checkbox"> and <label>because <details> itself will trigger the animation. 🤩

3. Placing <div> with the detailed content outside <details> element, becoming its sibling instead of its descendant (this is the new trick). 🤔

The HTML

Then

<input type="checkbox" name="detail-one" id="detail-one" />
<details open>
<summary>
<label for="detail-one">Click to open and close smoothly with pure CSS</label>
</summary>
<div class="content">…</div>
</details>

Now

<details>
<summary>
<span>Click to open and close smoothly with pure CSS</span>
</summary>
</details>
<div class="content">…</div>

I have replaced <label> with <span>, but I kept the same style in CSS, so I could use and animate the triangle icon as a ::beforepseudo-element.

Due to Safari’s poor support, you can only apply animation to some element or pseudo-element that is a child of <summary>, not to the <summary> itself. Otherwise, we could discard <span>too.

The CSS

<details>
Then

details {
max-width: 500px;
box-sizing: border-box;
margin-top: 5px;
background: white;
transition: max-height 400ms ease-out;
max-height: 4rem; /* Set a max-height value just enough to show the summary content */
overflow: hidden; /* Hide the rest of the content */
}

Now

details {
max-width: 500px;
overflow: hidden; /* Keep this line to prevent an odd blue outline around the element in Safari. */
}

<summary>
Then

summary {
display: block; /* This hides the summary's ::marker pseudo-element */
}

Now

summary {
display: block; /* This hides the summary's ::marker pseudo-element */
}
summary::-webkit-details-marker {
display: none; /* This also hides the ::marker pseudo-element, but in Safari */
}

<div class="content">
Then

div.content {
padding: 0 10px;
border: 2px solid #888;
}

Now

div.content { 
max-width: 500px;
box-sizing: border-box;
padding: 0 10px;
max-height: 0;
overflow: hidden;
border: 2px solid transparent;
transition: max-height 400ms ease-out, border 0ms 400ms linear;
}

The initial value of max-height is 0, which changes when <details> opens (see the “Transitions” section next).

Transitions
Then

input:checked + details,
details:has(input:checked) {
max-height: 800px; /* Set a max-height value enough to show all the content */
}
input:checked + details label::before,
details:has(input:checked) label::before {
rotate: 90deg;
transition: rotate 200ms ease-out;
}

Now

details[open] + div.content {
max-height: 800px; /* Set a max-height value enough to show all the content */
border-color: #888;
transition: max-height 400ms ease-out, border 0ms linear;
}
details[open] span::before {
rotate: 90deg;
transition: rotate 200ms ease-out;
}

The technique with the adjacent sibling combinator (+) is the only one that works now. The technique with :has()pseudo-class isn’t an option anymore.

Final result

Final words

In case you haven’t noticed yet, this solution does not actually animate the height of <details> element. Indeed, it uses the open/closed state of that element to control the animation of the height of an adjacent <div> which holds the detailed content.

Thus, the original title “How to fully animate the <details> element…” is not accurate here because the detailed content is no longer a child of that element.

However, I think the benefits overcome this detail (pun not intended). The user can interact with the design seamlessly. And the developer gets a nice and clean code, rid of working issues, despite the limitations of some browsers.

Thank you for reading!

--

--

jgustavo.wd
jgustavo.wd

Written by jgustavo.wd

JavaScript developer focused on front-end

Responses (1)