Accordion

Toggle the visibility of content across your project with a few classes and out additional Javascript plugin (only for accessibility).

How it works#

A radio or checkbox is used to show and hide content. A label with an additional role attribute is used to trigger a toggle of the div with class .accordion-collapse . Collapsing an element will animate the max-height from it’s current value to 0.

Accordion example#

Using the following syntax, you can use a collapse behavior to create the accordion.

multiple#

preview

<div class="accordion" id="accordion1">
    <div class="accordion-item">
        <input type="checkbox" data-controller="accordion/input" class="accordion-check sr-only"
            id="1ed9426a" name="accordion1_first" checked />
        <label type="button" class="accordion-label" for="1ed9426a" id="2bf6c612"
            data-target="#c21c8732" aria-expanded="false" aria-controls="c21c8732">
            <h5 class="accordion-item-header">Collapsible Group Item #1</h5>
            <svg class="icon accordion-icon" xmlns="http://www.w3.org/2000/svg" role="img"
                focusable="false">
                <title>Expand</title>
                <use xlink:href="../../.././_assets/images/icon__sprite.svg#arrow-down">
                </use>
            </svg>
        </label>
        <div id="c21c8732" class="accordion-collapse" aria-labelledby="2bf6c612"
            data-parent="#accordion1">Lorem ipsum</div>
    </div>
    <div class="accordion-item">
        <input type="checkbox" data-controller="accordion/input" class="accordion-check sr-only"
            id="87a835a4" name="accordion1_second" />
        <label type="button" class="accordion-label" for="87a835a4" id="e526f217"
            data-target="#fd436328" aria-expanded="false" aria-controls="fd436328">
            <h5 class="accordion-item-header">Collapsible Group Item #2</h5>
            <svg class="icon accordion-icon" xmlns="http://www.w3.org/2000/svg" role="img"
                focusable="false">
                <title>Expand</title>
                <use xlink:href="../../.././_assets/images/icon__sprite.svg#arrow-down">
                </use>
            </svg>
        </label>
        <div id="fd436328" class="accordion-collapse" aria-labelledby="e526f217"
            data-parent="#accordion1">Lorem ipsum</div>
    </div>
    <div class="accordion-item">
        <input type="checkbox" data-controller="accordion/input" class="accordion-check sr-only"
            id="9d6254d2" name="accordion1_third" disabled />
        <label type="button" class="accordion-label" for="9d6254d2" id="528eeeec"
            data-target="#a65a90ac" aria-expanded="false" aria-controls="a65a90ac">
            <h5 class="accordion-item-header">Collapsible Group Item #3</h5>
            <svg class="icon accordion-icon" xmlns="http://www.w3.org/2000/svg" role="img"
                focusable="false">
                <title>Expand</title>
                <use xlink:href="../../.././_assets/images/icon__sprite.svg#arrow-down">
                </use>
            </svg>
        </label>
        <div id="a65a90ac" class="accordion-collapse" aria-labelledby="528eeeec"
            data-parent="#accordion1">Lorem ipsum</div>
    </div>
</div>
import "./cakeDOM";

if (typeof window.cake !== "object") {
    window.cake = {};
}

const defaultOptions = {
    elements: [],
    querySelector: '*[data-controller="accordion/input"]'
};

window.cake.accordion = (options = defaultOptions) => {
    options = {
        ...defaultOptions,
        ...options
    };
    //Get All Accordion-Inputs and set Event-Listener (change)
    let accordionInputElements = window.cake.utils.getElements(options.elements, options
        .querySelector);

    accordionInputElements.forEach((accordionInputElement) => {
        accordionInputElement.addEventListener('change', () => {
            let accordionInputLabel = accordionInputElement.nextSibling;

            //Toggle aria-expanded on all other elements, when input is radio (single-variant)
            if (accordionInputElement.getAttribute('type') === 'radio') {
                const accordionInputLabelElement = accordionInputElement
                    .parentElement.siblingSelector('label[aria-expanded=true]');
                if (accordionInputLabelElement) {
                    accordionInputLabelElement.setAttribute('aria-expanded',
                        false);
                }
            }

            accordionInputLabel.setAttribute('aria-expanded',
                accordionInputLabel.getAttribute('aria-expanded') ==
                "false" ? true : false);
        });
    })
};

export default window.cake.accordion;

Instead of a type="checkbox" you can simply use the type="radio" to make only a single accordion-item extensible at once. If you wantan accordion-item to be extended on page load, simply set the input to checked.

single#

preview

