12k
All articles

Создание всплывающей подсказки на чистом CSS

Создайте pure CSS tooltip с ::after, data-tooltip и переходами opacity, а также поддержкой focus-visible и важными ограничениями доступности.

OpenReplay Team
OpenReplay Team
Создание всплывающей подсказки на чистом 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 полностью устраняет этот эффект.

Почему для центрирования используется 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.

Часто задаваемые вопросы

Можно ли разместить CSS-подсказку над, под или сбоку от триггера?

Да. Положение управляется свойствами смещения у псевдоэлемента. Используйте 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, который в современных браузерах поддерживает более гибкое нативное поведение позиционирования.

Достаточно ли доступна CSS-подсказка для использования в продакшене?

Это приемлемо для несущественных, дополнительных подсказок на фокусируемых элементах вроде кнопок и ссылок. Этого недостаточно, когда подсказка несёт информацию, необходимую для выполнения задачи, поскольку содержимое псевдоэлемента ненадёжно озвучивается скринридерами, а поведение hover непоследовательно на сенсорных устройствах. Для критически важного содержимого используйте реальный DOM-элемент с aria-describedby и поведением, управляемым через JavaScript, или рассмотрите Popover API.

Digital experience platform

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.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.