<ul class="accordion l-section-full__item">
    <li class="accordion__theme--2">
        <h3 class="accordion__heading">
            <button class="accordion__trigger js-accordion__trigger" aria-expanded="false" aria-controls="accordion--multiple--content--1">
                <div class="accordion__heading-inner">
                    <div class="accordion__heading-col accordion__trigger-icon">
                        <img src="/assets/img/accordion-icon-2.svg">
                    </div>
                    <div class="accordion__heading-col accordion__trigger-text">Aims & objectives</div>

                    <div class="accordion__heading-col accordion__trigger-chevron">

                        <svg viewBox="0 0 15 20" class="a-icon__svg a-icon__svg--x2">
                            <use xlink:href="#icon__chevron"></use>
                        </svg>

                    </div>
                </div>
            </button>
        </h3>
        <div class="accordion__content" id="accordion--multiple--content--1">
            <div class="accordion__content-inner">
                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi culpa dolorum enim molestiae molestias nemo nulla quas sed, temporibus voluptatem!</p>
            </div>
        </div>
    </li>
    <li class="accordion__theme--3">
        <h3 class="accordion__heading">
            <button class="accordion__trigger js-accordion__trigger" aria-expanded="false" aria-controls="accordion--multiple--content--2">
                <div class="accordion__heading-inner">
                    <div class="accordion__heading-col accordion__trigger-icon">
                        <img src="/assets/img/accordion-icon-3.svg">
                    </div>
                    <div class="accordion__heading-col accordion__trigger-text">Methods</div>

                    <div class="accordion__heading-col accordion__trigger-chevron">

                        <svg viewBox="0 0 15 20" class="a-icon__svg a-icon__svg--x2">
                            <use xlink:href="#icon__chevron"></use>
                        </svg>

                    </div>
                </div>
            </button>
        </h3>
        <div class="accordion__content" id="accordion--multiple--content--2">
            <div class="accordion__content-inner">
                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi culpa dolorum enim molestiae molestias nemo nulla quas sed, temporibus voluptatem!</p>
            </div>
        </div>
    </li>
    <li class="accordion__theme--4">
        <h3 class="accordion__heading">
            <button class="accordion__trigger js-accordion__trigger" aria-expanded="false" aria-controls="accordion--multiple--content--3">
                <div class="accordion__heading-inner">
                    <div class="accordion__heading-col accordion__trigger-icon">
                        <img src="/assets/img/accordion-icon-4.svg">
                    </div>
                    <div class="accordion__heading-col accordion__trigger-text">Plicy relevance & dissemination</div>

                    <div class="accordion__heading-col accordion__trigger-chevron">

                        <svg viewBox="0 0 15 20" class="a-icon__svg a-icon__svg--x2">
                            <use xlink:href="#icon__chevron"></use>
                        </svg>

                    </div>
                </div>
            </button>
        </h3>
        <div class="accordion__content" id="accordion--multiple--content--3">
            <div class="accordion__content-inner">
                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi culpa dolorum enim molestiae molestias nemo nulla quas sed, temporibus voluptatem!</p>
            </div>
        </div>
    </li>
</ul>
<ul class="accordion l-section-full__item">
  {% for item in items %}
    <li class="accordion__theme--{{item.theme}}">
      <h3 class="accordion__heading">
        <button class="accordion__trigger js-accordion__trigger"
                aria-expanded="false"
                aria-controls="accordion--multiple--content--{{ loop.index }}">
          <div class="accordion__heading-inner">
            <div class="accordion__heading-col accordion__trigger-icon">
              <img src="/assets/img/accordion-icon-{{item.theme}}.svg">
            </div>
            <div class="accordion__heading-col accordion__trigger-text">{{ item.buttonText }}</div>
  
            <div class="accordion__heading-col accordion__trigger-chevron">
              {% render '@icon--icon__chevron' with {'class' : 'a-icon__svg--x2'} %}
            </div>
          </div>
        </button>
      </h3>
      <div class="accordion__content"
           id="accordion--multiple--content--{{ loop.index }}">
           <div class="accordion__content-inner">
        {{ item.content }}
        </div>
      </div>
    </li>
  {% endfor %}
