87

Using just CSS3, is there a way to add a nice fade-in and slide-from-left transition effect on a DETAILS/SUMMARY reveal?

For a demo of this new tag, see this demo:

details { transition:height 3s ease-in; }
<details> <summary>Copyright 1999-2014.</summary> <section> <p> - by Refsnes Data. All Rights Reserved.</p> <p>All content and graphics on this web site are the property of the company Refsnes Data.</p> </section> </details>

In my case, after the summary tag, I put all other content in its own section tag so that I could style it because summary:after selector didn't seem to work. I tried using CSS3 transitions on height for the section and details tag, but it didn't help.

4
  • 1
    Please include all of the HTML & CSS in the question itself, in a minimal reproducible example. You may also want to consider using a different reference than w3schools, like MDN or even the standard itself. Also, HTML elements do not need to be in all uppercase, and referring to them in that manner makes the question hard to read. Commented Jul 5, 2016 at 22:07
  • Note that Edge support is still lacking. I have proposed a solution here. Commented Oct 2, 2018 at 11:40
  • See: CSS-only toggle Details height Commented Oct 30, 2024 at 16:45
  • For a more current answer (06/02/2025), go here Commented Jun 15 at 18:23

15 Answers 15

100

This should fix it.

details[open] summary ~ * { animation: sweep .5s ease-in-out; } @keyframes sweep { 0% {opacity: 0; margin-left: -10px} 100% {opacity: 1; margin-left: 0px} }
<details> <summary>Copyright 1999-2014.</summary> <p> - by Refsnes Data. All Rights Reserved.</p> <p>All content and graphics on this web site are the property of the company Refsnes Data.</p> </details>

Some credit goes to Andrew for pointing this out. I adapted his answer. Here's how this works. By adding the [open] attribute on the DETAILS tag, it only fires the animation keyframe when clicked. Then, by adding SUMMARY ~ * it means "all elements after the SUMMARY tag" so that the animation applies to those, and not the SUMMARY element as well.

Sign up to request clarification or add additional context in comments.

6 Comments

this works well only if you have nothing after the <details>.
@fcalderan not so -- I've implemented it on FAQs with multiples of these.
Consider Felipe's answer due to performance reasons. He's made a small improvement.
It's a good solution ! But the animation is only triggered the first time on Chrome (looks ok on Firefox)
@KCarnaille did you figure out a solution for Chrome? I want to restart the animation on Chrome.
|
71

For those looking for an open/close transition, it's possible to fake it by margin-bottom property.

details { background: gainsboro; padding: 10px; } details summary { cursor: pointer; transition: margin 150ms ease-out; } details[open] summary { margin-bottom: 10px; } 
<details> <summary><code>&lt;details&gt;</code> pseudo content transition</summary> Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia amet magnam fugit nihil delectus, id ratione deleniti minima, ipsum accusantium exercitationem est ipsa, possimus harum distinctio consequatur qui recusandae et. Alias quas temporibus aliquam modi nulla omnis unde atque magni tempora, corporis ducimus voluptas, recusandae, repellendus officiis molestias cumque quam. </details> 

<update>
Since my original response, CSS has evolved in many awesome ways. :)
Today, is possible to achieve this using transition animation.
eg: https://codepen.io/argyleink/pen/QWewVjd
</update>

7 Comments

This is the only answer where animation works on opening & closing the Details tags again. All other answers works only on first open of details tag.
For some reason I don't understand, when the contents of the <details> are wrapped in certain tags, like <h1> or <p>, then the animation only plays on closing and not opening.
Genius approach: since only the visible (summary) element's css is changed, it works every time.
I'm surprised this "solution" has so many votes. The content is not being animated, only the margin-bottom. If you haven't noticed, it's because the height of the content is small and the animation is fast.
This worked best, the fake is fine. the top rated answer does not have consistent behavior on chrome, at least (you can even see the non-deterministic behavior in the code snippet if you click a lot)
|
30

