Перейти к содержимому
Плотность
Тема

Оверлеи

Поповер

Небольшой блок, всплывающий над интерфейсом по нажатию на кнопку и исчезающий по клику мимо. Целиком на платформе: popover + popovertarget, без единой строки скрипта.

Компонент
Разметка
<button class="inst-btn" type="button" popovertarget="pop-actions">Действия</button>
<div class="inst-popover inst-popover--anchored" id="pop-actions" popover>
  <div class="inst-menu" role="menu">
    <span class="inst-menu-label">Прогон #4127</span>
    <button class="inst-menu-item" type="button" role="menuitem">Перезапустить</button>
    <button class="inst-menu-item" type="button" role="menuitem">Скопировать id</button>
  </div>
</div>

Использование

<button class="inst-btn" type="button" popovertarget="pop-1">Действия</button>
<div class="inst-popover inst-popover--anchored" id="pop-1" popover>…</div>
Что Обязательно Почему
popover на блоке да, у всплывающего Без него это плавающая поверхность в потоке: ни верхнего слоя, ни закрытия по Escape
id на блоке да Кнопка ссылается на него по имени
popovertarget="id" на кнопке да Связка и неявный якорь одновременно
type="button" на кнопке да Иначе внутри формы она её отправит

Содержимое поповера своего класса не имеет: чаще всего это меню, но может быть любая разметка.

Вид отделён от механизма. .inst-popover — это поверхность; поведение верхнего слоя добавляет атрибут popover. Поэтому тот же вид можно взять для того, что плавает, но поповером не является: списка результатов комбобокса, подсказки автодополнения. Без атрибута блок просто виден, показом управляет приложение.

Когда использовать

Используйте Возьмите другое
Список действий над объектом, вызываемый кнопкой Выбор одного значения из спискаселект: у него есть значение, а у поповера нет
Подробности, которые не нужны постоянно и не блокируют работу Ответ, без которого нельзя продолжитьмодалка: она забирает фокус и гасит фон
Пояснение, которое обрежется внутри панели с overflow: hidden Короткая подпись к контролутултип, если он не внутри обрезающего контейнера
Длинная форма или сводка сбоку — открывается той же кнопкой Долгий контекст, который держат открытымшторка

Варианты

Пример
Встаёт под кнопкой и переворачивается, если снизу не помещается.
Без модификатора поповер стоит по центру экрана.
Разметка
<button class="inst-btn" type="button" popovertarget="pop-anchored">Под кнопкой</button>
<div class="inst-popover inst-popover--anchored" id="pop-anchored" popover>
  Встаёт под кнопкой и переворачивается, если снизу не помещается.
</div>
<button class="inst-btn" type="button" popovertarget="pop-centered">По центру</button>
<div class="inst-popover" id="pop-centered" popover>
  Без модификатора поповер стоит по центру экрана.
</div>
Модификатор Где встаёт Как меряется ширина
без модификатора По центру экрана max-content в пределах 11–22rem
inst-popover--anchored Под вызвавшей кнопкой То же
inst-popover--fill Там, куда его поставили Во всю ширину родителя

Якорь у --anchoredнеявный: кнопка с popovertarget уже является якорем для своего поповера, поэтому имена якорей заводить не нужно.

Правило спрятано под @supports (position-area: block-end). Где position-area не поддержан, поповер встаёт по центру экрана: деградирует, а не ломается. Раскладка задана логическими значениями, поэтому в RTL зеркалится сама. Если снизу или сбоку не помещается, position-try-fallbacks переворачивает поповер по нужной оси, а не выпускает его за край экрана.

inst-popover--fill нужен там, где плашка обязана совпасть по ширине с тем, под чем стоит: выпадающий список под полем. Список у́же или ши́ре поля читается как чужой элемент, случайно оказавшийся рядом.

Размеры

Свойство Значение
минимальная ширина 11rem
максимальная ширина min(22rem, 100vw - var(--space-8)) — на узком экране поповер не вылезет за край
ширина по содержимому max-content в этих пределах
с --fill 100% родителя, без потолка

Тень здесь при деле: --shadow-popover означает «плавает сверху и сейчас исчезнет» — то есть описывает поповер целиком. Второй и последний носитель тени в ките — модалка.

Поведение

Поповер не имеет ни одной строки поведения в ките, потому что всё это уже есть в браузере.

Что Кто делает
Верхний слой — поповер не срежет ни overflow, ни z-index соседа Платформа
Закрытие по Escape Платформа
Закрытие по клику мимо (лёгкое закрытие) Платформа
Возврат фокуса на вызвавшую кнопку Платформа
Связка кнопки и блока popovertarget="id"
Появление и исчезновение Кит: переход по opacity и translate

Переход доигрывает и на закрытии, потому что в правиле объявлены display и overlay с allow-discrete. Без них поповер исчезал бы мгновенно, не дождавшись собственной анимации.

Правила

Так Всплывающее — через popover

Верхний слой, Escape, лёгкое закрытие и возврат фокуса приходят от платформы. Ни одной строки скрипта.

Не так Плавающая плашка на z-index

Число в z-index работает до первого чужого числа побольше, а overflow: hidden у родителя срежет плашку целиком.

Так Плашка под полем — во всю его ширину

inst-popover--fill. Список у́же или ши́ре поля читается как чужой элемент рядом, а не как его продолжение.

Не так Панель вместо плавающей поверхности

Панель — это место на экране: другой радиус, другая поверхность, тени у неё нет. Приписанная ей тень поповера даёт две разные плашки на одном экране.

Доступность

Клавиатура Enter/Space на кнопке открывает, Escape закрывает, фокус возвращается на кнопку. Всё нативное, кит ничего не перехватывает
Подложки нет ::backdrop прозрачен: поповер не блокирует страницу и не притворяется модальным
Роль содержимого Кит роли не выдумывает. Если внутри меню — role="menu" и role="menuitem" ставятся в разметке
Поверхность без атрибута .inst-popover без popover не имеет ни верхнего слоя, ни закрытия по Escape: показ, скрытие и роль — на приложении
Уменьшенное движение Переход схлопывается до 0.01ms, а не выключается: машины состояний, слушающие transitionend, продолжают работать
Печать Поповер на листе не печатается: он по определению временный

API

ИмяЗначениеЧто делает
класс
inst-popoverПлавающая поверхность: рамка, радиус, фон, тень, внутренний отступ
модификатор
inst-popover--anchoredПривязка к вызвавшей кнопке. Под @supports
inst-popover--fillШирина во всю ширину родителя вместо max-content. Для выпадающего списка под полем
атрибут
popoverauto · manualДобавляет к поверхности верхний слой, закрытие по Escape и переход появления
popovertargetНа кнопке. Связка и неявный якорь одновременно
токен
--surface-overlaylight-dark(var(--n-0), var(--n-11))
--shadow-popover0 4px 12px -2px var(--shadow-color-far), 0 2px 4px -2px var(--shadow-color-near)
--borderlight-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11))
--hairline1px
--radius-md7px
--space-12px
--space-24px
--space-832px
--dur-2140ms
--ease-outcubic-bezier(0.22, 0.61, 0.36, 1)

Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument