Reputation: 24916
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
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
Reputation: 206505
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:
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>
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
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
Reputation: 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>
Upvotes: -1
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><details></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
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
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
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
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
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
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
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