In addition to Volomike's answer, it would be possible to change margin-left to transform: translateX() for performance reasons.

details[open] summary ~ * { animation: sweep .5s ease-in-out; } @keyframes sweep { 0% {opacity: 0; transform: translateX(-10px)} 100% {opacity: 1; transform: translateX(0)} }
<details> <summary>Copyright 1999-2014.</summary> <p> - by Refsnes Data. All Rights Reserved.</p> <p>All content and graphics on this web site are the property of the company Refsnes Data.</p> </details>

3 Comments

How do I revert this animation upon closing? Simply adding a details[close] summary ~ * property with the same animation but with the property animation-direction: reverse did not work.
@Rich_Rich it's not possible. According to MDN: "Unfortunately, at this time there's no built-in way to animate the transition between open and closed."
there goes my CSS only dream. Also good to note: On Firefox iOS (and thus presumably on all iOS browsers), closing and re-opening does not replay the animation.
17

Toggle details height from 0 to auto

details { interpolate-size: allow-keywords; &::details-content { transition: block-size 1s, content-visibility 1s allow-discrete; overflow: hidden; block-size: 0; /* Or also: height:0; */ } &[open]::details-content { block-size: auto; /* Or also: height:auto; */ } }
<details> <summary>Example 1</summary> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio error facere magni, deserunt neque quo! Accusamus alias expedita, enim quasi vel, omnis, quisquam quo asperiores ea nesciunt nihil quam praesentium. </details> <details> <summary>Example 2</summary> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet cumque omnis repellendus quos perferendis molestias, voluptatum quasi excepturi. </details>

The Details' Slot ::details-content should be available as selector in future releases.
Tip: to inspect the Slots, in Developer Tools' Settings check the "Show user agent shadow DOM" option.


The above example is based on this related answer Animate Element Height from 0 to auto using interpolate-size: allow-keywords;, so here's an example that uses DIV elements:

Toggle-animate element height from 0 to auto

// JS just to control the aria-expanded value for A11Y document.querySelectorAll("input[aria-expanded]").forEach((elCheckbox) => { elCheckbox.addEventListener("input", () => { elCheckbox.setAttribute("aria-expanded", elCheckbox.checked); }); });
.details { .details-content { interpolate-size: allow-keywords; transition: height 0.6s; overflow: hidden; block-size: 0; /* Or also: height:0; */ } &:has(input:checked) .details-content { block-size: auto; /* Or also: height:auto; */ } }
<div class="details"> <label><input type="checkbox" aria-expanded="false" aria-controls="details-content-1" hidden>Header 1</label> <div class="details-content" id="details-content-1"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet cumque omnis repellendus quos perferendis molestias, voluptatum quasi excepturi, sit obcaecati dolorem. Fugit cupiditate iusto debitis, inventore, autem officiis beatae reprehenderit. </div> </div> <div class="details"> <label><input type="checkbox" aria-expanded="false" aria-controls="details-content-2" hidden>Header 2</label> <div class="details-content" id="details-content-2"> Omnis repellendus quos perferendis molestias, voluptatum quasi excepturi, consectetur adipisicing elit. Amet cumque omnis repellendus quos perferendis molestias. </div> </div>


Future aims?

I believe the tendency to additionally simplify the necessary syntax in order to achieve CSS transition from 0 to some intrinsic size would be using CSS calc-size(), something among this lines:

2024-11: not yet available

/* 2024-11 not yet possible */ details { &::details-content { transition: height 1s; /* useful as a fallback? */ transition: height 1s, content-visibility 1s allow-discrete; overflow: hidden; height: 0; } &[open]::details-content { height: auto; /* useful as a fallback? */ height: calc-size(auto); /* useful as a fallback? */ height: calc-size(auto, size); /* or eventually: calc-size(min-content, size) ? */ } }
<details> <summary>Example 1</summary> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odio error facere magni, deserunt neque quo! Accusamus alias expedita, enim quasi vel, omnis, quisquam quo asperiores ea nesciunt nihil quam praesentium. </details> <details> <summary>Example 2</summary> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet cumque omnis repellendus quos perferendis molestias, voluptatum quasi excepturi. </details>

