JSFiddle - React, Tailwind, and code Playground

by aka_SKIff

HTML

<div class="item__extra" data-accordion>
  <div class="accordion__item collapsible-opened" data-collapsible data-collapsible-type="accordion">
    <div class="accordion__trigger" data-collapsible-trigger>Дополнительная информация</div>
    <div class="accordion__body" data-collapsible-body>
      <div class="accordion__inner" data-collapsible-inner>
        Любой текст 1
      </div>
    </div>
  </div>
  
  <div class="accordion__item collapsible-closed" data-collapsible data-collapsible-type="accordion">
    <div class="accordion__trigger" data-collapsible-trigger>Дополнительная информация 2</div>
    <div class="accordion__body" data-collapsible-body>
      <div class="accordion__inner" data-collapsible-inner>
        Любой текст 2
      </div>
    </div>
  </div>

</div>

SCSS

.collapsible-closed {
  [data-collapsible-body] {
    height: 0!important;
  }
}

[data-collapsible-body] {
  overflow: hidden;
  transition: height 0.3s;
}

[data-collapsible] {}

[data-collapsible-header] {}

[data-collapsible-inner] {}

.accordion {}

.accordion__item {}

.accordion__trigger {
  line-height: 20px;
  font-size: 16px;
  color: #231F20;
  display: inline-block;
  position: relative;
  padding-left: 16px;
  overflow: hidden;
  &:hover {
    cursor: pointer;
  }
  &:before {
    /*vertical*/
    content: '';
    position: absolute;
    width: 1px;
    height: 9px;
    background: black;
    left: 4px;
    top: 5px;
    transform-origin: 50% 50%;
    transition: transform 0.3s;
  }
  &:after {
    /*horizontal*/
    content: '';
    position: absolute;
    width: 9px;
    height: 1px;
    background: black;
    left: 0;
    top: 50%;
    margin-top: -1px;
  }
  .collapsible-opened & {
    &:before {
      transform: rotate(90deg);
    }
  }
}

.accordion__body {}

.accordion__inner {
  padding-top: 1rem;
  padding-bottom: 1rem;
}

Babel + JSX

function closest(el, selector) {
  const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;

  while (el) {
    if (matchesSelector.call(el, selector)) {
      break;
    }
    el = el.parentElement;
  }
  return el;
}

class Accordion {
  constructor() {
    this.events = {
      handleCollapsibleTriggerClick: (e) => {
        e.preventDefault();
        const _this = e.target;
        const _collapsible = closest(_this, '[data-collapsible]');
        const _collapsibleType = _collapsible.getAttribute('data-collapsible-type');
        const _accordion = (_collapsibleType === 'accordion') ? closest(_this, '[data-accordion]') : null;
        const _collapsibleOpened = _accordion ? _accordion.querySelector('.collapsible-opened') : null;

        if (_collapsible.classList.contains('collapsible-opened') && !_accordion) {
          _collapsible.classList.remove('collapsible-opened');
          _collapsible.classList.add('collapsible-closed');
        } else {
          if (_accordion) {
            _collapsibleOpened.classList.remove('collapsible-opened');
            _collapsibleOpened.classList.add('collapsible-closed');
          }
          _collapsible.classList.add('collapsible-opened');
          _collapsible.classList.remove('collapsible-closed');
        }
      }
    };
    this.collapsibles = Array.prototype.slice.call(document.querySelectorAll('[data-collapsible]'));
    this.initialize();
  }
  getFullHeight(el) {
    let height = el.offsetHeight;
    const style = getComputedStyle(el);
    height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10);
    return height;
  }
  initialize() {
    this.collapsibles.forEach((elem) => {
      const _body = elem.querySelector('[data-collapsible-body]');
      const _inner = elem.querySelector('[data-collapsible-inner]');
      _body.style.height = this.getFullHeight(_inner) + 'px';
     ...