Accordions with <details>
<details>
demos and exploration of html accordions.
<details>
Basic Accordion with Details
This is just a basic accordion
<details>
<summary>Details</summary>
<p>This is just a basic accordion</p>
</details>
Animating the caret
<style>
::marker,
::-webkit-details-marker {
display: none;
}
summary {
list-style: none;
}
details summary:before {
content: '→';
display: inline-block;
rotate: 0deg;
margin-inline-end: 0.5em;
transition: rotate 0.3s ease-in-out;
}
details[open] > summary:before {
rotate: 90deg;
}
</style>
<details>
A CSS Only Accordion with It feels like this should work and maybe it’s just a browser issue / quirk that it doesn’t currently. Maybe by the time you’re looking at this it’s ✨flawless✨.
Details
This is just a basic accordion
<details class="css-only">
<summary>Details</summary>
<div id="demo-accordion-css">
<p>This is just a basic accordion</p>
</div>
</details>
<style>
.css-only #demo-accordion-css {
transition:
display 0.3s allow-discrete,
opacity 0.3s,
max-block-size 0.3s;
opacity: 0;
max-block-size: 0;
}
.css-only[open] #demo-accordion-css {
opacity: 1;
max-block-size: 200px;
}
@starting-style {
.css-only[open] #demo-accordion-css {
opacity: 0;
max-block-size: 0;
}
}
</style>
WTF is max-block-size
max-block-size === max-height. Well.. kinda, it’s the logical property version of max-height. This supports all type directions instead of just top to bottom, left to right. This is the same as things like margin-block, margin-inline. It has full browser support, so update your brain matter and use block-size and inline-size.
Animate According with JavaScript via View Transitions
Woof, no wonder we reach for JS frameworks. It’s not too crazy but it’s a lot of JS just to animate an accordion. Remember .slideDown();
.
This next one is good, less code, but the container itself still isn’t sliding without adding in a custom JS height animation.
Details
This is an accordion with more
I'm adding more stuff here to make animation look nicer.
<script>
const details = document.querySelector('#demo-accordion-vt');
const content = details.querySelector('#demo-accordion-vt .details-content');
const summary = details.querySelector('#demo-accordion-vt summary');
summary.addEventListener('click', async (e) => {
e.preventDefault();
let transition = document.startViewTransition(() => {
if (details.open) {
details.open = false;
} else {
details.open = true;
}
});
});
</script>
<details id="demo-accordion-vt">
<summary>Details</summary>
<div class="details-content">
<p>This is an accordion with more</p>
<p>I'm adding more stuff here to make animation look nicer.</p>
</div>
</details>
Animate According with JavaScript via WAAPI
Get ready, it’s about to be heavy.
Details
This is an accordion with more
I'm adding more stuff here to make animation look nicer.
<script>
const details = document.querySelector('#demo-accordion-waapi');
const content = details.querySelector('#demo-accordion-waapi .details-content');
const summary = details.querySelector('#demo-accordion-waapi summary');
let isCollapsing = false;
let isExpanding = false;
let animation = null;
summary.addEventListener('click', (e) => {
e.preventDefault();
details.style.overflow = 'hidden';
details.open ? collapse() : expand();
});
function expand() {
details.style.height = `${details.offsetHeight}px`;
details.open = true;
window.requestAnimationFrame(() => {
isExpanding = true;
if (animation) animation.cancel();
const start = `${details.offsetHeight}px`;
const end = `${summary.offsetHeight + content.offsetHeight}px`;
animation = details.animate({ height: [start, end] }, { duration: 300, easing: 'ease-in-out' });
animation.onfinish = () => onFinish(true);
animation.oncancel = () => (isExpanding = false);
});
}
function collapse() {
isCollapsing = true;
window.requestAnimationFrame(() => {
if (animation) animation.cancel();
const start = `${details.offsetHeight}px`;
const end = `${summary.offsetHeight}px`;
animation = details.animate({ height: [start, end] }, { duration: 300, easing: 'ease-in-out' });
animation.onfinish = () => onFinish(false);
animation.oncancel = () => (isCollapsing = false);
});
}
function onFinish(isOpen) {
details.open = isOpen;
animation = null;
isCollapsing = false;
isExpanding = false;
details.style.height = null;
details.style.overflow = 'visible';
}
</script>
<details id="demo-accordion-waapi">
<summary>Details</summary>
<div class="details-content">
<p>This is an accordion with more</p>
<p>I'm adding more stuff here to make animation look nicer.</p>
</div>
</details>
Exclusive Accordions
By giving an accordion a name attribute, you can make it part of a group of “exclusive” accordions where only one with the same name can be open at a time.
Q: Will only one of these stay open at a time?
You betcha
Q: Why doesn't starting-style and allow-discrete work here?
Dunno, it probably should tbh.
Q: Why is there a third question?
Just here so I don't get fired.
<details name="faq">
<summary>Q: Will only one of these stay open at a time?</summary>
<p>You betcha</p>
</details>
<details name="faq">
<summary>Q: Why doesn't starting-style and allow-discrete work here?</summary>
<p>Dunno, it probably should tbh.</p>
</details>
<details name="faq">
<summary>Q: Why is there a third question?</summary>
<p>Just here so I don't get fired.</p>
</details>