Обратная связь
Аккордеон
Группа сворачиваемых разделов, из которых открыт ровно один. На нативных
<details> с общим name, поэтому взаимное исключение, раскрытие и
клавиатура достаются от платформы — без единой строки JS.
Почему прогон занял 42 секунды?
Можно ли ограничить расход токенов?
Что происходит с артефактами после удаления?
Разметка
<div class="inst-accordion">
<details class="inst-accordion-item" name="faq" open>
<summary class="inst-accordion-head">Почему прогон занял 42 секунды?</summary>
<div class="inst-accordion-body">Три прохода расстановки ресурсов плюс запекание навмеша.
Основное время ушло на валидацию путей, которая перезапускалась дважды.</div>
</details>
<details class="inst-accordion-item" name="faq">
<summary class="inst-accordion-head">Можно ли ограничить расход токенов?</summary>
<div class="inst-accordion-body">Да, в параметрах проекта. Лимит проверяется перед стартом
каждого агента, а не по факту — прогон не уйдёт в минус.</div>
</details>
<details class="inst-accordion-item" name="faq">
<summary class="inst-accordion-head">Что происходит с артефактами после удаления?</summary>
<div class="inst-accordion-body">Удаляются вместе с прогоном. Резервная копия хранится семь
суток, восстановление — через поддержку.</div>
</details>
</div>
Использование
<div class="inst-accordion">
<details class="inst-accordion-item" name="faq" open>
<summary class="inst-accordion-head">Заголовок</summary>
<div class="inst-accordion-body">Содержимое</div>
</details>
<details class="inst-accordion-item" name="faq">
<summary class="inst-accordion-head">Второй</summary>
<div class="inst-accordion-body">Содержимое</div>
</details>
</div>
| Что | Обязательно | Почему |
|---|---|---|
<details> под inst-accordion-item |
да | Раскрытие, клавиатура и состояние open — от платформы |
<summary> под inst-accordion-head |
да | Только <summary> фокусируем и переключает <details> |
Общий name |
да, для аккордеона | Делает группу взаимоисключающей. Без него секции независимы |
| Прямое вложение | да | Правила открытой секции записаны через >: заголовок обязан быть прямым потомком <details> |
Настоящий заголовок внутри <summary> |
нет, но обычно да | <summary> сам по себе в оглавление страницы не попадает |
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Длинные разделы содержимого, из которых читают один | Равноправные виды одного объекта — вкладки: у них видно все названия сразу и нет прыжка высоты |
| Вопросы и ответы, справка, редко нужные подробности | Вызов инструмента с телом вывода — шаг агента: это другая работа, у него есть фаза и длительность |
| Секции формы, которые заполняют по очереди | Всё, что нужно видеть одновременно — панели в колонку: аккордеон прячет то, что сравнивают |
| Свёрнутые подробности внутри одного блока | Одиночный сворачиваемый блок — просто <details> без группы: inst-accordion предполагает группу |
Устройство
| Элемент | Класс | Тег |
|---|---|---|
| Контейнер группы | inst-accordion |
<div> |
| Секция | inst-accordion-item |
<details> |
| Заголовок-переключатель | inst-accordion-head |
<summary> |
| Содержимое | inst-accordion-body |
<div> |
Шеврон рисуется псевдоэлементом заголовка и поворачивается на 180° при
открытии — своего класса у него нет и разметки он не требует. Нативный
треугольник <summary> снят.
Волосок под заголовком появляется только у открытой секции
(.inst-accordion-item[open] > .inst-accordion-head): у закрытой отделять
нечего.
Поведение
Взаимное исключение
Общий атрибут name на элементах <details> делает группу
взаимоисключающей: открытие второй секции закрывает первую.
| Что нужно | Как |
|---|---|
| Открыта ровно одна секция | Общий name на всех <details> |
| Открыто сколько угодно | name не ставить |
| Какая открыта при загрузке | open на нужном <details> |
Уберите name — и секции станут независимыми, открывать можно сколько
угодно. Это единственная разница между «аккордеоном» и «списком
раскрывашек», и она в один атрибут.
Без name — открываются независимо
Вторая секция
Разметка
<div class="inst-accordion">
<details class="inst-accordion-item" open>
<summary class="inst-accordion-head">Без name — открываются независимо</summary>
<div class="inst-accordion-body">Эту можно оставить открытой и открыть следующую.</div>
</details>
<details class="inst-accordion-item">
<summary class="inst-accordion-head">Вторая секция</summary>
<div class="inst-accordion-body">Первая при этом не закроется.</div>
</details>
</div>
Правила
Так Нативный details с общим name
Взаимное исключение, раскрытие, клавиатура и поиск по странице приходят от платформы. JS не нужен вовсе.
Не так Своя раскрывашка на div
Она теряет aria-expanded, поиск по странице, раскрытие при печати и требует
скрипта ради того, что уже работает.
Так Заголовок нужного уровня внутри summary
Если секции — разделы страницы, <summary> сам в оглавление не попадёт.
Доступность
| Клавиатура | Tab до заголовка, Enter или Space — переключение. Всё нативное, кит ничего не перехватывает |
| Роль и состояние | <summary> сам объявляет себя переключателем и сообщает, раскрыт ли раздел. Своего aria-expanded ставить не надо и не нужно |
| Поиск по странице | Содержимое закрытой секции находится браузерным поиском, и секция раскрывается сама — это поведение <details>, ради него нативный элемент и взят |
| Заголовок | Если секции — разделы страницы, вложите в <summary> настоящий заголовок нужного уровня: <summary> сам по себе в оглавление не попадает |
| Цель нажатия | Заголовок нажимается целиком, от края до края: --control-h-lg в высоту |
| Уменьшенное движение | Поворот шеврона схлопывается вместе со всеми переходами |
| Печать | Закрытые секции раскрываются на листе: закрытый <details> на бумаге — невосстановимо потерянное содержимое |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-accordion | — | Контейнер. Колонка секций с зазором --space-1 |
inst-accordion-item | — | Секция. Рамка, радиус, overflow: hidden |
inst-accordion-head | — | Заголовок. Высота от --control-h-lg, шеврон у дальнего края |
inst-accordion-body | — | Содержимое |
| токен | ||
--space-1 | 2px | |
--space-3 | 6px | |
--pad-cell-x | var(--space-4) | |
--pad-panel | var(--space-5) | |
--gap-inline | var(--space-4) | |
--control-h-lg | 38px | |
--radius-md | 7px | |
--hairline | 1px | |
--border | light-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11)) | |
--border-subtle | light-dark(oklch(0 0 0 / 0.07), oklch(1 0 0 / 0.06)) | |
--surface-raised | light-dark(var(--n-0), var(--n-12)) | |
--surface-hover | light-dark(oklch(0 0 0 / 0.035), oklch(1 0 0 / 0.045)) | |
--size-chevron | 10px | |
--text-muted | light-dark(var(--n-8), var(--n-6)) | |
--text-sm | 0.8125rem | |
--weight-medium | 500 | |
--dur-1 | 80ms | |
--ease-out | cubic-bezier(0.22, 0.61, 0.36, 1) | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument