Оверлеи
Тултип
Короткая подпись, появляющаяся при наведении и при фокусе. Единственный оверлей библиотеки, сделанный без Popover API: обычным позиционированием, чтобы работать везде одинаково.
Разметка
<span class="inst-tooltip">
<button class="inst-btn inst-btn--icon inst-btn--ghost" type="button" aria-label="Обновить" aria-describedby="tt-refresh">
<svg class="inst-icon" aria-hidden="true"><use href="#i-refresh"/></svg>
</button>
<span class="inst-tooltip-text" role="tooltip" id="tt-refresh">Обновить</span>
</span>
<span class="inst-tooltip">
<button class="inst-btn inst-btn--ghost" type="button" aria-describedby="tt-pass">Что такое проход?</button>
<span class="inst-tooltip-text" role="tooltip" id="tt-pass">Один полный обход карты агентом</span>
</span>
Подпись центрируется по триггеру и не переворачивается: у обычного
позиционирования нет запасных мест. У края узкой области её обрежет ближайший
overflow: hidden — там нужен поповер.
Контракт
| Что | Обязательно | Почему |
|---|---|---|
role="tooltip" на подписи |
да | Иначе это просто спрятанный текст |
id на подписи |
да | На него ссылается триггер |
aria-describedby на триггере |
да | Указывает на id подписи и есть единственное, что связывает её с контролом. Без него тултип для скринридера не существует |
| Триггер фокусируем | да | Показ идёт через :focus-within. Нефокусируемому элементу нужен tabindex="0" |
Доступность
| Клавиатура | Тултип появляется по :focus-within, то есть при табе на триггер. Без этого он был бы доступен только мыши |
| Сенсорный экран | Наведения там нет, а фокус приходит вместе с нажатием. Не кладите в тултип то, без чего нельзя обойтись |
| Не заменяет имя | aria-describedby — это описание. Доступное имя иконочной кнопке даёт aria-label, а не тултип |
| Перекрытие | Тултип стоит над триггером на --space-3: он не закрывает то, что поясняет |
| Уменьшенное движение | Переход прозрачности схлопывается вместе со всеми остальными |
| Печать | На листе не печатается |
Варианты
Разметка
<span class="inst-tooltip">
<button class="inst-btn inst-btn--sm" type="button" aria-describedby="tt-short">Коротко</button>
<span class="inst-tooltip-text" role="tooltip" id="tt-short">19:38:04</span>
</span>
<span class="inst-tooltip" style="margin-inline-start:9rem">
<button class="inst-btn inst-btn--sm" type="button" aria-describedby="tt-wide">Подробно</button>
<span class="inst-tooltip-text inst-tooltip-text--wide" role="tooltip" id="tt-wide">Открытие модалки — единственная строка скрипта во всём разделе оверлеев; закрытие, подложка и блокировка прокрутки обходятся без него.</span>
</span>
Базовый тултип — в одну строку (white-space: nowrap). Это верно для подписи
из двух слов и ломается на предложении: строка растягивается за край экрана.
Поэтому длинному тексту нужен явный inst-tooltip-text--wide — перенос и
потолок ширины в 22rem.
Плашка центрируется по триггеру, и на последнем контроле в строке это
уводит её за край: nowrap-плашка в несколько раз шире глифа, который она
поясняет, и обрезается ровно та половина, которую читают.
inst-tooltip-text--end ставит её хвостовой край по хвостовому краю триггера —
правый в LTR, левый в RTL, одним правилом. Что контрол последний, знает
вызывающий, а не кит.
Поведение
| Событие | Селектор |
|---|---|
| наведение мышью | .inst-tooltip:hover |
| фокус внутри обёртки | .inst-tooltip:focus-within |
Подпись не убирается из потока, а гасится прозрачностью, и на ней снята мышь
(pointer-events: none): курсор, идущий к тултипу, не перехватывается им и не
роняет наведение.
Тултип обрежется внутри контейнера с overflow: hidden. Он позиционируется
position: absolute относительно своей обёртки и не живёт в верхнем слое. Это
известная плата за то, что тултип работает одинаково всюду и не зависит от
поддержки якорного позиционирования.
Там, где обрезка возможна — внутри панели, ячейки таблицы, аккордеона, — берите поповер: он в верхнем слое, и его не срежет ничто.
API
| Имя | Что делает |
|---|---|
| класс | |
inst-tooltip | Обёртка триггера. position: relative, отсюда считается положение |
inst-tooltip-text | Сама подпись. Над триггером, по центру |
| модификатор | |
inst-tooltip-text--end | Хвостовой край плашки по хвостовому краю триггера, а не по центру. Для последнего контрола в строке, где центрированная плашка уходит за контейнер |
inst-tooltip-text--wide | Перенос строк и потолок ширины 22rem |
| Имя | Значение | Откуда |
|---|---|---|
| токен | ||
--z-popover | 100 | одно на все ячейки |
--surface-overlay | light-dark(var(--n-0), var(--n-11)) | база +1: тема |
--shadow-popover | 0 4px 12px -2px var(--shadow-color-far), 0 2px 4px -2px var(--shadow-color-near) | одно на все ячейки |
--border | light-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11)) | одно на все ячейки |
--hairline | 1px | база +1: экран |
--radius-sm | 6px | база +4: масштаб |
--space-2 | 4px | одно на все ячейки |
--space-3 | 6px | одно на все ячейки |
--text-xs | 0.78125rem | база +4: масштаб |
--leading-ui | 1.4 | одно на все ячейки |
--dur-1 | 80ms | одно на все ячейки |
--ease-out | cubic-bezier(0.22, 0.61, 0.36, 1) | одно на все ячейки |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument