Tooltip
Пояснение, появляющееся рядом с элементом по наведению или фокусу. Указатель ждёт задержку — задержаться на чём-то ещё не значит захотеть подсказку, — а клавиатура открывает сразу: придя по Tab, читатель уже выбрал. Собственного элемента у тултипа нет, поэтому каждый пакет цепляет его так, чтобы ничего не добавить в дерево: React клонирует ребёнка, Vue — vnode, Svelte цепляется к узлу, Angular — директива, снимающая собственный атрибут после сопоставления. Обёртка портировалась бы всюду и была бы хуже: на flex-элементе она меняла бы вёрстку, которую должна была только пояснить. ⚠️ Тултип на disabled-контроле не открывается никогда, и никакая обёртка с нашей стороны этого не изменит: отключённый элемент вообще не шлёт pointer-события, открывать нечем. Ровно наоборот тому, что нужно: подсказка на отключённой кнопке — это как раз та, что объясняет, *почему* она отключена. Ответ — aria-disabled="true" вместо disabled плюс ранний выход в собственном обработчике. Контрол остаётся фокусируемым и наводимым, объявляет себя отключённым и поддаётся объяснению; стили рисуют его инертным и — с тех пор как это проверили — больше не дают ему подпрыгивать, менять цвет и вдавливаться под указателем.
Код
<button type="button" class="aura-btn aura-btn--secondary" aria-describedby="t1">Delete</button>
<div id="t1" role="tooltip" data-state="open" class="aura-popup aura-tooltip">Delete this space</div>import { Button, Group, IconButton, Tooltip } from '@ilomee/aura-react';
/**
* Наведите указатель или поставьте фокус на любой из контролов. Панель портализуется в
* `document.body` и позиционируется через `place()`: переворачивается, когда не влезает, и следует
* за прокруткой. У второго показан `names`: у кнопки-иконки нет текста, поэтому там тултип не
* дополняет имя — он и есть имя, а быть тем и другим значило бы прозвучать дважды.
*/
export default function Demo() {
return (
<Group>
<Tooltip label="Delete this space">
<Button variant="secondary">Delete</Button>
</Tooltip>
<Tooltip label="Settings" names>
{/*
Named here as well as by the tooltip. `names` points `aria-labelledby` at the tooltip
only while it is open, so an icon-only button with nothing else would be nameless for
as long as nobody is hovering it — which is most of the time. `aria-labelledby` wins
while the tooltip is up, so the two never disagree.
*/}
<IconButton aria-label="Settings">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
<circle cx="12" cy="12" r="3" />
<path d="M12 2v4M12 18v4M2 12h4M18 12h4" />
</svg>
</IconButton>
</Tooltip>
</Group>
);
}<script setup lang="ts">
import { Tooltip, Button } from '@ilomee/aura-vue';
</script>
<template>
<Tooltip label="Delete this space">
<Button variant="secondary">Delete</Button>
</Tooltip>
</template><script lang="ts">
import { tooltip, Button } from '@ilomee/aura-svelte';
</script>
<Button variant="secondary" {@attach tooltip({ label: 'Delete this space' })}>Delete</Button>Аттачмент, а не компонент, поэтому не добавляет элемента: {@attach tooltip({ label })} прямо на триггере.
Используется как [auraTooltip] — директива, которую вы вешаете на свой элемент.
<button auraButton variant="secondary" auraTooltip="Delete this space">Delete</button>Директива снимает собственный атрибут auraTooltip в ngOnInit, поэтому итоговая разметка совпадает с остальными тремя. Angular сопоставляет директивы на этапе компиляции, так что удаление ничего не стоит.
Пропсы
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| label* | string | — | Плоский текст, не узел: тултип — это доступное имя или описание, а они сводятся к тексту; разметку внутри вспомогательные технологии проигнорируют. |
| children* | ReactElement | — | Ровно один элемент, который пробрасывает ref и раскрывает переданные пропсы. Все примитивы Aura так делают. |
| side | 'top' | 'right' | 'bottom' | 'left' | 'top' | |
| align | 'start' | 'center' | 'end' | 'center' | |
| delay | number | 400 | Только для указателя. Фокус не ждёт. |
| names | boolean | false | Сделать тултип доступным именем триггера (aria-labelledby), а не дополнением к нему (aria-describedby). У кнопки без подписи тултип — единственное имя. |
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| label* | string | — | |
| defaultслот | slot | — | Ровно один элемент. Клонируется, а не оборачивается. |
| side | 'top' | 'right' | 'bottom' | 'left' | 'top' | |
| align | 'start' | 'center' | 'end' | 'center' | |
| delay | number | 400 | |
| names | boolean | false |
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| label* | string | — | |
| side | 'top' | 'right' | 'bottom' | 'left' | 'top' | |
| align | 'start' | 'center' | 'end' | 'center' | |
| delay | number | 400 | |
| names | boolean | false |
| Имя | Тип | По умолчанию | Примечания |
|---|---|---|---|
| auraTooltip* | string | — | Подпись, названная по селектору, чтобы <button auraTooltip="Delete"> читался. |
| side | 'top' | 'right' | 'bottom' | 'left' | 'top' | |
| align | 'start' | 'center' | 'end' | 'center' | |
| delay | number | 400 | |
| names | boolean | false |
Какие классы отдаёт
Все фреймворки отдают одни и те же классы — именно поэтому четыре библиотеки выглядят одинаково. Их можно использовать напрямую, вообще без библиотеки компонентов.
| Класс | Роль | Примечания |
|---|---|---|
| .aura-popup | база | Обязательный. Без него модификатор не работает. |
| .aura-tooltip | база | Обязательный. Без него модификатор не работает. |
Токены и правила
components.css- слой
- var(--z-tooltip) — above every other float, on purpose
- ширина
- min(32ch, calc(100vw - 16px)) — a label, not a surface
- зазор до якоря
- var(--popover-offset)