<div class="accordion" id="accordion2">
    <div class="accordion-item">
        <input type="radio" data-controller="accordion/input" class="accordion-check sr-only"
            id="d4d4c845" name="accordion2_input" checked />
        <label type="button" class="accordion-label" for="d4d4c845" id="2c3d33de"
            data-target="#b7923d87" aria-expanded="false" aria-controls="b7923d87">
            <h5 class="accordion-item-header">Collapsible Group Item #1</h5>
            <svg class="icon accordion-icon" xmlns="http://www.w3.org/2000/svg" role="img"
                focusable="false">
                <title>Expand</title>
                <use xlink:href="../../.././_assets/images/icon__sprite.svg#arrow-down">
                </use>
            </svg>
        </label>
        <div id="b7923d87" class="accordion-collapse" aria-labelledby="2c3d33de"
            data-parent="#accordion2">Lorem ipsum</div>
    </div>
    <div class="accordion-item">
        <input type="radio" data-controller="accordion/input" class="accordion-check sr-only"
            id="ecaea25e" name="accordion2_input" />
        <label type="button" class="accordion-label" for="ecaea25e" id="86f66cb8"
            data-target="#7d8e8453" aria-expanded="false" aria-controls="7d8e8453">
            <h5 class="accordion-item-header">Collapsible Group Item #2</h5>
            <svg class="icon accordion-icon" xmlns="http://www.w3.org/2000/svg" role="img"
                focusable="false">
                <title>Expand</title>
                <use xlink:href="../../.././_assets/images/icon__sprite.svg#arrow-down">
                </use>
            </svg>
        </label>
        <div id="7d8e8453" class="accordion-collapse" aria-labelledby="86f66cb8"
            data-parent="#accordion2">Lorem ipsum</div>
    </div>
    <div class="accordion-item">
        <input type="radio" data-controller="accordion/input" class="accordion-check sr-only"
            id="17aae0c0" name="accordion2_input" disabled />
        <label type="button" class="accordion-label" for="17aae0c0" id="d68e51ff"
            data-target="#bb64f623" aria-expanded="false" aria-controls="bb64f623">
            <h5 class="accordion-item-header">Collapsible Group Item #3</h5>
            <svg class="icon accordion-icon" xmlns="http://www.w3.org/2000/svg" role="img"
                focusable="false">
                <title>Expand</title>
                <use xlink:href="../../.././_assets/images/icon__sprite.svg#arrow-down">
                </use>
            </svg>
        </label>
        <div id="bb64f623" class="accordion-collapse" aria-labelledby="d68e51ff"
            data-parent="#accordion2">Lorem ipsum</div>
    </div>
</div>
import "./cakeDOM";

if (typeof window.cake !== "object") {
    window.cake = {};
}

const defaultOptions = {
    elements: [],
    querySelector: '*[data-controller="accordion/input"]'
};

window.cake.accordion = (options = defaultOptions) => {
    options = {
        ...defaultOptions,
        ...options
    };
    //Get All Accordion-Inputs and set Event-Listener (change)
    let accordionInputElements = window.cake.utils.getElements(options.elements, options
        .querySelector);

    accordionInputElements.forEach((accordionInputElement) => {
        accordionInputElement.addEventListener('change', () => {
            let accordionInputLabel = accordionInputElement.nextSibling;

            //Toggle aria-expanded on all other elements, when input is radio (single-variant)
            if (accordionInputElement.getAttribute('type') === 'radio') {
                const accordionInputLabelElement = accordionInputElement
                    .parentElement.siblingSelector('label[aria-expanded=true]');
                if (accordionInputLabelElement) {
                    accordionInputLabelElement.setAttribute('aria-expanded',
                        false);
                }
            }

            accordionInputLabel.setAttribute('aria-expanded',
                accordionInputLabel.getAttribute('aria-expanded') ==
                "false" ? true : false);
        });
    })
};

export default window.cake.accordion;

Accessibility#

Be sure to add aria-expanded to the label element. This attribute explicitly conveys the current state of the collapsible element tied to the control to screen readers and similar assistive technologies. If the collapsible element is closed by default, the attribute on the control element should have a value of aria-expanded="false". If you’ve set the collapsible element to be open by default set aria-expanded="true" on the control instead. The plugin will automatically toggle this attribute on the control based on whether or not the collapsible element has been opened or closed (via Javascript).

If your control element is targeting a single collapsible element – i.e. the data-target attribute is pointing to an id selector – you should add the aria-controls attribute to the control element, containing the id of the collapsible element. Modern screen readers and similar assistive technologies make use of this attribute to provide users with additional shortcuts to navigate directly to the collapsible element itself.

JavaScript#

The accordion does work without javascript. But to ensure best accessibility we've created a small script to set the aria-expanded="true" to it's correct state on user interaction. You can import our javascript bundle cake.js to automatically use this feature. You only must ensure that the property data-controller="accrodion/collapse" is set on the input element of the accordion.

Initialization#

To initialize the javascript with default configuration you could simply run the following code:

document.addEventListener ('DOMContentLoaded', () => {
    window.cake.accordion ();
});

Customization#

To customize the default behavior you can only embedd the accordion.js file into your mockups. Then you can initialize the functionality by calling:

document.addEventListener ('DOMContentLoaded', () => {
    window.cake.accordion (options = {
        elements: [],
        querySelector: '*[data-controller="accordion/input"]'
    });
});
  • elements [Array] – provide the specific input elements of your accordion (optional)
  • querySelector [String] – provide a query-selector to select all accordion input elements in your DOM (optional, default: *[data-controller="accordion/input"])

If you do provide the options.elements the options.querySelector option gets ignored. If you do not provide any options.elements always the options.querySelector is used!

Change log#

6.2.0 - 2021-08-19#

Added#

  • Doc: "Accordion" | added javaScript file content as "JS" tab

5.1.0 - 2021-03-22#

Added#

  • SCSS: "Accordion" | added margin-left to the icon, to match the design requirements
  • SCSS: "Accordion" | Added overflow: hidden and text-overflow: ellipsis to the accordion label to hide too much content.

Fixed#

  • SCSS: "Accordion" | fixed margin-top between accordion elements to be exact 8px

5.0.0 - 2021-01-28#

Changed#

  • JS, Doc: "Accordion" | updated javascript to provide options object for better integration of CAKE
  • HTML, Doc: "Accordion" | refactored data-toggle=accordion-collapse to data-controller="accordion/input"
  • SCSS: "Accordion" | add new variables $accordion-max-height and $accordion-padding-y

3.9.0 - 2020-01-16#

Changed#

  • SCSS: "Accordion" | added new height for xs-md

3.8.0 - 2019-11-07#

Added#

  • SCSS: "Accordion" | Transitions are configurable in the variables.scss file

Changed#

  • SCSS: "Accordion" | Icon transition is now configurable $accordion-icon-transition
  • SCSS: "Accordion" | Use mixins for border radius and transition