</ul>
/* No context defined. */
  • Content:
    .accordion {
        margin: 0;
    
        &__heading {
            margin: 0;
            font-size: rem(22);
            color: setcolor(1);
            font-weight: 400;
            @include mappy-bp(small) {
                font-size: rem(40);
            }
            &-inner {
                display: grid;
                grid-template-columns: repeat(12, 1fr);
                grid-gap: 30px;
                max-width: $width-max;
                margin: 0 auto; 
            }
    
            &-col {
                display: flex;
                align-items: center;
            }
            button {
                color: inherit;
                font-size: inherit;
            }
        }
        >li, >.accordion__item {
            margin:0;
            list-style: none;
            padding-left: 0;
    
            
            
        }
        
        dd {
            padding: 0;
          }
    
        &__trigger {
            background: none;
            display: grid;
            grid-template-columns: 1fr;
            color: hsl(0deg 0% 13%);
            padding-left:rem(20);
            font-weight: 400;
            font-size: 1rem;
            margin: 0;
            padding: $elem-pad-sm;
            position: relative;
            text-align: left;
            width: 100%;
            outline: none;
            border: 0;
            border-bottom: 5px solid setcolor('white');
            
    
            padding:$elem-pad-sm $elem-pad;
            // @include mappy-bp(small) {
            //     padding:$elem-pad-lrg $elem-pad $elem-pad $elem-pad;
            // }
    
    
            &-text {
                grid-column: span 11;
    
                @include mappy-bp(small) {
                    grid-column: span 10;
                }
            }
            
            &-icon {
                display: none;
            @include mappy-bp(small) {
                display: block;
            }
            }
            &-chevron {
                display: flex;
                color:setcolor(2);
                svg {
                    transition: transform .2s ease-in-out;
                }
                .a-icon__svg {
                    width: 22px;
                    height: 22px;
                    @include mappy-bp(small) {
                        width: 40px;
                        height: 40px;
                    }
                }
                //transform: rotate(90deg) ;
                //color:setcolor(1);
            }
            // &::after {
            //     position: absolute;
            //     content: url("data:image/svg+xml,%3Csvg width='12' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 1.604C0 1.157.269.755.682.584a1.105 1.105 0 0 1 1.202.24L6 4.938 10.116.823a1.103 1.103 0 1 1 1.56 1.562L6.78 7.28a1.103 1.103 0 0 1-.78.323 1.103 1.103 0 0 1-.78-.323L.324 2.385A1.101 1.101 0 0 1 0 1.604Z' fill='%23121212'/%3E%3C/svg%3E");
            //     right: 1rem;
            //     top: 1rem;
            //     transition: transform .2s ease-in-out;
            // }
    
            &[aria-expanded=true] {
                .accordion__trigger-chevron {
                    svg {
                        transform: rotate(90deg);
                    }
                    //color: #ffffff;
                }
                // &::after {
                //     transform: rotate(-180deg);
                // }
            }
    
            // &:focus, &:hover, &[aria-expanded="true"] {
            //    color: #ffffff;
            //     background: setcolor(3);
            // }
    
             &:hover {
            //    color: #ffffff;
                background: setcolor(9);
            }
    
            &:focus {
                outline: 4px solid transparent;
            }
        }
    
        &__content {
            transition: all .5s cubic-bezier(.41,.97,.36,.78);
            overflow: hidden;
            border-bottom: 5px solid setcolor('white');
            &-inner {
                padding: $elem-pad;
                max-width: $width-max;
                margin: 0 auto; 
    
               & > :last-child {
                    margin-bottom: 0;
                }
    
                @include mappy-bp(max) {
                    padding:$elem-pad 0;
                }
            }
        }
        &--expanded {
         //   margin: rem(15) rem(15) rem(10) rem(20);
        }
    
    
        &--alt {
            >li, >.accordion__item {
                margin-bottom: $elem-pad;
            }
    
            // .accordion__heading {
            //     border-bottom: none;
            // }
    
            .accordion__trigger {
                border: none;
                //padding:$elem-pad 0;
                // margin:0 $elem-pad;
                // width: calc(100% - ($elem-pad * 2));
    
                // &:hover {
                //     background-color: none;
                // }
            }
            .accordion__content-inner {
                padding-left: 0;
                padding-right: 0;
                margin:0 $elem-pad;
                border-top:1px solid setcolor(1);
                
            }
        }
    }
    
    .property {
        .accordion__content-inner {
            border-top: 0;;
        }
        .accordion__content-inner {
            padding-top: 5px;
        }
    }
    
    
    
    .accordion__theme {
    
        &--1 {
            .accordion {
                &__trigger {
                    background-color: $theme-color-2;
    
                    &:hover {
                        //background-color: setcolor(8);
                    }
                }
                &__content {
                    background-color: $theme-color-2--shade;
                }
            }
        }
    
        &--2 {
            .accordion {
                &__trigger {
                    background-color: $theme-color-3;
    
                    &:hover {
                        //background-color: setcolor(10);
                    }
                }
                &__content {
                    background-color: $theme-color-3--shade;
                }
            }
        }
    
        &--3 {
            .accordion {
                &__trigger {
                    background-color: $theme-color-4;
    
                    &:hover {
                        //background-color: setcolor(12);
                    }
                }
                &__content {
                    background-color: $theme-color-4--shade;
                }
            }
        }
    }
  • URL: /components/raw/accordion/_accordion.scss
  • Filesystem Path: components/accordion/_accordion.scss
  • Size: 5.7 KB
  • Content:
    import Accordion from './accordion.js';
    
    (function () {
        if (!Accordion) { // eslint-disable-line no-undef
            return;
        }
    
        const selected = document.querySelectorAll('.accordion');
        for (let i = selected.length; i--;) {
            new Accordion(selected[i]); // eslint-disable-line no-undef
        }
    }());
    
  • URL: /components/raw/accordion/accordion.bindings.js
  • Filesystem Path: components/accordion/accordion.bindings.js
  • Size: 317 Bytes
  • Content:
    export default function (elem, options) {
        const expandedContent = [];
    
        /**
         * Override default options with options param.
         */
        options = (() => {
            const defaults = {
                expand: (button, content) => {
                    content.style.maxHeight = `${content.scrollHeight}px`;
                },
                collapse: (button, content) => {
                    content.style.maxHeight = 0;
                },
                transitionEnd: (e) => {
                    if (e.propertyName !== "max-height") {
                        return;
                    }
                    if (
                        !e.currentTarget.classList.contains("accordion--expanded")
                    ) {
                        e.currentTarget.setAttribute("hidden", "hidden");
                    }
                },
                resizeEvent: (e, expandedContent) => {
                    for (let i = expandedContent.length; i--; ) {
                        options.expand(null, expandedContent[i]);
                    }
                },
                init: true,
                buttonSelector: "button.js-accordion__trigger",
                accordionExpandedClass: "accordion--expanded",
                expandMultiple: false,
                disableHash: false,
            };
            const keys = Object.keys(defaults);
            options = options || {};
    
            for (let i = keys.length; i--; ) {
                options[keys[i]] = options[keys[i]] || defaults[keys[i]];
            }
    
            if (elem.classList.contains("accordion--multiple")) {
                options.expandMultiple = true;
            }
            if (elem.classList.contains("accordion--nohash")) {
                options.disableHash = true;
            }
    
            return options;
        })();
    
        const buttons = elem.querySelectorAll(options.buttonSelector);
    
        /**
         * Toggle aria-expanded attributes and trigger visibility Change function.
         *
         * @param {event} e The triggered event.
         */
        const toggle = (e) => {
            e.preventDefault();
            const button = e.currentTarget;
            if (!options.expandMultiple) {
                closeAllExcept(button);
            }
            button.setAttribute(
                "aria-expanded",
                button.getAttribute("aria-expanded") === "true" ? "false" : "true"
            );
            setVisibility(button);
    
            // If the accordion is being expanded, update the URL hash
            if (button.getAttribute("aria-expanded") === "true") {
                const newPath = `${window.location.pathname}#${button.getAttribute(
                    "aria-controls"
                )}`;
    
                // Set the userTriggered flag to true because this is a user action
                // userTriggered = true;
    
                // Change the URL hash
                if (!options.disableHash) {
                    history.pushState(null, null, newPath);
                }
                
            }
        };
    
        /**
         * Handle keyboard input: arrows, home & end.
         *
         * @param {event} e The triggered event.
         */
        const keyDown = (e) => {
            const keyCode = e.keyCode || e.which;
            let current = (() => {
                for (let i = buttons.length; i--; ) {
                    if (buttons[i] === e.currentTarget) {
                        return i;
                    }
                }
            })();
            switch (keyCode) {
                case 40: // arrow down
                    e.preventDefault();
                    if (current === buttons.length - 1) {
                        current = -1;
                    }
                    buttons[current + 1].focus();
                    break;
                case 38: // arrow up
                    e.preventDefault();
                    if (current === 0) {
                        current = buttons.length;
                    }
                    buttons[current - 1].focus();
                    break;
                case 36: // home
                    e.preventDefault();
                    buttons[0].focus();
                    break;
                case 35: // end
                    e.preventDefault();
                    buttons[buttons.length - 1].focus();
                    break;
            }
        };
    
        /**
         * Add events to buttons.
         * Listen for animationEnd on accordionContent.
         */
        const addEvents = () => {
            const onResize = (e) => {
                options.resizeEvent(e, expandedContent);
            };
    
            for (let i = buttons.length; i--; ) {
                const button = buttons[i];
                button.addEventListener("click", toggle);
                button.addEventListener("keydown", keyDown);
    
                const accordionContent = elem.querySelector(
                    `#${button.getAttribute("aria-controls")}`
                );
                accordionContent.addEventListener(
                    "transitionend",
                    options.transitionEnd
                );
    
                if (options.resizeEvent) {
                    window.addEventListener("resize", onResize);
                }
            }
    
            window.addEventListener("hashchange", hashEvent);
        };
    
        /**
         * Hide or show the accordion content.
         *
         * @param {Object} button  The accordion button.
         * @param {boolean|false} isInitial  True if this is the first run
         *   triggered by init().
         */
        const setVisibility = (button, isInitial) => {
            const accordionContent = elem.querySelector(
                `#${button.getAttribute("aria-controls")}`
            );
            if (!accordionContent) {
                return;
            }
    
            if (button.getAttribute("aria-expanded") === "true") {
                accordionContent.classList.add(options.accordionExpandedClass);
                accordionContent.setAttribute("aria-hidden", "false");
                accordionContent.removeAttribute("hidden");
                expandedContent.push(accordionContent);
                options.expand(button, accordionContent);
            } else {
                accordionContent.classList.remove(options.accordionExpandedClass);
                accordionContent.setAttribute("aria-hidden", "true");
                if (isInitial) {
                    accordionContent.setAttribute("hidden", "hidden");
                }
                expandedContent.filter((content) => content !== accordionContent);
                options.collapse(button, accordionContent);
            }
        };
    
        /**
         * Set all attributes and toggle visibility accordingly.
         */
        const setInitial = () => {
            for (let i = buttons.length; i--; ) {
                setVisibility(buttons[i], true);
            }
        };
    
        const close = (button) => {
            button.setAttribute("aria-expanded", "false");
            setVisibility(button);
        };
    
        /**
         * Closes all accordion items.
         */
        const closeAll = () => {
            for (let i = buttons.length; i--; ) {
                close(buttons[i]);
            }
        };
    
        /**
         * Closes all accordion items.
         */
        const closeAllExcept = (button) => {
            for (let i = buttons.length; i--; ) {
                if (buttons[i] !== button) {
                    close(buttons[i]);
                }
            }
        };
    
        const open = (button) => {
            button.setAttribute("aria-expanded", "true");
            setVisibility(button);
        };
    
        /**
         * Opens all accordion items.
         */
        const openAll = () => {
            for (let i = buttons.length; i--; ) {
                open(buttons[i]);
            }
        };
    
        /**
         * Open the accordion-content related to the location hash.
         */
        const hashEvent = () => {
            const hash = window.location.hash.replace("#", "");
            if (!hash) {
                return;
            }
            try {
                const trigger = elem.querySelector(`[aria-controls=${hash}]`);
                if (trigger) {
                    // Close all accordions except the one we want to open if expandMultiple is false
                    if (!options.expandMultiple) {
                        closeAllExcept(trigger);
                    }
                    open(trigger);
                    // Instead of directly focusing, we wait for the transition to end,
                    const accordionContent = elem.querySelector(
                        `#${trigger.getAttribute("aria-controls")}`
                    );
                    accordionContent.addEventListener(
                        "transitionend",
                        function () {
                            trigger.focus();
                        },
                        { once: true }
                    ); // Use 'once' option to ensure this listener only fires one time.
                }
            } catch (e) {}
        };
    
        /**
         * Enable accordion functionality.
         */
        const init = () => {
            setInitial();
            addEvents();
            hashEvent();
        };
    
        if (options.init !== false) {
            init();
        }
    
        return { init, closeAll, openAll };
    }
    
  • URL: /components/raw/accordion/accordion.js
  • Filesystem Path: components/accordion/accordion.js
  • Size: 8.6 KB

