Эта статья — перевод оригинальной статьи «Perfectly Pointed Tooltips: To The Corners».

1 часть - Идеально размещённые тултипы: база

2 часть - Идеально размещённые тултипы: все четыре стороны

Также я веду телеграм канал «Frontend по‑флотски», где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

Готовы к последнему челленджу?

Мы продолжаем делать тултипы, которые “следуют” за своим якорем, и на этот раз поработаем с новыми позициями и изучим новые приёмы. Я буду исходить из того, что вы прочитали и поняли первые две части, поэтому не стану заново объяснять уже разобранные вещи. Сразу предупрежу: если вы их пропустили, местами можно ощутимо запутаться.

На момент написания этой статьи полный набор нужных нам возможностей поддерживают только Chrome и Edge.

Как обычно, вот демо того, что мы будем делать:

https://codepen.io/t_afif/pen/yyepRJM

На этот раз вместо сторон я рассматриваю углы. Это ещё один распространённый паттерн в мире тултипов. Структура кода и начальная конфигурация остаются такими же, как в предыдущих примерах, так что давайте сразу перейдём к новому.

Определяем позиции

Если вы уже успели покликать мой интерактивный демо-пример, то знаете, с какой позиции мы начнём:

position-area: top left;

Остальные позиции логично будут: сверху справа, снизу слева и снизу справа. Мы уже выяснили, что жёстко задавать все позиции — не самый удачный вариант, так что давайте воспользуемся flip’ом!

Перевёрнутые значения такие:

position-try-fallbacks: flip-inline, flip-block, flip-block flip-inline; 
https://codepen.io/t_afif/pen/qEbMJaB

Плюс такой конфигурации в том, что мы не используем 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% относятся именно к якорю.

https://codepen.io/t_afif/pen/GgoXYEm

Пока ничего хитрого: у нас есть квадрат позади якоря.

Теперь немного увеличим его размер, чтобы он также касался тултипа. Для этого добавим к размерам удвоенный отступ, заданный переменной --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)));
}
https://codepen.io/t_afif/pen/azdaREb

Кажется, что эта идея ни к чему не ведёт, но поверьте, мы уже почти у цели.

Теперь “высекаем” из псевдоэлемента форму хвостика в каждом углу — как на иллюстрации ниже.

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

https://codepen.io/t_afif/pen/qEbMJyZ

Начинаете замечать трюк? Хвостики уже стоят правильно (можете потаскать якорь и посмотреть), но нам всё ещё нужно спрятать лишние.

Нужно всего лишь добавить одну строчку кода в тултип:

clip-path: inset(0) margin-box;

Понимаю, что это выглядит не слишком интуитивно, но объяснение на самом деле довольно простое. Даже если псевдоэлемент использует position: fixed и “потерял связь” с тултипом, он всё равно остаётся частью его содержимого, поэтому обрезка (clip-path), применённая к тултипу, затрагивает и его.

В нашем случае clip-path берёт за опорную область margin-box и с помощью inset(0) формирует простой прямоугольник, который показывает только то, что находится внутри него. Иными словами, всё, что выходит за пределы области margin, скрывается.

Включите “debug mode” в демо ниже — вы увидите чёрный прямоугольник, который иллюстрирует область действия clip-path.

https://codepen.io/t_afif/pen/yyepRJM

В этот прямоугольник помещается только один хвостик — и это именно то, что нам нужно!

Крутой трюк, правда? А нельзя ли применить его и к предыдущему демо?

Можно! Вся эта серия статей вообще могла бы быть одной — где я подробно разбираю только этот трюк и применяю его ко всем трём примерам. Но мне хотелось покопаться в разных идеях и, что ещё важнее, разобраться с anchor positioning на множестве вариантов. Плюс всегда полезно иметь несколько способов прийти к одному и тому же результату.

Как насчёт попробовать переделать прошлый пример с использованием этой техники? Считайте это домашкой, чтобы закрепить всё, что вы узнали из этой серии. Мою реализацию вы найдёте в следующем разделе.

Больше примеров

Давайте начнём с предыдущих демо-примеров, применив к ним новый приём. Как обычно, у вас есть режим отладки, чтобы увидеть, что происходит “за кулисами”.

https://codepen.io/t_afif/pen/XJXPywg
https://codepen.io/t_afif/pen/WbrgLvr

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

https://codepen.io/t_afif/pen/QwyZLzZ

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

https://codepen.io/t_afif/pen/KwVGwom

Заключение

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

Сейчас у этой фичи только первый уровень (Level 1). Во втором уровне появится ещё больше возможностей — одна из них позволит запрашивать fallback-позиции и применять к ним кастомный CSS. Вот один из предыдущих демо-примеров, переписанный с использованием этой будущей техники:

https://codepen.io/t_afif/pen/ogbPrWG

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

Комментарии (0)