Олег Марков
Заголовок details в HTML - summary
Введение
Элементы details и summary в HTML позволяют создавать раскрывающиеся блоки без единой строчки JavaScript. Вы можете сделать «спойлер», FAQ, секцию с дополнительными параметрами формы или техническими подробностями буквально парой строк разметки.
Смотрите, я покажу вам, как это выглядит в самой простой форме:
<details>
<summary>Показать подробности</summary>
<p>Здесь находится дополнительная информация.</p>
</details>Как только вы откроете такую страницу в браузере, заголовок с текстом «Показать подробности» станет кликабельным, а содержимое будет скрыто до первого клика. При повторном клике блок снова свернется.
В этой статье мы разберем, как именно устроены details и summary, какие у них семантические особенности, как они влияют на доступность, как их стилизовать и чего стоит избегать. Покажу вам, как использовать их грамотно и в простых проектах, и в интерфейсах посложнее.
Что такое details и summary
Назначение элемента details
Элемент <details> — это контейнер, который:
- хранит содержимое, которое можно свернуть или развернуть;
- имеет встроенное состояние открытости (атрибут
open); - сам управляет переключением состояния при взаимодействии с пользователем;
- уведомляет вспомогательные технологии (скринридеры и другие) о том, что содержимое можно раскрыть.
Если говорить проще, <details> — это семантический «раскрывающийся блок».
Важные особенности:
- по умолчанию содержимое свернуто, если атрибут
openне указан; - браузер добавляет собственный маркер (обычно треугольник или стрелку);
- переключение состояния происходит без JavaScript, на чистом HTML.
Роль элемента summary
Элемент <summary> — это краткий заголовок или аннотация к содержимому <details>. По сути, это «кнопка», по которой пользователь раскрывает или сворачивает блок. Но при этом:
- формально это не
<button>, а именно заголовок/текст; - он кликабелен и фокусируется с клавиатуры;
- по клику или нажатию Enter/Space переключает состояние родительского
<details>.
Простой пример, чтобы вы увидели базовую структуру:
<details>
<summary>Условия доставки</summary>
<p>Мы осуществляем доставку по всей стране в течение 3–5 рабочих дней.</p>
</details>Комментарии к структуре:
<details>— оборачивает и заголовок, и содержимое;<summary>— первая строка, которая отображается всегда и по которой можно кликать;<p>— то, что показывается только в открытом состоянии.
Базовый синтаксис и атрибут open
Минимально рабочий пример
Давайте разберемся на самом простом примере с атрибутом open:
<details open>
<summary>Раскрытый по умолчанию блок</summary>
<p>Этот текст виден сразу, потому что атрибут open указан.</p>
</details>
<details>
<summary>Скрытый по умолчанию блок</summary>
<p>Этот текст будет показан только после клика по summary.</p>
</details>Комментарии:
openбез значения — логический атрибут; его наличие означает «открыто»;- отсутствие
open— блок закрыт при загрузке страницы; - браузер сам переключает
openпри взаимодействии пользователя.
Как работает атрибут open
При взаимодействии:
- если
<details>закрыт, то после клика по<summary>браузер добавляет атрибутopenв DOM; - если открыт, атрибут удаляется.
Покажу вам, как это увидеть в браузере:
- Откройте страницу с
<details>в браузере. - Откройте DevTools (обычно F12).
- Найдите элемент
<details>в DOM-дереве. - Кликните по
<summary>на странице. - Обратите внимание, как атрибут
openпоявляется и исчезает в разметке в реальном времени.
Это поведение важно, если вы планируете синхронизировать состояние с JavaScript или стилями (через селектор [open]).
Вложенное содержимое details
Какие элементы можно помещать внутрь
Внутрь <details> можно помещать практически любые блочные и строчные элементы:
- параграфы
<p>; - списки
<ul>,<ol>,<dl>; - таблицы
<table>; - формы
<form>; - картинки
<img>; - вложенные
<details>.
Главное правило — первый дочерний <summary> будет считаться заголовком. Всё остальное станет раскрываемым содержимым.
Пример:
<details>
<summary>Технические характеристики</summary>
<!-- Любое содержимое, которое вы хотите скрывать -->
<ul>
<li>Процессор - 8 ядер</li>
<li>Оперативная память - 16 ГБ</li>
<li>Накопитель - 512 ГБ SSD</li>
</ul>
<p>Гарантия - 2 года при условии соблюдения условий эксплуатации.</p>
</details>Здесь <summary> — только первый заголовок. Все остальные элементы не влияют на поведение раскрытия.
Обязательно ли использовать summary
Формально <summary> не является строго обязательным. <details> без <summary> будет считаться корректным HTML, но:
- он не будет иметь кликабельный заголовок по умолчанию;
- пользователю будет трудно понять, как взаимодействовать с блоком;
- браузеры могут показать
<details>как открытый без возможности свернуть.
Поэтому на практике <summary> нужен почти всегда. Без него теряется ключевой смысл элемента — контролируемый пользователем «спойлер».
Семантика и доступность
Как details/summary воспринимаются скринридерами
Элементы <details> и <summary> имеют встроенную семантику:
<details>обычно объявляется как «раскрывающаяся секция»;<summary>объявляется как кнопка/заголовок, который можно развернуть;- атрибут
openпередает состояние «открыто/закрыто» в видеaria-expandedили аналогичных внутренних флагов.
Это значит, что:
- пользователи скринридеров понимают, что блок интерактивен;
- состояние «развернуто/свернуто» озвучивается;
- взаимодействие клавиатурой работает из коробки.
Вы тем самым экономите время: не нужно вручную добавлять role, aria-expanded, tabindex и т.п., как при кастомной реализации на div + JS.
Навигация с клавиатуры
Проверьте, как это работает:
- Перейдите на страницу с
<details>. - Используйте клавишу Tab, чтобы перейти к
<summary>. - Нажмите Enter или Space.
Результат:
- блок раскрывается/сворачивается;
- фокус остается на
<summary>; - все это работает без дополнительного кода.
Если вы переопределяете стили фокуса, важно не убрать визуальное выделение элемента. Иначе пользователям клавиатуры будет сложно понять, где находится фокус.
Пример кастомного стиля фокуса:
details > summary:focus {
outline: 2px solid #005fcc; /* Подчеркиваем границу при фокусе */
outline-offset: 2px;
}Правильный текст summary с точки зрения UX
Чтобы интерфейс был понятен:
- делайте текст
<summary>максимально конкретным:- вместо «Подробнее» — «Подробнее о доставке»;
- вместо «Показать» — «Показать технические подробности»;
- избегайте длинных предложений, это заголовок, а не параграф;
- не прячьте критически важную информацию в свернутом состоянии, если от нее зависит решение пользователя.
Стилизация details и summary
Базовые CSS-селекторы для стилизации
Вы можете стилизовать эти элементы как обычные блочные элементы. Вот базовые селекторы:
details {
border: 1px solid #ccc; /* Рамка вокруг всего блока */
border-radius: 4px; /* Скругление углов */
padding: 0.5rem 1rem; /* Внутренние отступы */
margin-bottom: 1rem; /* Отступ снизу между несколькими блоками */
}
details[open] {
background-color: #f8f9fa; /* Фон при открытом состоянии */
}
details > summary {
cursor: pointer; /* Курсор в виде руки, чтобы обозначить кликабельность */
font-weight: 600; /* Подчеркиваем заголовок визуально */
list-style: none; /* Убираем стандартный маркер, если он оформлен как список */
}Комментарии:
details[open]позволяет изменить оформление при раскрытии (например, фон, тень, рамку).details > summary— стили только для заголовка внутри блока.
Убираем стандартный маркер
В разных браузерах <summary> отображается с встроенным маркером (стрелочка, треугольник). Иногда он выглядит не так, как вам нужно. Давайте посмотрим, как его убрать:
details > summary {
list-style: none; /* Убираем маркер в некоторых браузерах */
}
/* Для WebKit-браузеров (Chrome, Safari, Edge) */
details > summary::-webkit-details-marker {
display: none; /* Прячем стандартный маркер */
}После этого вы можете добавить свой маркер, например, через псевдоэлемент:
details > summary::before {
content: "▶"; /* Стрелка вправо */
display: inline-block;
margin-right: 0.5rem; /* Отступ между стрелкой и текстом */
transition: transform 0.2s; /* Плавный поворот */
}
/* Поворачиваем стрелку, когда блок открыт */
details[open] > summary::before {
transform: rotate(90deg); /* Стрелка вниз */
}Как видите, этот код делает управление состоянием более наглядным: стрелка поворачивается при открытии.
Стилизация содержимого внутри details
Обычно содержимое <details> вы стилизуете так же, как обычные блочные элементы. Но иногда удобно сделать небольшие отступы, чтобы отделить его от заголовка:
details > *:not(summary) {
margin-top: 0.5rem; /* Отступ сверху, чтобы текст не «лип» к summary */
}Или, например, задать другой шрифт, цвет текста и т.д.
Практические примеры использования
Простой FAQ без JavaScript
Давайте посмотрим, как сделать блок часто задаваемых вопросов (FAQ), не прибегая к скриптам:
<section>
<h2>Часто задаваемые вопросы</h2>
<details>
<summary>Как оформить заказ</summary>
<p>Выберите товар, добавьте его в корзину и перейдите к оформлению заказа.</p>
</details>
<details>
<summary>Какие способы оплаты вы принимаете</summary>
<p>Мы принимаем банковские карты, электронные кошельки и оплату при доставке.</p>
</details>
<details>
<summary>Можно ли изменить адрес доставки после оформления</summary>
<p>Да, напишите в службу поддержки до передачи заказа курьерской службе.</p>
</details>
</section>Комментарии:
- каждый
<details>— отдельный вопрос; <summary>— текст вопроса;<p>внутри — ответ.
Такой FAQ:
- легко читается;
- хорошо индексируется поисковыми системами;
- не требует JS и при этом интерактивен.
Дополнительные параметры в форме
Элементы details/summary хорошо смотрятся, когда нужно скрыть неосновные настройки. Например, дополнительные параметры фильтра:
<form>
<label>
Поиск по названию
<input type="text" name="q">
</label>
<details>
<summary>Дополнительные фильтры</summary>
<!-- Скрытые по умолчанию параметры -->
<div>
<label>
Минимальная цена
<input type="number" name="price_min">
</label>
</div>
<div>
<label>
Максимальная цена
<input type="number" name="price_max">
</label>
</div>
<div>
<label>
Только в наличии
<input type="checkbox" name="in_stock" value="1">
</label>
</div>
</details>
<button type="submit">Искать</button>
</form>Покажу вам, зачем это удобно:
- основное поле поиска всегда на виду;
- дополнительные параметры не загромождают интерфейс;
- при этом они доступны при необходимости одним кликом.
Технические подробности, «спойлеры», описания
Еще один частый сценарий — скрывать второстепенную техническую информацию, чтобы не перегружать текст.
<article>
<h2>Как мы измеряем производительность</h2>
<p>
В главной части статьи мы используем усредненные значения времени
ответа системы в реальных условиях эксплуатации.
</p>
<details>
<summary>Показать методику измерений</summary>
<p>
Для измерений мы использовали синтетические нагрузки с профилем,
близким к боевой эксплуатации системы.
</p>
<ul>
<li>Нагрузка - 1000 запросов в секунду</li>
<li>Продолжительность теста - 30 минут</li>
<li>Система мониторинга - Prometheus</li>
</ul>
</details>
</article>Так вы оставляете основную мысль на виду, а те, кто хочет углубиться, легко добираются до деталей.
Работа с details и summary в JavaScript
Чтение и изменение состояния open
Хотя <details> отлично работает и без JS, иногда вам нужно управлять его состоянием программно. Например, открыть все блоки по кнопке.
Вот базовый пример:
<button id="expand-all">Развернуть все</button>
<button id="collapse-all">Свернуть все</button>
<details class="faq-item">
<summary>Вопрос 1</summary>
<p>Ответ 1</p>
</details>
<details class="faq-item">
<summary>Вопрос 2</summary>
<p>Ответ 2</p>
</details>
<script>
// Находим кнопки управления
const expandAllBtn = document.getElementById('expand-all')
const collapseAllBtn = document.getElementById('collapse-all')
// Находим все элементы details
const detailsList = document.querySelectorAll('details.faq-item')
// Открываем все details при клике на кнопку "Развернуть все"
expandAllBtn.addEventListener('click', () => {
detailsList.forEach(details => {
details.open = true // Свойство .open управляет состоянием
})
})
// Закрываем все details при клике на кнопку "Свернуть все"
collapseAllBtn.addEventListener('click', () => {
detailsList.forEach(details => {
details.open = false
})
})
</script>Комментарии:
- у элемента
<details>есть DOM-свойствоopen(boolean); details.open = true;— раскрывает блок;details.open = false;— сворачивает блок;- изменения отражаются в разметке: атрибут
openдобавляется/удаляется.
Обработка событий toggle
У <details> есть специальное событие toggle. Оно вызывается каждый раз, когда состояние блока меняется (раскрыт/свернут).
Давайте посмотрим, как это использовать:
<details id="loggable-details">
<summary>Логи работы системы</summary>
<p>Здесь будут показаны последние события.</p>
</details>
<script>
// Находим элемент details
const loggableDetails = document.getElementById('loggable-details')
// Подписываемся на событие toggle
loggableDetails.addEventListener('toggle', () => {
// Проверяем текущее состояние
if (loggableDetails.open) {
console.log('Блок с логами открыт')
// Здесь вы можете, например, подгрузить данные с сервера
} else {
console.log('Блок с логами закрыт')
}
})
</script>Как видите, этот код выполняет простую задачу: реагирует на раскрытие и скрытие содержимого. Это удобно, если вы хотите загружать данные только при первом открытии или отправлять аналитику.
Вложенные details: аккордеоны и сложные структуры
Вложенные раскрывающиеся блоки
Иногда вам нужно сделать многоуровневые раскрывающиеся структуры (например, в документации).
<details>
<summary>Установка</summary>
<p>Ниже вы найдете инструкции по установке под разные операционные системы.</p>
<details>
<summary>Windows</summary>
<p>Скачайте установщик и следуйте шагам мастера установки.</p>
</details>
<details>
<summary>Linux</summary>
<p>Используйте пакетный менеджер вашей системы или сборку из исходников.</p>
</details>
</details>Особенности:
- внутренние
<details>работают независимо от внешнего; - если внешний блок свернуть, внутренние скрываются вместе с ним;
- при повторном открытии внешний блок не меняет внутреннее состояние (если оно было открыто, таким и останется).
Аккордеон на базе details
Классический «аккордеон» — это список секций, где в каждый момент может быть открыт только один блок. Смотрите, как сделать это с помощью <details> и немного JS:
<section id="faq-accordion">
<h2>FAQ</h2>
<details>
<summary>Вопрос 1</summary>
<p>Ответ 1</p>
</details>
<details>
<summary>Вопрос 2</summary>
<p>Ответ 2</p>
</details>
<details>
<summary>Вопрос 3</summary>
<p>Ответ 3</p>
</details>
</section>
<script>
// Находим контейнер с аккордеоном
const faqAccordion = document.getElementById('faq-accordion')
// Находим все details внутри
const items = faqAccordion.querySelectorAll('details')
// Для каждого details добавляем обработчик toggle
items.forEach(item => {
item.addEventListener('toggle', () => {
// Если текущий элемент открыт, закрываем остальные
if (item.open) {
items.forEach(otherItem => {
if (otherItem !== item) {
otherItem.open = false // Закрываем все остальные блоки
}
})
}
})
})
</script>Обратите внимание, как этот фрагмент кода решает задачу:
- событие
toggleсрабатывает при каждом переключении; - если блок открылся (
item.open === true), все остальные блоки закрываются; - при закрытии блока вручную другие не открываются — логика остается простой и предсказуемой.
Особенности поведения и подводные камни
Не вкладывайте интерактивные элементы внутрь summary без необходимости
Хотя технически в <summary> можно поместить ссылки, кнопки и другие интерактивные элементы, это часто создает конфликт поведения:
- клики по вложенному элементу могут одновременно:
- активировать сам элемент (например, ссылку),
- открыть/закрыть блок
<details>;
- поведение может отличаться в разных браузерах.
Без крайней необходимости лучше ограничиться простым текстом и, возможно, иконками (<span>, <svg> и т.п.).
Если все-таки нужно добавить ссылку, стоит предотвращать всплытие клика:
<details>
<summary>
О продукте
<a href="/more" id="more-link">Подробнее</a>
</summary>
<p>Краткое описание товара.</p>
</details>
<script>
// Находим ссылку внутри summary
const moreLink = document.getElementById('more-link')
moreLink.addEventListener('click', event => {
event.stopPropagation() // Останавливаем всплытие события
// Теперь клик по ссылке не переключит details
})
</script>Этот код гарантирует, что клик по ссылке не будет сворачивать или разворачивать родительский блок.
Поддержка в браузерах
На момент последних лет <details> и <summary> поддерживаются современными версиями:
- Chrome;
- Firefox;
- Safari;
- Edge;
- мобильные браузеры на популярных платформах.
Старые Internet Explorer эти элементы не поддерживают. Если вам важно их поддерживать, необходим полифилл на JS. Сейчас такие случаи редки, но в корпоративных системах с очень старыми браузерами это все еще встречается.
SEO и индексация
Поисковые системы умеют индексировать содержимое <details>. Но важно учитывать:
- информация внутри
<details>может считаться менее приоритетной; - лучше не прятать в свернутом блоке ключевой контент страницы:
- основные заголовки;
- главный текст;
- критически важные описания товара и услуг.
Используйте <details> для дополнительной, поясняющей и технической информации.
Рекомендации по использованию в реальных проектах
Когда использовать details/summary
Подходящие сценарии:
- FAQ и справочные разделы;
- дополнительные параметры фильтров;
- технические детали, документация, методики измерений;
- большие блоки второстепенной информации (например, историю изменений).
В общем, это хороший способ «сжать» интерфейс, когда контента много, но не все нужно показывать сразу.
Когда лучше отказаться от details/summary
Не стоит использовать <details>:
- для критически важного контента, который должен быть виден сразу;
- если вам нужна сложная анимация высоты и индивидуальные сценарии;
- если нужен сложный аккордеон с возможностью сохранять состояние между страницами без участия JS (в этом случае вы все равно придете к JavaScript и, возможно, к другой структуре).
Иногда проще реализовать собственный компонент на <button> + <div> и полностью контролировать его поведение, особенно если у вас уже есть библиотека UI-компонентов.
Тестирование доступности
Перед тем как запускать блоки на <details> в продакшн, проверьте:
- как они работают только с клавиатурой:
- Tab, Shift+Tab, Enter, Space;
- как они читаются скринридером (NVDA, VoiceOver и т.д.);
- не исчезает ли фокус при кастомной стилизации;
- не прячете ли вы важную информацию в закрытом состоянии.
Если вы вносите изменения через JavaScript (например, делаете аккордеон), убедитесь, что:
- не ломаете стандартное поведение;
- не добавляете избыточные ARIA-атрибуты, которые конфликтуют со встроенной семантикой.
Заключение
Элементы details и summary — это простой способ добавить интерактивные раскрывающиеся блоки на страницу без JavaScript. Вы получаете:
- семантическую разметку, понятную и браузеру, и вспомогательным технологиям;
- встроенное управление состоянием через атрибут open;
- поддержку клавиатуры и базовой доступности «из коробки»;
- гибкие возможности стилизации через CSS и управления через JS при необходимости.
Если вы хотите сделать FAQ, скрыть дополнительные параметры формы, спрятать технические детали или реализовать простой аккордеон, имеет смысл сначала попробовать именно details/summary. В большинстве типичных сценариев этого достаточно, и вам не понадобится сложный самописный компонент.
Частозадаваемые технические вопросы по теме и ответы
Как сделать плавную анимацию раскрытия и сворачивания details
По умолчанию <details> переключается мгновенно. Для плавной анимации можно использовать max-height и JS:
details[open] > div.content {
max-height: 500px; /* Достаточно большое значение */
transition: max-height 0.3s;
}
details > div.content {
max-height: 0;
overflow: hidden; /* Скрываем содержимое при малой высоте */
}<details>
<summary>Заголовок</summary>
<div class="content">
<p>Анимируемое содержимое.</p>
</div>
</details>Атрибут open переключает состояние, а CSS-анимация срабатывает на изменении max-height. Важно подобрать значение max-height под максимальный ожидаемый размер блока.
Почему псевдоэлемент ::marker не работает с summary
Псевдоэлемент ::marker корректно работает с элементами списка (li). <summary> не является элементом списка, поэтому маркер списка может появляться, но поведение непредсказуемо и зависит от браузера. Для кастомного маркера надежнее использовать ::before:
details > summary::before {
content: "▶";
}Так вы получаете единое поведение во всех современных браузерах.
Как сделать так, чтобы один из блоков details был всегда открыт
Уберите возможность закрывать последний открытый блок. Например, в аккордеоне:
const items = document.querySelectorAll('#faq details')
items.forEach(item => {
item.addEventListener('toggle', () => {
const opened = Array.from(items).filter(i => i.open)
if (opened.length === 0) {
// Если все закрыты, снова открываем текущий
item.open = true
}
})
})Теперь пользователь не сможет оставить все блоки закрытыми.
Можно ли управлять details через атрибуты data-*
Можно: вы можете добавлять к <details> собственные атрибуты data-* и использовать их в JS. Например, чтобы открыть только те блоки, где data-group="advanced":
document
.querySelectorAll('details[data-group="advanced"]')
.forEach(d => d.open = true)Это удобно для группировки логики, не перегружая классами.
Как синхронизировать состояние details с URL (хеш)
Используйте хеш в адресной строке и id у <details>:
<details id="shipping">
<summary>Доставка</summary>
<p>Условия доставки...</p>
</details>// Открываем блок, если его id совпадает с хешем
const idFromHash = location.hash.slice(1)
const target = document.getElementById(idFromHash)
if (target && target.tagName === 'DETAILS') {
target.open = true
}
// Обновляем хеш при открытии
document.querySelectorAll('details').forEach(d => {
d.addEventListener('toggle', () => {
if (d.open) {
history.replaceState(null, '', '#' + d.id)
}
})
})Так вы позволяете делиться ссылками на уже раскрытый блок.
Постройте личный план изучения Html до уровня Middle — бесплатно!
Html — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Все гайды по Html
Лучшие курсы по теме

HTML и CSS
Антон Ларичев
TypeScript с нуля
Антон Ларичев