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="ons-collapsible ons-js-collapsible" data-btn-close="Hide this">
  <div class="ons-collapsible__heading ons-js-collapsible-heading">
    <div class="ons-collapsible__controls">
      <h2 class="ons-collapsible__title">What is a photovoltaic system?</h2>
      <span class="ons-collapsible__icon">
        <svg class="ons-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="ons-collapsible__content ons-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="ons-btn ons-js-collapsible-button ons-u-d-no ons-btn--secondary ons-btn--small" aria-hidden="true">
      <span class="ons-btn__inner ons-js-collapsible-button-inner">Hide this</span>
      <span class="ons-btn__context ons-u-vh">What is a photovoltaic system? content</span>
    </button>
  </div>
</div>
{% 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"
            }
        }
    })
}}
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 (for example, “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 (for example, “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
{% macro onsCollapsible(params) %}
    {% from "components/button/_macro.njk" import onsButton %}

    <div
        id="{{ params.id }}"
        class="ons-collapsible ons-js-collapsible{% if params.isAccordion is defined and params.isAccordion %} ons-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="ons-collapsible__heading ons-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="ons-collapsible__controls">
                {% set titleTag = params.titleTag | default("div") %}
                <{{ titleTag }} class="ons-collapsible__title">{{ params.title }}</{{ titleTag }}>
                <span class="ons-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": "ons-collapsible__btn ons-js-collapsible-button ons-u-d-no ons-u-d-no@xxs@s",
                            "innerClasses": "ons-js-collapsible-button-inner",
                            "variants": ["secondary", "small"],
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            </div>
        </div>
        <div id="{{ params.id }}-content" class="ons-collapsible__content ons-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": "ons-js-collapsible-button ons-u-d-no " + (params.button.classes | default("ons-btn--secondary")),
                            "innerClasses": "ons-js-collapsible-button-inner",
                            "variants": "small",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            {% endif %}
        </div>
    </div>
{% endmacro %}
$collapsible-caret-width: 1.5rem;

.ons-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 {
        .ons-collapsible__title {
          @extend a:focus;
          // extend collapsible focus background behind caret
          margin-left: -$collapsible-caret-width;
          padding-left: $collapsible-caret-width;
        }
        .ons-collapsible__icon {
          fill: $color-black;
        }
      }

      &:hover {
        color: $color-text-link-hover;
        .ons-collapsible__icon {
          fill: $color-text-link-hover;
        }
        .ons-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;
    &.ons-collapsible--open {
      .ons-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 button 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, for example, 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="ons-collapsible ons-js-collapsible" data-btn-close="Hide this" data-save-state="true">
  <div class="ons-collapsible__heading ons-js-collapsible-heading">
    <div class="ons-collapsible__controls">
      <h2 class="ons-collapsible__title">What is a photovoltaic system?</h2>
      <span class="ons-collapsible__icon">
        <svg class="ons-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="ons-collapsible__content ons-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="ons-btn ons-js-collapsible-button ons-u-d-no ons-btn--secondary ons-btn--small" aria-hidden="true">
      <span class="ons-btn__inner ons-js-collapsible-button-inner">Hide this</span>
      <span class="ons-btn__context ons-u-vh">What is a photovoltaic system? undefined</span>
    </button>
  </div>
</div>
{% 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"
    })
}}
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 (for example, “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 (for example, “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
{% macro onsCollapsible(params) %}
    {% from "components/button/_macro.njk" import onsButton %}

    <div
        id="{{ params.id }}"
        class="ons-collapsible ons-js-collapsible{% if params.isAccordion is defined and params.isAccordion %} ons-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="ons-collapsible__heading ons-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="ons-collapsible__controls">
                {% set titleTag = params.titleTag | default("div") %}
                <{{ titleTag }} class="ons-collapsible__title">{{ params.title }}</{{ titleTag }}>
                <span class="ons-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": "ons-collapsible__btn ons-js-collapsible-button ons-u-d-no ons-u-d-no@xxs@s",
                            "innerClasses": "ons-js-collapsible-button-inner",
                            "variants": ["secondary", "small"],
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            </div>
        </div>
        <div id="{{ params.id }}-content" class="ons-collapsible__content ons-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": "ons-js-collapsible-button ons-u-d-no " + (params.button.classes | default("ons-btn--secondary")),
                            "innerClasses": "ons-js-collapsible-button-inner",
                            "variants": "small",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            {% endif %}
        </div>
    </div>
{% endmacro %}
$collapsible-caret-width: 1.5rem;

.ons-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 {
        .ons-collapsible__title {
          @extend a:focus;
          // extend collapsible focus background behind caret
          margin-left: -$collapsible-caret-width;
          padding-left: $collapsible-caret-width;
        }
        .ons-collapsible__icon {
          fill: $color-black;
        }
      }

      &:hover {
        color: $color-text-link-hover;
        .ons-collapsible__icon {
          fill: $color-text-link-hover;
        }
        .ons-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;
    &.ons-collapsible--open {
      .ons-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="ons-collapsible ons-js-collapsible" data-btn-close="Hide this">
  <div class="ons-collapsible__heading ons-js-collapsible-heading">
    <div class="ons-collapsible__controls">
      <h2 class="ons-collapsible__title">Need to answer separately from your household?</h2>
      <span class="ons-collapsible__icon">
        <svg class="ons-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="ons-collapsible__content ons-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="ons-panel ons-panel--warn ons-panel--no-title">
      <span class="ons-panel__icon" aria-hidden="true">!</span>
      <span class="ons-u-vh">Warning: </span>
      <div class="ons-panel__body">
        Someone in your household must still complete a census using a household access code
      </div>
    </div>
    <button type="button" class="ons-btn ons-js-collapsible-button ons-u-d-no ons-btn--secondary ons-btn--small" aria-hidden="true">
      <span class="ons-btn__inner ons-js-collapsible-button-inner">Hide this</span>
      <span class="ons-btn__context ons-u-vh">Need to answer separately from your household? content</span>
    </button>
  </div>
</div>
{% 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 %}
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 (for example, “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 (for example, “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
{% macro onsCollapsible(params) %}
    {% from "components/button/_macro.njk" import onsButton %}

    <div
        id="{{ params.id }}"
        class="ons-collapsible ons-js-collapsible{% if params.isAccordion is defined and params.isAccordion %} ons-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="ons-collapsible__heading ons-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="ons-collapsible__controls">
                {% set titleTag = params.titleTag | default("div") %}
                <{{ titleTag }} class="ons-collapsible__title">{{ params.title }}</{{ titleTag }}>
                <span class="ons-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": "ons-collapsible__btn ons-js-collapsible-button ons-u-d-no ons-u-d-no@xxs@s",
                            "innerClasses": "ons-js-collapsible-button-inner",
                            "variants": ["secondary", "small"],
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            </div>
        </div>
        <div id="{{ params.id }}-content" class="ons-collapsible__content ons-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": "ons-js-collapsible-button ons-u-d-no " + (params.button.classes | default("ons-btn--secondary")),
                            "innerClasses": "ons-js-collapsible-button-inner",
                            "variants": "small",
                            "attributes": params.button.attributes
                        })
                    }}
                {% endif %}
            {% endif %}
        </div>
    </div>
{% endmacro %}
$collapsible-caret-width: 1.5rem;

.ons-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 {
        .ons-collapsible__title {
          @extend a:focus;
          // extend collapsible focus background behind caret
          margin-left: -$collapsible-caret-width;
          padding-left: $collapsible-caret-width;
        }
        .ons-collapsible__icon {
          fill: $color-black;
        }
      }

      &:hover {
        color: $color-text-link-hover;
        .ons-collapsible__icon {
          fill: $color-text-link-hover;
        }
        .ons-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;
    &.ons-collapsible--open {
      .ons-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 ons-collapsible__title to provide the correct semantic 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
.ons-collapsible aria-expanded="true/false" Indicates the state of the associated .ons-collapsible__heading
aria-selected="true/false" Indicates the selected state of the focussed .ons-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 .ons-collapsible__content block.
.ons-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}"

Help improve this component

Let us know how we could improve this component or share your user research findings. Discuss the ‘Collapsible’ component on GitHub