If the above snippet works for you, well, seems like we finally achieved another major milestone in CSS evolution 🎉

4 Comments

In current Chrome, calc-size(auto) should be replaced by calc-size(auto, size). If some browser versions support only the former, maybe height: calc-size(auto); height: calc-size(auto, size); for fallback makes sense?
I have a question about the allow-keywords solution. You wrote code comments about replacing the 2nd and 3rd occurrence of block-size by height. Why does that work even if we don't replace the 1st occurrence?
To also animate the closing in the calc-size solution, use transition: height 1s, content-visibility 1s allow-discrete;. I deleted my wrong comment.
@root deleted mine consequentially as well. Thank you for your super valuable edit. Works in Chromium, android, and Windows 11. Seems like we achieved that longly craved milestone after all 🥳
13

details { transition: height 1s ease; overflow: hidden; } details:not([open]) { height: 1.25em; } details[open] { height: 2.50em; }
<details> <summary>Example</summary> Height needs to be set for this to work. Works on Chrome, haven't tested further. </details>

I recommend you also check out Animate.css here: http://daneden.me/animate. If

1 Comment

Unfortunately, my height needs to be auto because it's unpredictable.
10

I think a true CSS3 details summary reveal should be done like this (no fixed height and no javascript):

@keyframes animate { from {grid-template-rows: 0fr;} to {grid-template-rows: 1fr;} } details > div { display: grid; grid-template-rows: 0fr; transition: all ease-in-out 0.5s; } details > div > div { overflow: hidden; } details[open] > div { animation: animate 0.15s 0s 1 normal forwards; }
<details> <summary>Lorem ipsum</summary> <div> <div> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus suscipit neque vestibulum elit vehicula luctus. Aenean eget rutrum felis. Ut blandit, massa vitae pulvinar suscipit, nunc lorem pulvinar metus, sed elementum lectus erat euismod eros. Aliquam ac metus id tortor maximus mollis. Sed semper pulvinar risus. Mauris orci mauris, blandit tempus dolor sed, tincidunt pharetra nisi. Cras pulvinar ligula eu sapien cursus, in malesuada diam volutpat. Ut eleifend risus vitae eros pretium rhoncus. Nunc diam nibh, pharetra et orci eu, rutrum congue augue. Etiam quis tempor tortor. Pellentesque nec faucibus mi. Curabitur vitae tempus lorem. </div> </div> </details> <details> <summary>Lorem ipsum</summary> <div> <div> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus suscipit neque vestibulum elit vehicula luctus. Aenean eget rutrum felis. Ut blandit, massa vitae pulvinar suscipit, nunc lorem pulvinar metus, sed elementum lectus erat euismod eros. Aliquam ac metus id tortor maximus mollis. Sed semper pulvinar risus. Mauris orci mauris, blandit tempus dolor sed, tincidunt pharetra nisi. Cras pulvinar ligula eu sapien cursus, in malesuada diam volutpat. Ut eleifend risus vitae eros pretium rhoncus. Nunc diam nibh, pharetra et orci eu, rutrum congue augue. Etiam quis tempor tortor. Pellentesque nec faucibus mi. Curabitur vitae tempus lorem. </div> </div> </details> <br> <br> Inspired by <a href="https://www.youtube.com/watch?v=B_n4YONte5A">Kevin Powell</a>

2 Comments

This only works for the first couple of clicks. If I click over and over again the details eventually stop sliding and abruptly open
fwiw... you can combine your solution with this answer here. It takes minimal javascript to listen to animation end and click events
5

My styling…

Using max-height for transition instead of height you can have unknown height (smaller than 99rem) of content in opened details.