Accordion

When to use this component

An accordion component is used to:

  • Show an overview of sections of multiple, related sections of content, that can be read independently from each other and which users typically are not interested to see all, but rather one or a few specific sections.
  • Make it possible for users to show or hide those section as needed.
  • Hide a simple piece or section of content that is advanced or that is only necessary for a minority of users (progressive disclosure principle).

When not to use this component

  • Do not use an accordion component to hide content that is essential to all users.
  • Do not use an accordion component when the amount of content in the items (when expanded) will make the page too long or too slow to load. Only use accordion components for simple content.

Alternatives to using an accordion component:

  • Reduce and simplify the content so that it can be placed as is on the page, without hiding it or the need for an accordion component.
  • Split the content accross multiple pages, especially if the amount of content is large (more than one text paragraph or if also other components than text paragraphs are needed to convey the message).
  • Keep the content as is on the page, separate the content by headings.
  • Use a table of contents component to let users navigate quickly to specific sections of content.

How it works

There are two types of accordions:

  • Single expandable item
  • Multiple expandable items

Adding the class ‘dropdown’ will add the following behaviour: when you click on an element that isn’t a dropdown, all dropdowns will be closed. This behaviour is used in the case of an application that doesn’t refresh when clicking a button or link.

Single expandable item

Accordions with one single expandable item are displayed with a chevron on the right-hand side.

  • When collapsed, the chevron points down, to indicate that it can be expanded.
  • When expanded, the chevron points up, to indicate that it can be collapsed.

