Volomike
Volomike

Reputation: 24916

How To Add CSS3 Transition With HTML5 details/summary tag reveal?

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.

Upvotes: 82

Views: 68050

Answers (12)

Be3y4uu_K0T
Be3y4uu_K0T

Reputation: 478

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>

Upvotes: 0

Roko C. Buljan
Roko C. Buljan

Reputation: 206505

Toggle details height from 0 to auto

Jumping ahead of time (2024) since this is currently available in Chrome Canary, you can download here.
If the following example works for you on the latest stable release, that means that the time has come!

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?

In 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;
    overflow: hidden;
    height: 0;
  }
  
  &[open]::details-content {
    height: calc-size(auto);  /* or eventually: calc-size(min-content) ? */
  }
}
<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 🎉

Upvotes: 9

graphicdivine
graphicdivine

Reputation: 11211

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>

Upvotes: -2

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>

Upvotes: -1

T&#225;rcio Zemel
T&#225;rcio Zemel

Reputation: 1177

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>

Upvotes: 63

LHLaurini
LHLaurini

Reputation: 2570

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.

Upvotes: 3

Mr. Hugo
Mr. Hugo

Reputation: 12590

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>

Upvotes: 9

iiic
iiic

Reputation: 1362

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>

Upvotes: 4

Felipe Saldanha
Felipe Saldanha

Reputation: 1365

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>

Upvotes: 26

Volomike
Volomike

Reputation: 24916

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.

Upvotes: 99

Daniel Sepp
Daniel Sepp

Reputation: 184

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

Upvotes: 13

Andrew
Andrew

Reputation: 46

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!

Upvotes: -2

Related Questions