details { margin: 1.3rem 0; border-bottom: 1px solid #aaa; padding: 0.5rem; height: auto; max-height: 1.5rem; /* set to line height */ transition: all 0.1s ease; } details[open] { max-height: 99rem; transition: all 1s ease; border: 1px solid #aaa; } details summary { font-weight: bold; cursor: pointer; margin-bottom: 0.5em; font-weight: bold; margin: -0.5em -0.5em 0; padding: 0.5em; } details[open] summary { border-bottom: 1px solid #aaa; margin-bottom: 0.8em; }
<details> <summary>Something like… question?</summary> And some very, very long explanation of summarised text. And some very, very long explanation of summarised text. And some very, very long explanation of summarised text </details>

1 Comment

The text just appears, it doesn't have a transition or animation at all. You can see this by increasing the transition duration.
4

An alternative would be to simply avoid <details> altogether and instead rely on some checkbox trickery:

.toggle { appearance: none; display: none; } .toggle+label:before { content: "⯈"; display: inline-block; line-height: 0; transition: transform .5s; } .toggle:checked+label:before { transform: rotate(90deg); } .toggle~.details { max-height: 0; opacity: 0; overflow: hidden; transition: max-height .5s, opacity .5s; } .toggle:checked~.details { max-height: 100vh; opacity: 1; }
<div> <input id="toggle0" class="toggle" type="checkbox"> <label for="toggle0"> This is the summary </label> <div class="details"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus felis eget ligula pretium, a feugiat turpis commodo. Quisque nunc ligula, posuere eget lacus vel, condimentum convallis nulla. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam consectetur lobortis libero, at pulvinar turpis. Sed semper leo id quam vestibulum, id bibendum dolor molestie. Nam eu porttitor est, et facilisis neque. Nullam id ultrices nibh, vel lacinia velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer egestas ut nibh eget maximus. Etiam quis odio dignissim, facilisis orci vitae, fermentum eros. Proin luctus felis et nunc ultrices, sed rhoncus est finibus. </div> </div> Some text below.

Caveat: since we transition max-height between 0 and 100vh, this won't work if the height of the content is larger than the height of the window. In that case, you can either use a larger number or add overflow: auto to .toggle:checked~.details (so that you get a scrollbar).


EDIT: Alternatively, we could achieve something similar using some Javascript as well:

function onClick(event) { let details = event.target.parentElement; details.style['overflow'] = 'hidden'; if (details.open) { event.preventDefault(); setTimeout(function () { details.open = false; details.style['overflow'] = 'visible'; }, 500); details.style['max-height'] = '1em'; } else { details.style['max-height'] = '100vh'; } } window.onload = function () { for (summary of document.getElementsByTagName('summary')) { summary.addEventListener('click', onClick); } }
details { transition: max-height .5s; max-height: 1em; }
<details> <summary> This is the summary </summary> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus felis eget ligula pretium, a feugiat turpis commodo. Quisque nunc ligula, posuere eget lacus vel, condimentum convallis nulla. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam consectetur lobortis libero, at pulvinar turpis. Sed semper leo id quam vestibulum, id bibendum dolor molestie. Nam eu porttitor est, et facilisis neque. Nullam id ultrices nibh, vel lacinia velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer egestas ut nibh eget maximus. Etiam quis odio dignissim, facilisis orci vitae, fermentum eros. Proin luctus felis et nunc ultrices, sed rhoncus est finibus. </details> Some text below.

Note that this has the same problem as before, but additionally won't work if the summary takes up more than one line. Also, glyphs containing descenders (g, j, p, q, y, ç, ŋ, µ, ...) flicker slightly when closing.

1 Comment

Using native details/summary has accessibility benefits and using checkbox trickery has negative impacts to assistive technologies because it doesn't properly adjust the accessibility DOM. Checkbox trickery may also have negative impacts on alternative input methods like keyboard-only control.
0

Here is another option, for multiple details elements, using a variation of some ideas presented above. a translateX sweep for the summaries and a transform:scaleY reveal for the details

First assign a class=reveal to all details elements, which you then assign a basic animation to.

details.reveal[open] { animation: reveal 0.6s ease-in-out; } details.reveal:open { animation: reveal 0.6s ease-in-out; } 

I use two options there to also cover mobile device browsers.

Then for the summary elements:

details[open] > summary { animation: sweep 0.8s; } details:open > summary { animation: sweep 0.8s; } 

Define the keyframes, which are somewhat similar to previous answers, except I use transform:scaleY for revealing the details.

@keyframes sweep { 0% { opacity: 0.75; transform: translateX(-30px) } 100% { opacity: 1; transform: translateX(0) } } @keyframes reveal { 0% { opacity: 0.25; transform:scaleY(0) } 100% { opacity: 1; transform:scaleY(1) } } 

Working example on my test site

This is all CSS except for a script to auto-refresh the page on animationend event, so the animation can be rerun continuously.

<script> const reveal = document.querySelector( ".reveal" ); reveal.addEventListener ( "animationend", ( event ) => { location.href=location.href; } </script> 

Comments

0

You’ve hit one of the common limitations with <details>/<summary>: the opening/closing is handled by the browser and it jumps between height: auto and height: 0. CSS transitions can’t animate to/from auto. That’s why your transition: height 3s doesn’t appear smooth.

But you can get a nice fade + slide effect on the contents (the <section>) with pure CSS, by animating properties that are animatable (like opacity, transform, max-height). Example:

details section { max-height: 0; overflow: hidden; opacity: 0; transform: translateX(-20px); transition: max-height 0.4s ease, opacity 0.4s ease, transform 0.4s ease; } /* When details is open */ details[open] section { max-height: 200px; /* pick a value larger than content height */ opacity: 1; transform: translateX(0); }
<details> <summary>Copyright 1999–2014.</summary> <section> <p>- by Refsnes Data. All Rights Reserved.</p> <p>All content and graphics on this web site are the property of the company Refsnes Data.</p> </section> </details>

Notes:

  • opacity → fades in/out.

  • transform: translateX(-20px) → slides in from the left.

  • max-height → fakes a smooth “grow” because it is animatable (unlike height:auto). Just pick a max-height that’s big enough for your content.

  • overflow: hidden prevents the content from spilling out during animation.

Caveat: This is a CSS-only trick. If your content is taller than your chosen max-height, it will get cut off. If you need truly dynamic height animations, you’d need JavaScript to measure the height and animate.

So the short answer: yes, you can get fade/slide purely with CSS, but you can’t animate height:auto; instead you animate max-height + opacity + transform.

1 Comment

I made your code run able for peoples to test out, however the transition doesn't seem to work at least in Firefox.
0

Here is a version using a CSS keyframe animation.

The trick is the CSS selector Subsequent-sibling combinator ~

The subsequent-sibling combinator (~, a tilde) separates two selectors and matches all instances of the second element that follow the first element (not necessarily immediately) and share the same parent element. https://developer.mozilla.org/en-US/docs/Web/CSS/Subsequent-sibling_combinator

Yes many rules on details could not be animated, but some are, in this example the background color and border-radius are animated. It does work consistently in almost all browsers.

details[open] summary ~ * { animation: animdetail 3s ease-in-out; padding: 2rem; background: yellow; border-radius: 1em } @keyframes animdetail { 0% { opacity: 0; background: blue; border-radius: 3em; margin: 0 0 0 -500px } 100% { opacity: 1; background: red; border-radius: 1em; margin: 0 0 0 0 } }
<details> <summary> Animated detail </summary> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam eu sodales tortor, posuere mattis nunc. Integer eget sapien ullamcorper diam mollis laoreet. Praesent dignissim id urna at malesuada. Etiam id nisl vitae ante vestibulum volutpat. </p> </details>

https://caniuse.com/mdn-css_selectors_subsequent-sibling

Comments

-1

Summary poses problems for the closing animation, personally I did it like this

function ShowContentDropDown($event) { let parentDropdown = $event.target.parentElement; let DropdownClickedIsActif = parentDropdown.classList.contains('active'); let OtherDropdownAreActif = false; document.querySelectorAll('.Dropdown').forEach(dropdown => { if(dropdown.classList.contains('active')) { OtherDropdownAreActif = true; } dropdown.classList.remove('active'); dropdown.querySelector('.ContentDropDown').style.maxHeight = null; }); if (!DropdownClickedIsActif) { setTimeout(() => { parentDropdown.classList.add('active'); let content = parentDropdown.querySelector('.ContentDropDown'); content.style.maxHeight = content.scrollHeight + 'px'; OtherDropdownAreActif = false }, OtherDropdownAreActif ? 300 : 0); } }
.ContentDropDown { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-in-out, padding 0.3s ease-in-out; padding: 0 1em; } .Dropdown.active .ContentDropDown { max-height: 10em; padding: 1em; } .HeaderDropDown { cursor: pointer; background-color: #f0f0f0; padding: 1em; border-bottom: 1px solid #ccc; } .HeaderDropDown h3, .HeaderDropDown span { margin: 0; display: inline-block; vertical-align: middle; } .HeaderDropDown span { margin-right: 10px; } .Dropdown { border: 1px solid #ccc; margin-bottom: 10px; }
<div class="Dropdown"> <div class="HeaderDropDown" onclick="ShowContentDropDown(event)"> <span>Icon</span> <h3>Titre 1</h3> </div> <div class="ContentDropDown"> <span>Lien 1</span><br> <span>Lien 2</span><br> <span>Lien 3</span> </div> </div> <div class="Dropdown"> <div class="HeaderDropDown" onclick="ShowContentDropDown(event)"> <span>Icon</span> <h3>Titre 2</h3> </div> <div class="ContentDropDown"> <span>Lien 1</span><br> <span>Lien 2</span><br> <span>Lien 3</span> </div> </div> <div class="Dropdown"> <div class="HeaderDropDown" onclick="ShowContentDropDown(event)"> <span>Icon</span> <h3>Titre 3</h3> </div> <div class="ContentDropDown"> <span>Lien 1</span><br> <span>Lien 2</span><br> <span>Lien 3</span> </div> </div>

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
-1

In addition to Felipe Saldanha answer, alternative way using @starting-style.

details[open] summary ~ * { transition: all .5s ease-in-out; @starting-style { opacity: 0; translate: -10px 0; } }
<details> <summary>Copyright 1999-2014.</summary> <p> - by Refsnes Data. All Rights Reserved.</p> <p>All content and graphics on this web site are the property of the company Refsnes Data.</p> </details>

Comments

-2

You probably would want to use a CSS animation using the keyframes if you don't plan on having it appear when you hover over it. If you only want the animation to appear, say, when you see details/summary description on the page, you could use some jQuery so that the browser adds the class of the animation when scrolling.

https://jsfiddle.net/22e95bxt/

Is this what you're looking for?

Edit: Whoops. This is NOT what you're asking for. My bad!

2 Comments

It's close. Looks like you need to use the [open] and :not([open]) selectors to improve it, which is what I'm working on.
Your answer looks to be the best if you could remove the bold edit and then switch your selector to like DETAILS[open] SECTION that fires the animation. That way, it only happens on the click.
-2

Here's how I'd go about doing this.:

details { border: 1px solid red; margin-left: -3rem; } details summary { border: 1px solid green; margin-left: 3rem; } details, summary { transition: all 150ms ease-out; } details[open], details[open] summary { margin-left: 0; }
<details> <summary>Lorem ipsum</summary> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </details>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.