details и summary

<details> и <summary> -- нативные HTML-элементы для раскрываемого контента (disclosure widget). Работают без JavaScript: браузер сам обрабатывает клик, анимацию маркера и состояние.

Зачем нужно

Аккордеоны, FAQ-секции, спойлеры -- всё это раньше требовало JavaScript. <details> даёт это из коробки: доступность (screen reader объявляет «развёрнуто/свёрнуто»), клавиатурная навигация (Enter/Space), сохранение состояния. Никаких библиотек.

Где используется

  • FAQ-секции (вопросы и ответы)
  • Аккордеоны в формах
  • Спойлеры в статьях
  • Дополнительные настройки (advanced options)
  • Навигационные меню на мобильных устройствах

Базовый синтаксис

<!-- По умолчанию закрыт -->
<details>
  <summary>Что такое HTML?</summary>
  <p>HTML (HyperText Markup Language) -- язык разметки для создания веб-страниц.
     Он определяет структуру и содержание страницы с помощью тегов.</p>
</details>

<!-- Открыт по умолчанию (атрибут open) -->
<details open>
  <summary>Показать код</summary>
  <pre><code>console.log('Hello');</code></pre>
</details>

Атрибут open

<details id="faq-1">
  <summary>Вопрос</summary>
  <p>Ответ</p>
</details>

<script>
  const details = document.getElementById('faq-1');

  // Проверить состояние
  console.log(details.open()); // true или false

  // Программно открыть/закрыть
  details.open() = true;
  details.open() = false;

  // Событие toggle
  details.addEventListener('toggle', () => {
    console.log(details.open() ? 'Открыт' : 'Закрыт');
  });
</script>

Стилизация

Маркер (треугольник)

/* Стилизация маркера через ::marker */
summary::marker {
  color: #0066cc;
  font-size: 1.2em;
}

/* Убрать стандартный маркер */
summary {
  list-style: none;
}

/* Webkit (Safari) */
summary::-webkit-details-marker {
  display: none;
}

/* Свой маркер через ::before */
summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}

summary::before {
  content: '▶';
  transition: transform 0.2s ease;
}

details[open] > summary::before {
  transform: rotate(90deg);
}

Полная стилизация

details {
  border: 1px solid #ddd;
  border-radius: 8px;
  margin-bottom: 8px;
  overflow: hidden;
}

summary {
  padding: 12px 16px;
  font-weight: 600;
  cursor: pointer;
  background: #f8f9fa;
  user-select: none;
}

summary:hover {
  background: #e9ecef;
}

details[open] summary {
  border-bottom: 1px solid #ddd;
}

details > :not(summary) {
  padding: 12px 16px;
}

Аккордеон (группа details)

Эксклюзивный аккордеон (атрибут name)

<!-- name="faq" -- только один details может быть открыт -->
<details name="faq">
  <summary>Вопрос 1</summary>
  <p>Ответ на вопрос 1</p>
</details>

<details name="faq">
  <summary>Вопрос 2</summary>
  <p>Ответ на вопрос 2</p>
</details>

<details name="faq">
  <summary>Вопрос 3</summary>
  <p>Ответ на вопрос 3</p>
</details>
<!-- Открытие одного автоматически закрывает другие в группе -->

Аккордеон через JS (для старых браузеров)

const allDetails = document.querySelectorAll('details[data-group="faq"]');

allDetails.forEach((details) => {
  details.addEventListener('toggle', () => {
    if (details.open()) {
      allDetails.forEach((other) => {
        if (other !== details) other.open() = false;
      });
    }
  });
});

Анимация раскрытия

details {
  overflow: hidden;
}

details > :not(summary) {
  /* Анимация через interpolate-size (Chrome 129+) */
  transition: height 0.3s ease, opacity 0.3s ease;
}

/* Fallback: анимация через max-height */
details .content {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 0.3s ease, opacity 0.3s ease;
}

details[open] .content {
  max-height: 500px; /* достаточно большое значение */
  opacity: 1;
}

FAQ-секция (полный пример)

<section class="faq">
  <h2>Часто задаваемые вопросы</h2>

  <details name="faq">
    <summary>Как оплатить заказ?</summary>
    <div class="faq-answer">
      <p>Мы принимаем банковские карты Visa и Mastercard,
         а также оплату через СБП.</p>
    </div>
  </details>

  <details name="faq">
    <summary>Сколько стоит доставка?</summary>
    <div class="faq-answer">
      <p>Доставка бесплатна при заказе от 3000 рублей.
         В остальных случаях -- 300 рублей.</p>
    </div>
  </details>

  <details name="faq">
    <summary>Как вернуть товар?</summary>
    <div class="faq-answer">
      <p>Возврат возможен в течение 14 дней с момента получения.
         Товар должен быть в оригинальной упаковке.</p>
    </div>
  </details>
</section>

Доступность

Screen reader объявляет <summary> как кнопку со состоянием «развёрнуто» или «свёрнуто»:

SR: "Как оплатить заказ? Свёрнуто. Кнопка."
[Enter]
SR: "Как оплатить заказ? Развёрнуто. Кнопка."

Клавиатурная навигация:

  • Tab -- перемещение между summary
  • Enter / Space -- открыть/закрыть

Частые ошибки

Ошибка Проблема Решение
Нет <summary> Браузер сгенерирует «Details» Всегда добавляй <summary>
display: none на summary Сломана навигация Стилизуй через CSS, не скрывай
Вложенные details Сложно для пользователя Максимум 2 уровня
list-style: none без ::marker Маркер не убран в Safari Добавить ::-webkit-details-marker
JS-аккордеон вместо name Лишний код Использовать name атрибут

Практика

  1. Создай FAQ-секцию из 5 вопросов с <details> и <summary>
  2. Добавь эксклюзивный аккордеон через атрибут name
  3. Стилизуй маркер: замени треугольник на свою иконку
  4. Добавь плавную анимацию раскрытия
  5. Протестируй клавиатурную навигацию (Tab + Enter)

Связанные темы

Ресурсы