Оверлеи
Шторка
Панель во всю высоту у края экрана. Тот же нативный <dialog>, что и у
модалки, — другая раскладка, не другой компонент.
Разметка
<button class="inst-btn" type="button"
onclick="document.getElementById('dlg-params').showModal()">Параметры прогона</button>
<dialog class="inst-dialog inst-sheet" id="dlg-params">
<form method="dialog">
<div class="inst-dialog-head">
<span class="inst-dialog-title">Параметры прогона</span>
<button class="inst-btn inst-btn--sm inst-btn--ghost inst-dialog-close" type="submit" aria-label="Закрыть">✕</button>
</div>
<div class="inst-dialog-body">
<dl class="inst-kv">
<dt>Модель</dt><dd>opus</dd>
<dt>Агентов</dt><dd>7 из 12</dd>
<dt>Токенов</dt><dd>184 320</dd>
<dt>Начат</dt><dd>19:38:04</dd>
</dl>
</div>
<div class="inst-dialog-foot inst-dialog-foot--end">
<button class="inst-btn" type="submit">Закрыть</button>
</div>
</form>
</dialog>
Использование
inst-sheet — модификатор поверх inst-dialog, а не замена: класс базы
остаётся, части (-head, -body, -foot) те же.
<dialog class="inst-dialog inst-sheet" id="dlg">
<form method="dialog">
<div class="inst-dialog-head">…</div>
<div class="inst-dialog-body">…</div>
<div class="inst-dialog-foot inst-dialog-foot--end">…</div>
</form>
</dialog>
| Что | Обязательно | Почему |
|---|---|---|
inst-dialog вместе с inst-sheet |
да | Поверхность, тень, подложка и раскладка частей приходят из базы |
Нативный <dialog> |
да | Модальность и Escape — от платформы |
showModal() |
да | show() открывает немодально: ни подложки, ни инертного фона |
<form method="dialog"> внутри |
да | Закрытие кнопкой без скрипта |
aria-label у крестика |
да | Кнопка без текста иначе не имеет доступного имени |
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Подробности объекта, которые смотрят, не теряя список из виду | Вопрос, требующий ответа «да» или «нет» — модалка: она короче и центрируется |
| Длинная форма, не помещающаяся в модалку по высоте | Форма, ради которой уместно уйти на страницу — обычный вид: шторка в 28rem шириной тесна для сложной формы |
| Фильтры и параметры, которые правят и сразу видят результат | Список действий над объектом — меню в поповере |
| Второстепенный контекст сбоку от основного вида | Постоянная панель, которую не закрывают — колонка оболочки: у шторки есть подложка, и фон под ней выключен |
Устройство
Все части — от модалки: inst-dialog-head,
inst-dialog-title, inst-dialog-close, inst-dialog-body,
inst-dialog-foot, inst-dialog-foot--end, inst-dialog-foot-note.
Модификатор меняет раскладку:
| Свойство | Модалка | Шторка |
|---|---|---|
| ширина | min(34rem, 100vw - var(--space-8)) |
min(28rem, 100vw) |
| высота | min(80dvh, 100dvh - var(--space-8)) |
100dvh |
| поля | margin: auto — по центру |
Прижата к краю |
| радиус | --radius-lg |
0 — блок примыкает к краю экрана |
| рамка | По периметру | Только со стороны содержимого |
На узком экране min(28rem, 100vw) даёт шторку во всю ширину — отдельного
мобильного варианта заводить не нужно.
Варианты
<!-- У конечного края (по умолчанию) -->
<dialog class="inst-dialog inst-sheet">…</dialog>
<!-- У начального края -->
<dialog class="inst-dialog inst-sheet inst-sheet--start">…</dialog>
| Класс | Край | Граница |
|---|---|---|
inst-sheet |
Конечный (справа в LTR) | Волосок по начальному краю |
inst-sheet--start |
Начальный (слева в LTR) | Волосок по конечному краю |
Стороны логические, а не физические: в RTL шторка зеркалится сама, править ничего не нужно.
JS
Открытие — одна строка. Всё остальное берёт на себя платформа.
document.getElementById('dlg').showModal();
| Что | Кто делает |
|---|---|
| Открытие | Приложение: showModal() |
| Закрытие | <form method="dialog"> внутри — без скрипта |
Подложка, верхний слой, Escape |
Платформа |
| Возврат фокуса на открывшую кнопку | Платформа |
| Блокировка прокрутки фона | Кит, через ::backdrop |
Декларативные command и commandfor избавили бы и от этой строки, но пока
поддержаны не везде и в контракт кита не берутся.
Правила
Так Шторка для контекста, который держат открытым
Подробности объекта, фильтры, параметры — то, что правят и сразу видят результат.
Не так Шторка вместо вопроса
«Удалить?» короче и центрируется — это модалка. Панель во всю высоту ради двух кнопок избыточна.
Так Логические стороны
inst-sheet--start и умолчание заданы началом и концом строки, а не левым и
правым краем: в RTL зеркалится само.
Не так Шторка вместо колонки оболочки
У шторки есть подложка, и фон под ней выключен. Постоянная панель, к которой нужен доступ, — колонка оболочки.
Доступность
| Фокус | Ловушка фокуса и возврат на вызвавшую кнопку — от showModal() |
| Клавиатура | Escape закрывает. Кнопки внутри <form method="dialog"> закрывают нажатием |
| Фон инертен | Подложка выключает фон для мыши и для скринридера. Если нужен доступ к фону, это не шторка, а колонка оболочки |
| Прокрутка фона | Останавливается тем же правилом html:has(dialog:modal), что и у модалки |
| Высота | 100dvh, а не 100vh: на мобильных панель браузера не срезает подвал с кнопками |
| Печать | Шторка на листе не печатается |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-sheet | — | Раскладка во всю высоту у конечного края. Только вместе с inst-dialog |
| модификатор | ||
inst-sheet--start | — | Тот же блок у начального края |
| токен | ||
--surface-overlay | light-dark(var(--n-0), var(--n-11)) | |
--shadow-modal | 0 16px 40px -8px var(--shadow-color-far), 0 4px 10px -4px var(--shadow-color-near) | |
--scrim | light-dark(oklch(0 0 0 / 0.32), oklch(0 0 0 / 0.58)) | |
--border | light-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11)) | |
--hairline | 1px | |
--pad-card | var(--space-6) | |
--space-5 | 12px | |
--gap-inline | var(--space-4) | |
--text-md | 0.9375rem | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument