Создание всплывающей подсказки на чистом CSS
Если вам нужна легковесная всплывающая подсказка и вы не хотите подключать JavaScript-библиотеку для её создания, один лишь CSS справится с этой задачей на удивление хорошо. Никаких зависимостей, никаких обработчиков событий — только несколько грамотно расставленных CSS-правил.
В этой статье разбирается практичная современная реализация с использованием псевдоэлементов, data-атрибутов и переходов, а также те аспекты доступности, о которых действительно стоит знать.
Ключевые выводы
- Всплывающую подсказку на чистом CSS можно построить с помощью псевдоэлемента
::after,content: attr(data-tooltip)и триггеров:hover/:focus-visible. - Используйте
opacityиvisibility(а неdisplay), чтобы подсказка плавно появлялась и исчезала. pointer-events: noneпредотвращает мерцание, не позволяя самой подсказке получать события наведения.- У CSS-подсказок есть реальные ограничения по доступности — они ненадёжно считываются скринридерами, а поведение hover непоследовательно на сенсорных устройствах.
- Для критически важных подсказок интерфейса используйте JavaScript, Popover API или корректные ссылки
aria-describedbyна видимые элементы.
Как работает всплывающая подсказка на чистом CSS
Основная идея проста: применить position: relative к элементу-триггеру, а затем использовать псевдоэлемент ::after в качестве пузырька подсказки, позиционируя его абсолютно относительно родителя. По умолчанию он скрыт и появляется при :hover и :focus-visible.
Поскольку псевдоэлементы не могут напрямую считывать данные из DOM, текст подсказки передаётся через атрибут data-tooltip и подтягивается с помощью content: attr(data-tooltip). Это сохраняет HTML чистым и избавляет от необходимости дублировать текст в скрытом <span>.
HTML-структура
<button
class="tooltip-trigger"
data-tooltip="Saves your current progress"
>
Save
</button>
Важно использовать в качестве элемента-триггера <button> или ссылку (<a>). Эти элементы изначально получают фокус, что означает доступность для пользователей клавиатуры без каких-либо дополнительных усилий.
Замечание: если вы хотите использовать
aria-describedby, он должен указывать наidреального видимого элемента в DOM, а не на псевдоэлемент. Так как эта техника использует::after, не указывайтеaria-describedby, если только вы не выводите текст подсказки внутри реального элемента.
CSS
.tooltip-trigger {
position: relative;
cursor: pointer;
}
/* Tooltip bubble */
.tooltip-trigger::after {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background-color: #1a1a1a;
color: #fff;
font-size: 0.8rem;
white-space: nowrap;
padding: 5px 10px;
border-radius: 4px;
/* Hidden by default */
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
z-index: 10;
pointer-events: none;
}
/* Show on hover and keyboard focus */
.tooltip-trigger:hover::after,
.tooltip-trigger:focus-visible::after {
opacity: 1;
visibility: visible;
}
Почему opacity + visibility, а не display?
Переключение между display: none и display: block нельзя анимировать через transition — подсказка просто резко появлялась бы и исчезала. Использование opacity вместе с visibility: hidden позволяет плавно растворять подсказку, при этом исключая её из взаимодействия с указателем в скрытом состоянии. Совместное применение visibility и opacity также не даёт скрытой подсказке оставаться визуально интерактивной.
Почему pointer-events: none?
Без этого свойства сам пузырёк подсказки может срабатывать на наведение, из-за чего подсказка начинает мерцать при движении курсора над ней. Установка pointer-events: none на элементе ::after полностью устраняет этот эффект.
Discover how at OpenReplay.com.
Почему для центрирования используется transform: translateX(-50%)?
Установка left: 50% смещает левый край подсказки к центру триггера. Затем translateX(-50%) сдвигает её обратно на половину собственной ширины, центрируя независимо от того, насколько широк текст подсказки — без необходимости вручную вычислять пиксели.
О каких ограничениях доступности нужно знать
У всплывающей подсказки на чистом CSS есть реальные ограничения:
- Скринридеры могут её не озвучивать. Свойство
contentу псевдоэлемента ненадёжно считывается всеми скринридерами. Для критически важной информации включайте её в видимый UI или используйтеaria-describedby, указывающий на реальный элемент. - Поведение hover непоследовательно на сенсорных устройствах. Не стоит полагаться на CSS-подсказки для передачи существенной информации.
- Нет роли или live-региона. Один лишь CSS не способен передать вспомогательным технологиям семантику подсказки.
Для простых дополнительных подсказок на интерактивных элементах CSS-подсказки вполне подойдут. Для всего, что критично для выполнения задачи, потребуется JavaScript для управления фокусом, объявлениями и состоянием ARIA.
Взгляд в будущее: CSS Anchor Positioning и Popover API
CSS Anchor Positioning делает размещение подсказок гораздо более гибким — можно «привязать» подсказку к любому элементу без необходимости задавать position: relative родителю. Поддержка Anchor Positioning в современных браузерах теперь широко доступна согласно Can I Use.
Для более богатого интерактивного поведения стоит изучить Popover API как нативную альтернативу для интерактивных «плавающих» интерфейсов.
Заключение
Всплывающая подсказка на чистом CSS, построенная с использованием ::after, content: attr(data-tooltip) и перехода visibility/opacity, получается аккуратной, быстрой и не требующей зависимостей. Сочетайте её с :focus-visible для поддержки клавиатуры — и вы получите надёжную базу. Только не забывайте честно оценивать ограничения CSS-подсказок: для всего, что выходит за рамки дополнительных подсказок, используйте JavaScript или нативную возможность платформы, например Popover API.
Часто задаваемые вопросы
Да. Положение управляется свойствами смещения у псевдоэлемента. Используйте bottom со значением calc(100% + 8px), чтобы поместить её сверху, top с calc(100% + 8px) — снизу, либо right и left для горизонтального размещения. Также можно создать классы-варианты, например tooltip-bottom или tooltip-right, которые переопределяют эти свойства.
Поведение hover непоследовательно на сенсорных устройствах, поэтому не стоит полагаться на CSS-подсказки для передачи существенной информации. Если содержимое подсказки важно на мобильных, выводите его в реальном элементе, управляемом через JavaScript, либо используйте Popover API, который обеспечивает более надёжную основу для интерактивных «плавающих» интерфейсов на разных типах ввода.
Правило white-space: nowrap удерживает подсказку в одну строку, что может приводить к выходу за пределы узких экранов. Замените его на max-width и разрешите перенос — например, max-width: 240px и white-space: normal. Для динамического определения краёв потребуется JavaScript или CSS Anchor Positioning, который в современных браузерах поддерживает более гибкое нативное поведение позиционирования.
Это приемлемо для несущественных, дополнительных подсказок на фокусируемых элементах вроде кнопок и ссылок. Этого недостаточно, когда подсказка несёт информацию, необходимую для выполнения задачи, поскольку содержимое псевдоэлемента ненадёжно озвучивается скринридерами, а поведение hover непоследовательно на сенсорных устройствах. Для критически важного содержимого используйте реальный DOM-элемент с aria-describedby и поведением, управляемым через JavaScript, или рассмотрите Popover API.
Truly understand users experience
See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..