Collapsible

<div class="c-collapsible--simple o-collapsible ">
    <p class="c-collapsible__title o-collapsible__title js-collapsible-title" id="molecule-collapsible--default">Element title</p>
    <div>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Maxime recusandae eligendi beatae nesciunt sit. Officiis nulla maiores, alias minima, dolorum harum et earum molestias odio architecto tempore est consequuntur ullam.</div>
</div>
<div class="c-collapsible--simple o-collapsible {{ modifier }}">
  {{#if tag }}
    <{{tag}} class="c-collapsible__title o-collapsible__title js-collapsible-title" id="{{ id }}">{{ title }}</{{tag}}>
  {{else}}
    <p class="c-collapsible__title o-collapsible__title js-collapsible-title" id="{{ id }}">{{ title }}</p>
  {{/if}}
  {{#> @partial-block }}
    <div>{{{ text }}}</div>
  {{/ @partial-block }}
</div>
{
  "name": "default",
  "title": "Element title",
  "text": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Maxime recusandae eligendi beatae nesciunt sit. Officiis nulla maiores, alias minima, dolorum harum et earum molestias odio architecto tempore est consequuntur ullam.",
  "id": "molecule-collapsible--default"
}
  • Content:
    /*------------------------------------*\
      Simple
    \*------------------------------------*/
    
    .c-collapsible--simple {
      background-color: secondary(soft, 100);
      border-radius: remify(16px);
      margin-bottom: remify(24px);
      border: remify(1px) solid primary(night, 10);
    
      .c-collapsible__title {
        color: primary(night, 100);
        @include font-scale(level-2, regular);
        cursor: pointer;
    
        button {
          display: flex;
          padding: remify(12px) remify(24px);
        }
    
        button span.h2 {
          display: flex;
          justify-content: space-between;
          width: 100%;
          align-items: center;
          color: currentColor;
        }
    
      }
    
      h3 {
        .c-canvas__paper & {
          font-size: 115%;
          font-weight: fw(semibold);
        }
      }
    
      h4 {
        @include font-scale(level-3, semibold);
      }
    
    }
    
    .c-collapsible--simple:hover,
    .c-collapsible--simple:has([aria-expanded="true"]) {
    
      .c-collapsible__title {
        color: primary(sky, 120);
      }
    
    }
    
    /*------------------------------------*\
      Button
    \*------------------------------------*/
    
    .c-collapsible--btn {
    
      border-bottom: none;
    
      .c-form__alone--createpsw & {
        flex-grow: 1;
      }
    
      .c-collapsible__title {
    
        // Hack for Safari (web and mobile)
        // See also in: 'src/views/01-atoms/ctas/atom-button/_button.scss'
        // @supports (-webkit-appearance:none) {
        //   color: #fff;
        // }
    
        .c-form--sticker & {
          margin-bottom: 0;
        }
    
      }
    
      [aria-expanded="false"],
      [aria-expanded="true"] {
        &:after {
          content: "\25be";
          display: inline-block;
          margin-left: remify(4px);
          // transition: transform .3s ease;
        }
      }
    
      [aria-expanded="true"] {
        margin-bottom: remify(24px);
        &:after {
          transform: rotate(-180deg);
        }
      }
    
      .c-collapsible__title button {
        color: primary(sky, 120);
        text-align: right;
        display: block;
        width: auto;
        @extend .c-button;
        @extend .c-button--ghost;
        padding-left: remify(16px);
        padding-right: remify(16px);
    
        svg.arrow-up,
        svg.arrow-down {
          @extend .u-visuallyhidden;
        }
    
        .c-form__alone--createpsw & {
          margin-top: 0;
          margin-right: remify(24px);
        }
    
      }
    
    }
    
    /*------------------------------------*\
      Password
    \*------------------------------------*/
    
    .c-collapsible--psw {
      
      [aria-expanded="false"],
      [aria-expanded="true"] {
        &:after {
          content: "\25be";
          display: inline-block;
          transition: transform .3s ease;
        }
      }
    
      [aria-expanded="true"] {
        margin-bottom: remify(24px);
        &:after {
          transform: rotate(-180deg);
        }
      }
    
    }
    
    /*------------------------------------*\
      Invoice
    \*------------------------------------*/
    
    .c-collapsible--invoice {
      
      .alert-msg {
        font-size: 75%;
        line-height: 1.5;
        text-align: center;
        position: relative;
        top: remify(40px);
    
        @include min-screen (bp(tablet)) {
          width: 70%;
          margin: 0 auto;
        }
    
      }
    
    }
    
    /*------------------------------------*\
      Baloon
    \*------------------------------------*/
    
    .c-collapsible__content--baloon {
      @include shadow(medium);
      padding: remify(16px);
      border-radius: remify(4px);
      position: relative;
      background-color: #F4F6F7;
    
      &:before{
        content: "";
        position: absolute;
        @include css-triangle("up", 10px, #F4F6F7);
        top: remify(-20px);
        left: 50%;
        transform: translateX(-50%);
        @include shadow(medium);
      }
    
      @include min-screen (bp(tablet)) {
        padding: remify(24px);
      }
    
    }
    
    /*------------------------------------*\
      Faq
    \*------------------------------------*/
    
    .c-collapsible--faq {
    
      // .c-collapsible__title button {
      //   border-radius: remify(8px);
    
      //   @include min-screen (bp(tablet)) {
      //     padding: remify(20px) remify(16px);
      //   }
    
      // }
    
      // .c-collapsible__title {
      //   font-size: 100%;
    
      //   @include min-screen (bp(tablet)) {
      //     font-size: 125%;
      //   }
      // }
    
    }
    
    /*------------------------------------*\
      Privacy
    \*------------------------------------*/
    
    .c-collapsible--privacy {
      margin: remify(16px) 0;
    
      // .c-collapsible__title {
    
      //   button {
      //     padding: remify(8px) remify(8px);
      //   }
    
      // }
    
    }
    
    
    /*------------------------------------*\
      Docs
    \*------------------------------------*/
    
    .c-collapsible__content { 
      .c-media {
        margin-bottom: remify(16px);
      }
    }
  • URL: /components/raw/collapsible/_collapsible.scss
  • Filesystem Path: src/views/02-molecules/collapsible/_collapsible.scss
  • Size: 4.3 KB
  • Content:
    // ES5
    'use strict';
    
    (function () {
      var headings = document.querySelectorAll('.js-collapsible-title');
    
      function isHTML(str) {
        var a = document.createElement('div');
        a.innerHTML = str;
        for (var c = a.childNodes, i = c.length; i--;) {
          if (c[i].nodeType === 1) return true;
        }
        return false;
      }
    
      Array.prototype.forEach.call(headings, function (heading) {
        var headingcontent = heading.innerHTML;
    
        // Se non c'è già un button nel titolo, iniettalo (vincolo: button solo via JS)
        var alreadyButton = heading.querySelector('button.c-collapsible__button');
        if (!(alreadyButton || (isHTML(headingcontent) && (window.jQuery && jQuery(headingcontent).is('button'))))) {
          var triggerId = (heading.id ? heading.id + '-trigger' : 'collapsible-trigger-' + Math.random().toString(36).slice(2));
          var panelId   = (heading.id ? heading.id + '-panel'   : 'collapsible-panel-' + Math.random().toString(36).slice(2));
    
          heading.innerHTML =
            '<button class="c-collapsible__button" type="button"' +
              ' id="' + triggerId + '"' +
              ' aria-expanded="false"' +
              ' aria-controls="' + panelId + '"' +
              ' data-opens-text="apri" data-closes-text="chiudi">' +
                headingcontent +
                '<svg class="arrow-down" viewBox="0 0 24 24" aria-hidden="true"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"></path></svg>' +
                '<svg class="arrow-up" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"></path></svg>' +
            '</button>';
        }
    
        // Raccoglie i nodi tra questo titolo e il prossimo titolo .js-collapsible-title
        var getContent = function (elem) {
          var elems = [];
          while (elem.nextElementSibling && !elem.nextElementSibling.classList.contains('js-collapsible-title')) {
            elems.push(elem.nextElementSibling);
            elem = elem.nextElementSibling;
          }
          elems.forEach(function (node) {
            if (node.parentNode) node.parentNode.removeChild(node);
          });
          return elems;
        };
    
        var contents = getContent(heading);
    
        // Crea o individua il pannello
        var wrapper = document.createElement('div');
        var ensuredPanelId = (heading.id ? heading.id + '-panel' : 'collapsible-panel-' + Math.random().toString(36).slice(2));
        wrapper.id = ensuredPanelId;
        wrapper.classList.add('o-collapsible__content');
        wrapper.setAttribute('role', 'region');
        wrapper.hidden = true;
    
        contents.forEach(function (node) { wrapper.appendChild(node); });
        heading.parentNode.insertBefore(wrapper, heading.nextElementSibling);
    
        // Button ↔ Panel: id/aria-controls + aria-labelledby
        var btn = heading.querySelector('button.c-collapsible__button');
        if (btn) {
          // Assicura un id al button (se mancasse) e collega aria-controls al pannello
          if (!btn.id) {
            btn.id = (heading.id ? heading.id + '-trigger' : 'collapsible-trigger-' + Math.random().toString(36).slice(2));
          }
          btn.setAttribute('aria-controls', ensuredPanelId);
    
          // 🔴 CHANGE PRINCIPALE: il pannello è etichettato DAL BUTTON, non dal titolo
          wrapper.setAttribute('aria-labelledby', btn.id);
    
          btn.onclick = function () {
            var expanded = btn.getAttribute('aria-expanded') === 'true';
            btn.setAttribute('aria-expanded', (!expanded).toString());
            wrapper.hidden = expanded;
          };
        }
      });
    
      // Apertura automatica se l’URL contiene l’ID del titolo (comportamento esistente)
      if (window.jQuery) {
        var $collapsible = jQuery('.o-collapsible');
    
        $collapsible.each(function () {
          var $this = jQuery(this);
          var $title = $this.find('.js-collapsible-title');
          var titleId = $title.attr('id') || '';
          var href = (jQuery(location).attr('href') || '');
          var $btn = $this.find('.c-collapsible__button');
          var $content = $this.find('.o-collapsible__content');
    
          if (titleId && typeof href.includes === 'function' && href.includes(titleId)) {
            $btn.attr('aria-expanded', 'true');
            $content.removeAttr('hidden');
    
            jQuery('html, body').animate({
              scrollTop: $this.offset().top - 100
            }, 2000);
          }
        });
      }
    })();
    
  • URL: /components/raw/collapsible/collapsible.js
  • Filesystem Path: src/views/02-molecules/collapsible/collapsible.js
  • Size: 4.2 KB

To tangible frontend team:

You can copy and paste the code of this component wherever you want instead importing.