By default, the single expandable item is collapsed.

The single expandable items has a label that tells what content is in the item. Do not start the label with a verb that describes the collapsing or expanding. This is not necessary because the item is already in an accordion. For example, if the item is a list of opening hours deze week, don’t use a label “Show opening hours this week” but use “Opening hours this week” instead.

Multiple expandable items

Accordions with multiple expandable items are displayed in a list of items with labels with a plus or a minus icon on the left-hand side of each item.

  • When an item is collapsed, the plus icon is shown, to indicate that it can be expanded.
  • When an item is expanded, the minus icon is shown, to indicate that it can be collapsed.

By default, all the expandable items are collapsed. Multiple items can be expanded at the same time.

The expandable items each have a label that tells what content is in the item. Do not start the label with a verb that describes the collapsing or expanding. This is not necessary because the item is already in an accordion. For example, if an item is about transportation by train, don’t use a label “Show transportation by train” but use “Transportation by train” instead.

Markup

  • Multiple expandable items must always be contained in an unordered- or description list.
  • The toggle must be of type button and preferably wrapped in an element with (implicit) role header.
  • The toggle button has both aria-expanded to indicate it’s current state and aria-controls to indicate which element it controls.

Javascript

Usage

Create a new accordion object by running:

new Accordion(element);

Where element contains a button with:

  • default class: accordion--button
  • aria-expanded (true or false)
  • aria-controls, the unique ID of the collapsible element

