<div class="accordion" data-accordion-container data-accordion-config-single="true">
<article class="accordion__panel" data-accordion-panel>
<h3 class="accordion__header">
<button class="accordion__button h4" data-accordion-button aria-expanded="false">
Test Heading
<svg class="accordion__arrow" viewBox="0 0 407.437 407.437" role="presentation" aria-hidden="true">
<polygon points="386.258,91.567 203.718,273.512 21.179,91.567 0,112.815 203.718,315.87 407.437,112.815 "/>
</svg>
</button>
</h3>
<div class="accordion__window" data-accordion-window>
<div class="accordion__content" data-accordion-content>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sollicitudin suscipit laoreet. Ut tristique nulla tempus commodo pharetra. Nam auctor, urna id consequat vulputate, ipsum ante pulvinar dolor, sed rhoncus neque enim eu
ipsum. Mauris viverra interdum mattis. Integer nec molestie tellus. Phasellus nec semper nibh, eget sagittis neque. <a href="https://www.w3.org/TR/wai-aria-practices/examples/accordion/accordion.html" target="_blank" rel="noopener">Read more about Accordions</a></p>
</div>
</div>
</article>
<article class="accordion__panel" data-accordion-panel>
<h3 class="accordion__header">
<button class="accordion__button h4" data-accordion-button aria-expanded="false">
Test Heading II
<svg class="accordion__arrow" viewBox="0 0 407.437 407.437" role="presentation" aria-hidden="true">
<polygon points="386.258,91.567 203.718,273.512 21.179,91.567 0,112.815 203.718,315.87 407.437,112.815 "/>
</svg>
</button>
</h3>
<div class="accordion__window" data-accordion-window>
<div class="accordion__content" data-accordion-content>
<p>Lorem ipsum dolor</p>
</div>
</div>
</article>
<article class="accordion__panel" data-accordion-panel>
<h3 class="accordion__header">
<button class="accordion__button h4" data-accordion-button aria-expanded="false">
Test Heading III
<svg class="accordion__arrow" viewBox="0 0 407.437 407.437" role="presentation" aria-hidden="true">
<polygon points="386.258,91.567 203.718,273.512 21.179,91.567 0,112.815 203.718,315.87 407.437,112.815 "/>
</svg>
</button>
</h3>
<div class="accordion__window" data-accordion-window>
<div class="accordion__content" data-accordion-content>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sollicitudin suscipit laoreet. Ut tristique nulla tempus commodo pharetra. Nam auctor, urna id consequat vulputate, ipsum ante pulvinar dolor, sed rhoncus neque enim eu
ipsum. Mauris viverra interdum mattis. Integer nec molestie tellus. Phasellus nec semper nibh, eget sagittis neque.</p>
</div>
</div>
</article>
</div>
<div class="accordion" data-accordion-container data-accordion-config-single="{{ config.single }}">
{% for item in panels %}
<article class="accordion__panel" data-accordion-panel>
<h3 class="accordion__header">
<button class="accordion__button h4" data-accordion-button aria-expanded="false">
{{ item.heading }}
<svg class="accordion__arrow" viewBox="0 0 407.437 407.437" role="presentation" aria-hidden="true">
<polygon points="386.258,91.567 203.718,273.512 21.179,91.567 0,112.815 203.718,315.87 407.437,112.815 "/>
</svg>
</button>
</h3>
<div class="accordion__window" data-accordion-window>
<div class="accordion__content" data-accordion-content>
{{ item.content }}
</div>
</div>
</article>
{% endfor %}
</div>
{
"config": {
"single": true
},
"panels": [
{
"heading": "Test Heading",
"content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sollicitudin suscipit laoreet. Ut tristique nulla tempus commodo pharetra. Nam auctor, urna id consequat vulputate, ipsum ante pulvinar dolor, sed rhoncus neque enim eu ipsum. Mauris viverra interdum mattis. Integer nec molestie tellus. Phasellus nec semper nibh, eget sagittis neque. <a href=\"https://www.w3.org/TR/wai-aria-practices/examples/accordion/accordion.html\" target=\"_blank\" rel=\"noopener\">Read more about Accordions</a></p>"
},
{
"heading": "Test Heading II",
"content": "<p>Lorem ipsum dolor</p>"
},
{
"heading": "Test Heading III",
"content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum sollicitudin suscipit laoreet. Ut tristique nulla tempus commodo pharetra. Nam auctor, urna id consequat vulputate, ipsum ante pulvinar dolor, sed rhoncus neque enim eu ipsum. Mauris viverra interdum mattis. Integer nec molestie tellus. Phasellus nec semper nibh, eget sagittis neque.</p>"
}
]
}
/*
* accordion styles
*
*****************************************************************************/
.accordion {
margin: 5px; // allows room to display full border when focused / Safari
&__panel + &__panel {
border-top: 1px solid color(neutral, lighter);
}
&__header {
margin: 0;
}
&__button {
z-index: 0;
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0;
border: 0;
padding: 20px 14px;
width: 100%;
background-color: color(neutral, lightest);
text-align: left;
appearance: none;
&:focus { z-index: 1; } // allows the full focus state border to be visible
}
&__arrow {
width: 14px;
height: 14px;
transition: .2s ease-in-out;
[aria-expanded='true'] & {
transform: rotate(180deg);
}
}
&__window {
margin: 0;
height: 0;
overflow: hidden;
visibility: hidden;
transition: .2s ease-in-out;
}
&__content {
padding: 20px 14px;
}
:nth-child(1) {
margin-top: 0;
}
:last-child {
margin-bottom: 0;
}
}
// Polyfills
if (window.Element && !Element.prototype.closest) {
Element.prototype.closest =
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i,
el = this;
do {
i = matches.length;
while (--i >= 0 && matches.item(i) !== el) {};
} while ((i < 0) && (el = el.parentElement));
return el;
};
}
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (callback, thisArg) {
thisArg = thisArg || window;
for (var i = 0; i < this.length; i++) {
callback.call(thisArg, this[i], i, this);
}
};
}
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
// Accordion Functionality
const buttons = document.querySelectorAll('[data-accordion-button]');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', (e) => {
const toggle = (e.target.getAttribute('aria-expanded') === 'true') ? false : true;
const root = buttons[i].closest('[data-accordion-container]');
const panel = buttons[i].closest('[data-accordion-panel]');
const panelSiblings = getSiblings(panel);
const window = panel.querySelector('[data-accordion-window]');
const content = panel.querySelector('[data-accordion-content]');
buttons[i].setAttribute('aria-expanded', toggle);
buttons[i].setAttribute('tabindex', '0');
toggle ?
window.setAttribute('style', `height: ${content.offsetHeight}px; visibility: visible;`) :
window.setAttribute('style', 'height: 0; visibility: hidden;');
if (root.getAttribute('data-accordion-config-single') === 'true') {
panelSiblings.forEach((sibling) => {
const siblingButton = sibling.querySelector('[data-accordion-button]');
const siblingWindow = sibling.querySelector('[data-accordion-window]');
siblingButton.setAttribute('aria-expanded', 'false');
siblingWindow.setAttribute('style', 'height: 0; visibility: hidden;');
});
}
});
// Arrow key controls
buttons[i].addEventListener('keydown', (e) => {
if (e.keyCode === 38) {
if (i === 0) {
buttons[buttons.length - 1].focus();
} else {
buttons[i - 1].focus();
}
}
if (e.keyCode === 40) {
if (i === buttons.length - 1) {
buttons[0].focus();
} else {
buttons[i + 1].focus();
}
}
});
}
function getSiblings(element) {
let siblings = [];
let sibling = element.parentNode.firstChild;
while (sibling) {
if (sibling.nodeType === 1 && sibling !== element) {
siblings.push(sibling);
}
sibling = sibling.nextSibling
}
return siblings;
};
Accordions deliver large amounts of content in a small space through progressive disclosure, and work especially well whenever vertical space is at premium. All accordion panels are closed by default – expanding when the user clicks one of the accordion section titles.
This accordion comes with a configuration attribute, data-accordion-config-single
that enables you to decide whether you want the accordion panel siblings to close or remain opened when selecting another panel. The example above is set to true
.
role="button"
.aria-expanded
set to true. If the panel is not visible, aria-expanded
is set to false.Tab
= Move to next focusable elementEnter
or Space
= Expand/Collapse PanelShift + Tab
= Move to previous focusable element↑
↓
= Cycle headers when header focusedControl + Option
←
→
= Navigate through accordion content with screen readerWhen interacting with accordions, screen readers should announce the following information:
When navigating through an accordion, the following tab order is expected: