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

Оверлеи

Тултип

Короткая подпись, появляющаяся при наведении и при фокусе. Единственный оверлей кита, сделанный без 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 — там нужен поповер.

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

<span class="inst-tooltip">
  <button class="inst-btn" type="button" aria-describedby="tt-1">Что это</button>
  <span class="inst-tooltip-text" role="tooltip" id="tt-1">Пояснение</span>
</span>
Что Обязательно Почему
role="tooltip" на подписи да Иначе это просто спрятанный текст
id на подписи да На него ссылается триггер
aria-describedby="id" на триггере да Единственное, что связывает подпись с контролом. Без него тултип для скринридера не существует
Триггер фокусируем да Показ идёт через :focus-within. Нефокусируемому элементу нужен tabindex="0"

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

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

Варианты

Пример
19:38:04 Открытие модалки — единственная строка скрипта во всём разделе оверлеев; закрытие, подложка и блокировка прокрутки обходятся без него.
Разметка
<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.

Поведение

Событие Селектор
наведение мышью .inst-tooltip:hover
фокус внутри обёртки .inst-tooltip:focus-within

Подпись не убирается из потока, а гасится прозрачностью, и на ней снята мышь (pointer-events: none): курсор, идущий к тултипу, не перехватывается им и не роняет наведение.

Тултип обрежется внутри контейнера с overflow: hidden. Он позиционируется position: absolute относительно своей обёртки и не живёт в верхнем слое. Это известная плата за то, что тултип работает одинаково всюду и не зависит от поддержки якорного позиционирования.

Там, где обрезка возможна — внутри панели, ячейки таблицы, аккордеона, — берите поповер: он в верхнем слое, и его не срежет ничто.

Правила

Так Тултип на дополнение

Пояснение к иконочной кнопке, полное значение обрезанной подписи — то, без чего можно обойтись.

Не так Необходимое в тултипе

На сенсорном экране наведения нет. Текст, без которого нельзя работать, стоит в потоке или в сноске.

Так aria-describedby на триггере

Единственное, что связывает подпись с контролом. Без него тултипа для скринридера не существует.

Не так Тултип вместо доступного имени

aria-describedby — это описание. Имя иконочной кнопке даёт aria-label.

Доступность

Клавиатура Тултип появляется по :focus-within, то есть при табе на триггер. Без этого он был бы доступен только мыши
Сенсорный экран Наведения там нет, а фокус приходит вместе с нажатием. Не кладите в тултип то, без чего нельзя обойтись
Не заменяет имя aria-describedby — это описание. Доступное имя иконочной кнопке даёт aria-label, а не тултип
Перекрытие Тултип стоит над триггером на --space-3: он не закрывает то, что поясняет
Уменьшенное движение Переход прозрачности схлопывается вместе со всеми остальными
Печать На листе не печатается

API

ИмяЗначениеЧто делает
класс
inst-tooltipОбёртка триггера. position: relative, отсюда считается положение
inst-tooltip-textСама подпись. Над триггером, по центру
модификатор
inst-tooltip-text--wideПеренос строк и потолок ширины 22rem
токен
--z-popover100
--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-sm5px
--space-24px
--space-36px
--text-xs0.75rem
--leading-ui1.4
--dur-180ms
--ease-outcubic-bezier(0.22, 0.61, 0.36, 1)

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