And a collapsible element with:

  • default class: accordion--content

By default, the accordion will initiate automatically and hide or show the content according to the aria-expanded attribute.
If the hash in the URL contains a matching ID, this element will also be expanded.

For instance:

<div class="accordion">
    <h3>
        <button aria-controls="accordion--content-ID"
                aria-expanded="false"
                class="accordion--button">
            single accordion
        </button>
    </h3>
    <div class="accordion--content" id="accordion--content-ID">
        ...
    </div>
</div>

Options

Option Type Default Description
expand function function(button, content) Function triggered after the expanded class is added, the ‘hidden’ attribute is removed from the content and aria-hidden is set to false.
collapse function function(button, content) Function triggered after the expanded class is removed and aria-hidden is set to true. By default, the ‘hidden’ attribute is set in the transitionEnd function
transitionEnd function function(event) Triggered for each transitionEnd event, use this to add the ‘hidden’ attribute after the content has been transitioned out of view.
resizeEvent function function(event, expandedContent) ExpandedContent is an array containing all expanded elements. Use this to trigger the ‘expand’ function on window.resize
init Boolean true Set to false if you want to manually initiate the accordion object (object.init())
buttonSelector String 'button.accordion--button' QuerySelector to identify the accordion trigger button
accordionExpandedClass String 'accordion--expanded' Determine which class is added to the expanded content.

Functions

Function Description
init() Manually initiate the accordion, this will expand or collapse all content according to the aria-expanded state
closeAll() Close all collapsible content in this accordion
openAll() Open all collapsible content in this accordion