Skip to main content

Collapsible

The collapsible component is used to simplify a page by allowing the user to reveal certain content only if they need it.

<div id="collapsible" class="collapsible js-collapsible" data-btn-close="Hide this">
  <div class="collapsible__heading js-collapsible-heading">
    <div class="collapsible__controls">
      <h2 class="collapsible__title">What is a photovoltaic system?</h2>
      <span class="collapsible__icon">
        <svg class="svg-icon" viewBox="0 0 8 13" xmlns="http://www.w3.org/2000/svg" focusable="false">
          <path d="M5.74,14.28l-.57-.56a.5.5,0,0,1,0-.71h0l5-5-5-5a.5.5,0,0,1,0-.71h0l.57-.56a.5.5,0,0,1,.71,0h0l5.93,5.93a.5.5,0,0,1,0,.7L6.45,14.28a.5.5,0,0,1-.71,0Z" transform="translate(-5.02 -1.59)" />
        </svg>
      </span>
    </div>
  </div>
  <div id="collapsible-content" class="collapsible__content js-collapsible-content">
    <p>A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky.</p>
    <button type="button" class="btn btn--small js-collapsible-button u-d-no btn--secondary" aria-hidden="true">
      <span class="btn__inner js-collapsible-button-inner">Hide this</span>
      <span class="btn__context u-vh">What is a photovoltaic system? content</span>
    </button>
  </div>
</div>
Nunjucks macro options
Name Type Required Description
id string true id for the collapsible
title string true The title for the collapsible
titleTag string false The HTML tag to wrap the title text in. Will default to a div
content string true HTML content for the collapsible
button CollapsibleButton false Settings for the close button. If not specified button will not render
classes string false Classes to add to the collapsible element
saveState boolean false Allows saving of collapsible state (open or closed) locally
attributes object false HTML attributes (for example data attributes) to add to the collapsible element
headingAttributes object false HTML attributes (for example data attributes) to add to the collapsible header element
contentAttributes object false HTML attributes (for example data attributes) to add to the collapsible content element

CollapsibleButton

Name Type Required Description
close string true Text for the button when the collapsible are open
context string false First part of a visually hidden text string appended to the close button text to add context for screen readers. Defaults to value of title if empty, and is appended with value of contextSuffix (e.g. “Hide this ‘What is a photovoltaic system?’ content”)
contextSuffix string true Second part of a visually hidden text string appended to the context text string to build a readable sentence for adding context to the close button for screen readers (e.g. “Hide this ‘What is a photovoltaic system?’ content“)
classes string false Classes to add to the close button. If classes are provided the btn--secondary class will not be applied by default
attributes object false HTML attributes (for example data attributes) to add to the button
{% from "components/collapsible/_macro.njk" import onsCollapsible %}
{{
    onsCollapsible({
        "id": "collapsible",
        "title": "What is a photovoltaic system?",
        "titleTag": "h2",
        "content": "<p>A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky.</p>",
        "button": {
            "close": "Hide this",
            "contextSuffix": "content",
            "attributes": {
                "aria-hidden": "true"
            }
        }
    })
}}

