The bit
The code
Template
<div class="">
<button data-expandable aria-expanded="false" aria-controls="expandable-content-1" class="flex gap-3 itemx-center">
<span>Toggle me please</span>
<span class="font-bold plus transition-all">+</span>
</button>
<div class="mt-3" id="expandable-content-1" aria-hidden="true" data-expandable-content>
<div class="overflow-hidden">
<p>
Id eu nisl nunc mi. Proin nibh nisl condimentum id.
Nunc aliquet bibendum enim facilisis <a href="#" class="underline">gravida</a> neque convallis a cras. Magna fringilla urna porttitor rhoncus dolor purus non.
</p>
</div>
</div>
</div>
Script
document.addEventListener('click', (event) => {
const target = event.target as HTMLElement;
const toggle = target.closest('[data-expandable]') as HTMLButtonElement;
if (!toggle) return;
const contentID = toggle.getAttribute('aria-controls') ?? 'xxx';
const contentElement = document.getElementById(contentID);
if (!contentElement) {
console.warn(
'No target content found for this toggle. Maybe you forget to add the aria-controls attribute or the target content element is missing',
toggle,
);
return;
}
if (!contentElement.style.getPropertyValue('--duration')) {
contentElement.style.setProperty('--duration', `${0.3 + contentElement.scrollHeight / 1000}s`);
}
const isExpanded = 'true' === toggle.getAttribute('aria-expanded');
if (isExpanded) {
toggle.setAttribute('aria-expanded', 'false');
contentElement.setAttribute('aria-hidden', 'true');
contentElement.setAttribute('inert', 'true');
} else {
toggle.setAttribute('aria-expanded', 'true');
contentElement.setAttribute('aria-hidden', 'false');
contentElement.removeAttribute('inert');
}
});
Styles
[data-expandable][aria-expanded="true"] {
.plus {
transform: rotate(135deg);
}
}
[data-expandable-content] {
display: grid;
grid-template-rows: 1fr;
transition-delay: var(--delay, 0s);
transition-duration: var(--duration, 0.3s);
transition-property: 'display';
&:is([aria-hidden='true']) {
grid-template-rows: 0fr;
}
}