Идеально размещённые тултипы: по углам
- вторник, 2 декабря 2025 г. в 00:00:03
Эта статья — перевод оригинальной статьи «Perfectly Pointed Tooltips: To The Corners».
1 часть - Идеально размещённые тултипы: база
2 часть - Идеально размещённые тултипы: все четыре стороны
Также я веду телеграм канал «Frontend по‑флотски», где рассказываю про интересные вещи из мира разработки интерфейсов.
Готовы к последнему челленджу?
Мы продолжаем делать тултипы, которые “следуют” за своим якорем, и на этот раз поработаем с новыми позициями и изучим новые приёмы. Я буду исходить из того, что вы прочитали и поняли первые две части, поэтому не стану заново объяснять уже разобранные вещи. Сразу предупрежу: если вы их пропустили, местами можно ощутимо запутаться.
На момент написания этой статьи полный набор нужных нам возможностей поддерживают только Chrome и Edge.
Как обычно, вот демо того, что мы будем делать:

На этот раз вместо сторон я рассматриваю углы. Это ещё один распространённый паттерн в мире тултипов. Структура кода и начальная конфигурация остаются такими же, как в предыдущих примерах, так что давайте сразу перейдём к новому.
Если вы уже успели покликать мой интерактивный демо-пример, то знаете, с какой позиции мы начнём:
position-area: top left;Остальные позиции логично будут: сверху справа, снизу слева и снизу справа. Мы уже выяснили, что жёстко задавать все позиции — не самый удачный вариант, так что давайте воспользуемся flip’ом!
Перевёрнутые значения такие:
position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline; 
Плюс такой конфигурации в том, что мы не используем flip-start, поэтому можем спокойно задавать min-width (или max-height) без каких-либо проблем. Минус в том, что добавить “хвостик” становится сложнее: его нужно разместить в углах, и трюк с margin тут уже не сработает. Нужен другой хак.
Обратите внимание, что для управления зазором между тултипом и якорем я использую margin, а не inset. Оба варианта корректны, но позже вы увидите, почему в моём случае margin чуть лучше.
В предыдущих примерах мы рисовали одну общую фигуру со всеми хвостиками, а потом прятали ненужные части. Хвостик был того же цвета, что и тултип, и находился позади его контента, так что мы видели только то, что выходило за границу самого тултипа.
В этот раз я использую немного другую идею. Мы по-прежнему рисуем фигуру со всеми хвостиками, но способ “прятать лишнее” будет другим.
Сначала мы размещаем псевдоэлемент тултипа поверх якоря. Не “над ним сверху”, а так, чтобы они перекрывались.
#tooltip::before {
content: "";
position: fixed;
position-anchor: --anchor;
position-area: center;
width: anchor-size(width);
height: anchor-size(height);
}Я использую позиционирование fixed, чтобы тултип мог “видеть” якорь (мы уже обсуждали эту странность в первой части). Затем я размещаю элемент в центральной области — то есть поверх элемента-якоря (или под ним, в зависимости от z-index).
Я ввожу новую функцию — anchor-size(), которая тоже входит в Anchor Positioning API. Раньше мы использовали функцию anchor(), которая позволяет получать координаты якорного элемента. anchor-size() делает то же самое, но с размерами. Я использую её, чтобы псевдоэлемент был того же размера, что и якорь. Это как width: 100%, только эти самые 100% относятся именно к якорю.

Пока ничего хитрого: у нас есть квадрат позади якоря.
Теперь немного увеличим его размер, чтобы он также касался тултипа. Для этого добавим к размерам удвоенный отступ, заданный переменной --d, плюс значение --s, которое управляет одновременно и скруглением, и размером тултипа.
#tooltip {
--d: .5em; /* distance between anchor and tooltip */
--s: .8em; /* tail size & border-radius */
}
#tooltip:before {
width: calc(anchor-size(width) + 2*(var(--d) + var(--s)));
height: calc(anchor-size(height) + 2*(var(--d) + var(--s)));
}
Кажется, что эта идея ни к чему не ведёт, но поверьте, мы уже почти у цели.
Теперь “высекаем” из псевдоэлемента форму хвостика в каждом углу — как на иллюстрации ниже.

Я использую довольно громоздкое значение clip-path, чтобы получить финальную форму, но сам способ здесь не принципиален. Можно взять градиенты, SVG-фон, новую функцию shape() и так далее. Возможно, вы вообще захотите другой дизайн хвостиков. Важно лишь одно: вокруг якоря должно быть четыре хвостика.

Начинаете замечать трюк? Хвостики уже стоят правильно (можете потаскать якорь и посмотреть), но нам всё ещё нужно спрятать лишние.
Нужно всего лишь добавить одну строчку кода в тултип:
clip-path: inset(0) margin-box;Понимаю, что это выглядит не слишком интуитивно, но объяснение на самом деле довольно простое. Даже если псевдоэлемент использует position: fixed и “потерял связь” с тултипом, он всё равно остаётся частью его содержимого, поэтому обрезка (clip-path), применённая к тултипу, затрагивает и его.
В нашем случае clip-path берёт за опорную область margin-box и с помощью inset(0) формирует простой прямоугольник, который показывает только то, что находится внутри него. Иными словами, всё, что выходит за пределы области margin, скрывается.
Включите “debug mode” в демо ниже — вы увидите чёрный прямоугольник, который иллюстрирует область действия clip-path.

В этот прямоугольник помещается только один хвостик — и это именно то, что нам нужно!
Крутой трюк, правда? А нельзя ли применить его и к предыдущему демо?
Можно! Вся эта серия статей вообще могла бы быть одной — где я подробно разбираю только этот трюк и применяю его ко всем трём примерам. Но мне хотелось покопаться в разных идеях и, что ещё важнее, разобраться с anchor positioning на множестве вариантов. Плюс всегда полезно иметь несколько способов прийти к одному и тому же результату.
Как насчёт попробовать переделать прошлый пример с использованием этой техники? Считайте это домашкой, чтобы закрепить всё, что вы узнали из этой серии. Мою реализацию вы найдёте в следующем разделе.
Давайте начнём с предыдущих демо-примеров, применив к ним новый приём. Как обычно, у вас есть режим отладки, чтобы увидеть, что происходит “за кулисами”.


Завершу всё последним примером для самостоятельного изучения. Если хотите ещё один челлендж, можете сначала попробовать реализовать его сами, прежде чем смотреть мой код.

И вариант с изогнутым хвостиком:

Надеюсь, вам понравилась эта серия статей. Наша цель была — с помощью современного CSS реализовать распространённые паттерны тултипов и заодно исследовать мощный Anchor Positioning API. Это одна из тех современных фич, которые привносят в CSS совершенно новые механизмы. Мы уже далеко ушли от эпохи, когда мы просто задавали свойства и получали статичный результат. Теперь мы можем “связывать” разные элементы на странице, создавать условное позиционирование, описывать динамическое поведение, которое подстраивается под каждую ситуацию, и многое другое!
Сейчас у этой фичи только первый уровень (Level 1). Во втором уровне появится ещё больше возможностей — одна из них позволит запрашивать fallback-позиции и применять к ним кастомный CSS. Вот один из предыдущих демо-примеров, переписанный с использованием этой будущей техники:

Кода тут, возможно, больше, но он выглядит менее “хаковым” и более интуитивным. Можете сами представить, сколько всего можно сделать с помощью этого подхода.