{% macro onsCollapsible(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    <div
        id="{{ params.id }}"
        class="collapsible js-collapsible{% if params.isAccordion is defined and params.isAccordion %} collapsible--accordion{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}"
        {% if params.button is defined and params.button and params.button.close is defined and params.button.close %} data-btn-close="{{ params.button.close }}"{% endif %}
        {% if params.group is defined and params.group %} data-group="{{ params.group }}"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        {% if params.saveState is defined and params.saveState %} data-save-state="true"{% endif %}
    >
        <div
            class="collapsible__heading js-collapsible-heading"
            {% if params.headingAttributes is defined and params.headingAttributes %}{% for attribute, value in (params.headingAttributes.items() if params.headingAttributes is mapping and params.headingAttributes.items else params.headingAttributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >
            {# Required to use display flex on Safari as it's not currently supported: https://bugs.webkit.org/show_bug.cgi?id=167111 #}
            <div class="collapsible__controls">
                {% set titleTag = params.titleTag | default("div") %}
                <{{ titleTag }} class="collapsible__title">{{ params.title }}</{{ titleTag }}>
                <span class="collapsible__icon">
                    {% from "components/icons/_macro.njk" import onsIcon %}
                    {{
                        onsIcon({
                            "icon": "chevron"
                        })
                    }}</span>
                {% if params.button is defined and params.button and params.button.open is defined and params.button.open and params.isAccordion is defined and params.isAccordion %}
                    {{
                        onsButton({
                            "type": "button",
                            "text": params.button.open,
                            "classes": "collapsible__btn btn--secondary btn--small js-collapsible-button u-d-no u-d-no@xxs@s",
                            "innerClasses": "js-collapsible-button-inner",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            </div>
        </div>
        <div id="{{ params.id }}-content" class="collapsible__content js-collapsible-content"
            {% if params.contentAttributes is defined and params.contentAttributes %}{% for attribute, value in (params.contentAttributes.items() if params.contentAttributes is mapping and params.contentAttributes.items else params.contentAttributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >
            {% if params.isAccordion is defined and params.isAccordion %}
                {{ params.content | safe }}{{ caller() if caller }}
            {% else %}
                {{ params.content | safe }}{{ caller() if caller }}
                {% if params.button is defined and params.button and params.button.close is defined and params.button.close and params.isAccordion != true %}
                    {{
                        onsButton({
                            "type": "button",
                            "text": params.button.close,
                            "buttonContext": (params.button.context | default(params.title)) + " " + params.button.contextSuffix,
                            "classes": "btn--small js-collapsible-button u-d-no " + (params.button.classes | default("btn--secondary")),
                            "innerClasses": "js-collapsible-button-inner",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            {% endif %}
        </div>
    </div>
{% endmacro %}

$collapsible-caret-width: 1.5rem;
.collapsible {
  &--initialised & {
    &__heading {
      color: $color-text-link;
      cursor: pointer;
      display: inline-block;
      outline: none;
      padding: 0 0 0 $collapsible-caret-width;
      pointer-events: initial;
      position: relative;
      &::marker {
        display: none;
      }
      &:focus {
        .collapsible__title {
          @extend a:focus;
          // extend collapsible focus background behind caret
          margin-left: -$collapsible-caret-width;
          padding-left: $collapsible-caret-width;
        }
        .collapsible__icon {
          fill: $color-black;
        }
      }
      &:hover {
        color: $color-text-link-hover;
        .collapsible__icon {
          fill: $color-text-link-hover;
        }
        .collapsible__title {
          text-decoration: underline solid $color-text-link-hover 2px;
        }
      }
    }
    &__icon {
      display: inline-block;
      fill: $color-text-link;
      height: $collapsible-caret-width;
      left: -0.2rem;
      position: absolute;
      top: -0.25rem;
      width: $collapsible-caret-width;
    }
    &__title {
      font-size: 1rem;
      font-weight: $font-weight-bold;
      margin-bottom: 0;
      text-decoration: underline;
      text-underline-position: under;
      transform: translateY(-1px);
    }
    &__content {
      display: none;
    }
  }
  &__icon {
    display: none;
  }
  &--open & {
    &__icon {
      left: -0.1rem;
      top: 0.1rem;
      transform: rotate(90deg);
    }
    &__content {
      border-left: 5px solid $color-grey-15;
      display: block;
      margin: 1rem 0 0;
      padding: 0 0 0 1.3em;
    }
  }
  &--accordion {
    margin: 0;
    &.collapsible--open {
      .svg-icon {
        left: 0.4rem;
        position: absolute;
        top: 0.25rem;
      }
    }
  }
  &--accordion & {
    &__heading {
      border-top: 1px solid $color-borders;
      margin: 0;
      padding-bottom: 1rem;
      padding-top: 1rem;
      width: 100%;
      &:focus {
        outline: none;
      }
    }
    &__title {
      margin: 0 1rem 0 0;
    }
    &__icon {
      top: 0.8rem;
      @include mq(s) {
        top: 1.15rem;
      }
    }
    &__controls {
      align-items: center;
      display: flex;
      justify-content: space-between;
    }
    &__btn {
      align-self: flex-start;
      width: auto;
    }
    &__content {
      border-left: 0;
      margin: 0;
      padding: 0;
    }
  }
}

When to use this component

Use the collapsible component to make a page easier to read by condensing information that is only needed by some users.

The collapsible can be used to condense a single block of content into a single selectable heading, allowing the user to reveal it if and when they need it. It also includes a hide buttton to close the collapsible when needed. We recommend adding aria-hidden="true" to the button in order so that screen reading software will not pick this up.

Good uses of the collapsible component are:

  • to reveal additional help at the point where a user may need it e.g. to help answer a question
  • to show the definition of a term that may not be well understood

When not to use this component

Don’t use the collapsible component to hide information that the majority of your users will need to progress with their task.

Alternative components

Accordions, tabs and collapsible all hide sections of content which a user can show or hide.

Variants

Use the collapsible component instead of tabs or accordions if there is only one section of content that needs to be condensed.

Collapsible with save state enabled

<div id="collapsible-with-save" class="collapsible js-collapsible" data-btn-close="Hide this" data-save-state="true">
  <div class="collapsible__heading js-collapsible-heading">
    <div class="collapsible__controls">
      <h2 class="collapsible__title">What is a photovoltaic system?</h2>
      <span class="collapsible__icon">
        <svg class="svg-icon" viewBox="0 0 8 13" xmlns="http://www.w3.org/2000/svg" focusable="false">
          <path d="M5.74,14.28l-.57-.56a.5.5,0,0,1,0-.71h0l5-5-5-5a.5.5,0,0,1,0-.71h0l.57-.56a.5.5,0,0,1,.71,0h0l5.93,5.93a.5.5,0,0,1,0,.7L6.45,14.28a.5.5,0,0,1-.71,0Z" transform="translate(-5.02 -1.59)" />
        </svg>
      </span>
    </div>
  </div>
  <div id="collapsible-with-save-content" class="collapsible__content js-collapsible-content">
    <p>A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky.</p>
    <button type="button" class="btn btn--small js-collapsible-button u-d-no btn--secondary" aria-hidden="true">
      <span class="btn__inner js-collapsible-button-inner">Hide this</span>
      <span class="btn__context u-vh">What is a photovoltaic system? undefined</span>
    </button>
  </div>
</div>
Nunjucks macro options
Name Type Required Description
id string true id for the collapsible
title string true The title for the collapsible
titleTag string false The HTML tag to wrap the title text in. Will default to a div
content string true HTML content for the collapsible
button CollapsibleButton false Settings for the close button. If not specified button will not render
classes string false Classes to add to the collapsible element
saveState boolean false Allows saving of collapsible state (open or closed) locally
attributes object false HTML attributes (for example data attributes) to add to the collapsible element
headingAttributes object false HTML attributes (for example data attributes) to add to the collapsible header element
contentAttributes object false HTML attributes (for example data attributes) to add to the collapsible content element

CollapsibleButton

Name Type Required Description
close string true Text for the button when the collapsible are open
context string false First part of a visually hidden text string appended to the close button text to add context for screen readers. Defaults to value of title if empty, and is appended with value of contextSuffix (e.g. “Hide this ‘What is a photovoltaic system?’ content”)
contextSuffix string true Second part of a visually hidden text string appended to the context text string to build a readable sentence for adding context to the close button for screen readers (e.g. “Hide this ‘What is a photovoltaic system?’ content“)
classes string false Classes to add to the close button. If classes are provided the btn--secondary class will not be applied by default
attributes object false HTML attributes (for example data attributes) to add to the button
{% from "components/collapsible/_macro.njk" import onsCollapsible %}
{{
    onsCollapsible({
        "id": "collapsible-with-save",
        "title": "What is a photovoltaic system?",
        "titleTag": "h2",
        "content": "<p>A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky.</p>",
        "button": {
            "close": "Hide this",
            "attributes": {
                "aria-hidden": "true"
            }
        },
        "saveState": "true"
    })
}}

{% macro onsCollapsible(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    <div
        id="{{ params.id }}"
        class="collapsible js-collapsible{% if params.isAccordion is defined and params.isAccordion %} collapsible--accordion{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}"
        {% if params.button is defined and params.button and params.button.close is defined and params.button.close %} data-btn-close="{{ params.button.close }}"{% endif %}
        {% if params.group is defined and params.group %} data-group="{{ params.group }}"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        {% if params.saveState is defined and params.saveState %} data-save-state="true"{% endif %}
    >
        <div
            class="collapsible__heading js-collapsible-heading"
            {% if params.headingAttributes is defined and params.headingAttributes %}{% for attribute, value in (params.headingAttributes.items() if params.headingAttributes is mapping and params.headingAttributes.items else params.headingAttributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >
            {# Required to use display flex on Safari as it's not currently supported: https://bugs.webkit.org/show_bug.cgi?id=167111 #}
            <div class="collapsible__controls">
                {% set titleTag = params.titleTag | default("div") %}
                <{{ titleTag }} class="collapsible__title">{{ params.title }}</{{ titleTag }}>
                <span class="collapsible__icon">
                    {% from "components/icons/_macro.njk" import onsIcon %}
                    {{
                        onsIcon({
                            "icon": "chevron"
                        })
                    }}</span>
                {% if params.button is defined and params.button and params.button.open is defined and params.button.open and params.isAccordion is defined and params.isAccordion %}
                    {{
                        onsButton({
                            "type": "button",
                            "text": params.button.open,
                            "classes": "collapsible__btn btn--secondary btn--small js-collapsible-button u-d-no u-d-no@xxs@s",
                            "innerClasses": "js-collapsible-button-inner",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            </div>
        </div>
        <div id="{{ params.id }}-content" class="collapsible__content js-collapsible-content"
            {% if params.contentAttributes is defined and params.contentAttributes %}{% for attribute, value in (params.contentAttributes.items() if params.contentAttributes is mapping and params.contentAttributes.items else params.contentAttributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >
            {% if params.isAccordion is defined and params.isAccordion %}
                {{ params.content | safe }}{{ caller() if caller }}
            {% else %}
                {{ params.content | safe }}{{ caller() if caller }}
                {% if params.button is defined and params.button and params.button.close is defined and params.button.close and params.isAccordion != true %}
                    {{
                        onsButton({
                            "type": "button",
                            "text": params.button.close,
                            "buttonContext": (params.button.context | default(params.title)) + " " + params.button.contextSuffix,
                            "classes": "btn--small js-collapsible-button u-d-no " + (params.button.classes | default("btn--secondary")),
                            "innerClasses": "js-collapsible-button-inner",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            {% endif %}
        </div>
    </div>
{% endmacro %}

$collapsible-caret-width: 1.5rem;
.collapsible {
  &--initialised & {
    &__heading {
      color: $color-text-link;
      cursor: pointer;
      display: inline-block;
      outline: none;
      padding: 0 0 0 $collapsible-caret-width;
      pointer-events: initial;
      position: relative;
      &::marker {
        display: none;
      }
      &:focus {
        .collapsible__title {
          @extend a:focus;
          // extend collapsible focus background behind caret
          margin-left: -$collapsible-caret-width;
          padding-left: $collapsible-caret-width;
        }
        .collapsible__icon {
          fill: $color-black;
        }
      }
      &:hover {
        color: $color-text-link-hover;
        .collapsible__icon {
          fill: $color-text-link-hover;
        }
        .collapsible__title {
          text-decoration: underline solid $color-text-link-hover 2px;
        }
      }
    }
    &__icon {
      display: inline-block;
      fill: $color-text-link;
      height: $collapsible-caret-width;
      left: -0.2rem;
      position: absolute;
      top: -0.25rem;
      width: $collapsible-caret-width;
    }
    &__title {
      font-size: 1rem;
      font-weight: $font-weight-bold;
      margin-bottom: 0;
      text-decoration: underline;
      text-underline-position: under;
      transform: translateY(-1px);
    }
    &__content {
      display: none;
    }
  }
  &__icon {
    display: none;
  }
  &--open & {
    &__icon {
      left: -0.1rem;
      top: 0.1rem;
      transform: rotate(90deg);
    }
    &__content {
      border-left: 5px solid $color-grey-15;
      display: block;
      margin: 1rem 0 0;
      padding: 0 0 0 1.3em;
    }
  }
  &--accordion {
    margin: 0;
    &.collapsible--open {
      .svg-icon {
        left: 0.4rem;
        position: absolute;
        top: 0.25rem;
      }
    }
  }
  &--accordion & {
    &__heading {
      border-top: 1px solid $color-borders;
      margin: 0;
      padding-bottom: 1rem;
      padding-top: 1rem;
      width: 100%;
      &:focus {
        outline: none;
      }
    }
    &__title {
      margin: 0 1rem 0 0;
    }
    &__icon {
      top: 0.8rem;
      @include mq(s) {
        top: 1.15rem;
      }
    }
    &__controls {
      align-items: center;
      display: flex;
      justify-content: space-between;
    }
    &__btn {
      align-self: flex-start;
      width: auto;
    }
    &__content {
      border-left: 0;
      margin: 0;
      padding: 0;
    }
  }
}

Collapsible with warning panel

<div id="collapsible" class="collapsible js-collapsible" data-btn-close="Hide this">
  <div class="collapsible__heading js-collapsible-heading">
    <div class="collapsible__controls">
      <h2 class="collapsible__title">Need to answer separately from your household?</h2>
      <span class="collapsible__icon">
        <svg class="svg-icon" viewBox="0 0 8 13" xmlns="http://www.w3.org/2000/svg" focusable="false">
          <path d="M5.74,14.28l-.57-.56a.5.5,0,0,1,0-.71h0l5-5-5-5a.5.5,0,0,1,0-.71h0l.57-.56a.5.5,0,0,1,.71,0h0l5.93,5.93a.5.5,0,0,1,0,.7L6.45,14.28a.5.5,0,0,1-.71,0Z" transform="translate(-5.02 -1.59)" />
        </svg>
      </span>
    </div>
  </div>
  <div id="collapsible-content" class="collapsible__content js-collapsible-content">
    <p>If you need to answer separately from the people you live with, you can <a href="#0">request an individual access code</a> to start a separate census.</p>
    <div class="panel panel--warn panel--no-title">
      <span class="panel__icon" aria-hidden="true">!</span>
      <span class="u-vh">Warning: </span>
      <div class="panel__body">
        Someone in your household must still complete a census using a household access code
      </div>
    </div>
    <button type="button" class="btn btn--small js-collapsible-button u-d-no btn--secondary" aria-hidden="true">
      <span class="btn__inner js-collapsible-button-inner">Hide this</span>
      <span class="btn__context u-vh">Need to answer separately from your household? content</span>
    </button>
  </div>
</div>
Nunjucks macro options
Name Type Required Description
id string true id for the collapsible
title string true The title for the collapsible
titleTag string false The HTML tag to wrap the title text in. Will default to a div
content string true HTML content for the collapsible
button CollapsibleButton false Settings for the close button. If not specified button will not render
classes string false Classes to add to the collapsible element
saveState boolean false Allows saving of collapsible state (open or closed) locally
attributes object false HTML attributes (for example data attributes) to add to the collapsible element
headingAttributes object false HTML attributes (for example data attributes) to add to the collapsible header element
contentAttributes object false HTML attributes (for example data attributes) to add to the collapsible content element

CollapsibleButton

Name Type Required Description
close string true Text for the button when the collapsible are open
context string false First part of a visually hidden text string appended to the close button text to add context for screen readers. Defaults to value of title if empty, and is appended with value of contextSuffix (e.g. “Hide this ‘What is a photovoltaic system?’ content”)
contextSuffix string true Second part of a visually hidden text string appended to the context text string to build a readable sentence for adding context to the close button for screen readers (e.g. “Hide this ‘What is a photovoltaic system?’ content“)
classes string false Classes to add to the close button. If classes are provided the btn--secondary class will not be applied by default
attributes object false HTML attributes (for example data attributes) to add to the button
{% from "components/collapsible/_macro.njk" import onsCollapsible %}
{% from "components/panel/_macro.njk" import onsPanel %}
{%
    call onsCollapsible({
        "id": "collapsible",
        "title": "Need to answer separately from your household?",
        "titleTag": "h2",
        "button": {
            "close": "Hide this",
            "contextSuffix": "content",
            "attributes": {
                "aria-hidden": "true"
            }
        }
    })
%}
    <p>If you need to answer separately from the people you live with, you can <a href="#0">request an individual access code</a> to start a separate census.</p>
    {% call onsPanel({
            type: "warn"
        })
    %}
        Someone in your household must still complete a census using a household access code
    {% endcall %}
{% endcall %}

{% macro onsCollapsible(params) %}
    {% from "components/button/_macro.njk" import onsButton %}
    <div
        id="{{ params.id }}"
        class="collapsible js-collapsible{% if params.isAccordion is defined and params.isAccordion %} collapsible--accordion{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}"
        {% if params.button is defined and params.button and params.button.close is defined and params.button.close %} data-btn-close="{{ params.button.close }}"{% endif %}
        {% if params.group is defined and params.group %} data-group="{{ params.group }}"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        {% if params.saveState is defined and params.saveState %} data-save-state="true"{% endif %}
    >
        <div
            class="collapsible__heading js-collapsible-heading"
            {% if params.headingAttributes is defined and params.headingAttributes %}{% for attribute, value in (params.headingAttributes.items() if params.headingAttributes is mapping and params.headingAttributes.items else params.headingAttributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >
            {# Required to use display flex on Safari as it's not currently supported: https://bugs.webkit.org/show_bug.cgi?id=167111 #}
            <div class="collapsible__controls">
                {% set titleTag = params.titleTag | default("div") %}
                <{{ titleTag }} class="collapsible__title">{{ params.title }}</{{ titleTag }}>
                <span class="collapsible__icon">
                    {% from "components/icons/_macro.njk" import onsIcon %}
                    {{
                        onsIcon({
                            "icon": "chevron"
                        })
                    }}</span>
                {% if params.button is defined and params.button and params.button.open is defined and params.button.open and params.isAccordion is defined and params.isAccordion %}
                    {{
                        onsButton({
                            "type": "button",
                            "text": params.button.open,
                            "classes": "collapsible__btn btn--secondary btn--small js-collapsible-button u-d-no u-d-no@xxs@s",
                            "innerClasses": "js-collapsible-button-inner",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            </div>
        </div>
        <div id="{{ params.id }}-content" class="collapsible__content js-collapsible-content"
            {% if params.contentAttributes is defined and params.contentAttributes %}{% for attribute, value in (params.contentAttributes.items() if params.contentAttributes is mapping and params.contentAttributes.items else params.contentAttributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >
            {% if params.isAccordion is defined and params.isAccordion %}
                {{ params.content | safe }}{{ caller() if caller }}
            {% else %}
                {{ params.content | safe }}{{ caller() if caller }}
                {% if params.button is defined and params.button and params.button.close is defined and params.button.close and params.isAccordion != true %}
                    {{
                        onsButton({
                            "type": "button",
                            "text": params.button.close,
                            "buttonContext": (params.button.context | default(params.title)) + " " + params.button.contextSuffix,
                            "classes": "btn--small js-collapsible-button u-d-no " + (params.button.classes | default("btn--secondary")),
                            "innerClasses": "js-collapsible-button-inner",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            {% endif %}
        </div>
    </div>
{% endmacro %}

$collapsible-caret-width: 1.5rem;
.collapsible {
  &--initialised & {
    &__heading {
      color: $color-text-link;
      cursor: pointer;
      display: inline-block;
      outline: none;
      padding: 0 0 0 $collapsible-caret-width;
      pointer-events: initial;
      position: relative;
      &::marker {
        display: none;
      }
      &:focus {
        .collapsible__title {
          @extend a:focus;
          // extend collapsible focus background behind caret
          margin-left: -$collapsible-caret-width;
          padding-left: $collapsible-caret-width;
        }
        .collapsible__icon {
          fill: $color-black;
        }
      }
      &:hover {
        color: $color-text-link-hover;
        .collapsible__icon {
          fill: $color-text-link-hover;
        }
        .collapsible__title {
          text-decoration: underline solid $color-text-link-hover 2px;
        }
      }
    }
    &__icon {
      display: inline-block;
      fill: $color-text-link;
      height: $collapsible-caret-width;
      left: -0.2rem;
      position: absolute;
      top: -0.25rem;
      width: $collapsible-caret-width;
    }
    &__title {
      font-size: 1rem;
      font-weight: $font-weight-bold;
      margin-bottom: 0;
      text-decoration: underline;
      text-underline-position: under;
      transform: translateY(-1px);
    }
    &__content {
      display: none;
    }
  }
  &__icon {
    display: none;
  }
  &--open & {
    &__icon {
      left: -0.1rem;
      top: 0.1rem;
      transform: rotate(90deg);
    }
    &__content {
      border-left: 5px solid $color-grey-15;
      display: block;
      margin: 1rem 0 0;
      padding: 0 0 0 1.3em;
    }
  }
  &--accordion {
    margin: 0;
    &.collapsible--open {
      .svg-icon {
        left: 0.4rem;
        position: absolute;
        top: 0.25rem;
      }
    }
  }
  &--accordion & {
    &__heading {
      border-top: 1px solid $color-borders;
      margin: 0;
      padding-bottom: 1rem;
      padding-top: 1rem;
      width: 100%;
      &:focus {
        outline: none;
      }
    }
    &__title {
      margin: 0 1rem 0 0;
    }
    &__icon {
      top: 0.8rem;
      @include mq(s) {
        top: 1.15rem;
      }
    }
    &__controls {
      align-items: center;
      display: flex;
      justify-content: space-between;
    }
    &__btn {
      align-self: flex-start;
      width: auto;
    }
    &__content {
      border-left: 0;
      margin: 0;
      padding: 0;
    }
  }
}

How to use this component

The collapsible component uses JavaScript so when it’s turned off, the section of content is expanded and displayed as a standard heading and paragraph.

Use an appropriate heading tag (h2, h3 etc) for the collapsible__title to provide the correct scemantic hierarchy to the document with the rest of the page.

Use a clear label

The collapsible component hides content so make sure your heading is clear so the user doesn’t need to work hard to understand if they need to click on it.

Accessibility

The collapsible component is made accessible by using the following aria attributes which are attached on domready via Javascript:

Element ARIA attribute Description
.collapsible aria-expanded="true/false" Indicates the state of the associated .collapsible__heading
aria-selected="true/false" Indicates the selected state of the focussed .collapsible element
.collapsible__heading aria-controls="{ID}-content" Indicates the id of the display controller element for the associated .collapsible__content block.
button aria-controls="{ID}-content" Indicates the id of the display controller element for the associated .collapsible__content block.
.collapsible__content aria-hidden="true/false" Indicates the visibility of the element and all of its descendants.

Google analytics

Optionally pass event tracking data attributes for Google Analytics using data-ga="click", data-ga-category="definition", data-ga-action="Open/Close panel", data-ga-label="{TITLE}"

Research on this component

If you have conducted any user research using this component, please feed back your findings via the Design System forum 

Design System forum

Discuss ‘Collapsible’ on GitHub