Якорное позиционирование¶
Якорное позиционирование CSS позволяет декларативно разместить элемент относительно другого элемента.
Когда вы размещаете всплывающую подсказку или раскрывающееся меню, часто нужно поставить их относительно другого элемента на странице. Абсолютное позиционирование позволяет добиться такого эффекта, но более сложные требования раньше приходилось решать позиционированием через JavaScript.
Привязка элементов¶
Чтобы сделать элемент якорем, задайте ему anchor-name: любую строку, которая начинается с двух дефисов. По этому идентификатору позиционируемый элемент находит свой якорь, поэтому имя лучше выбирать говорящее. Одному элементу можно дать несколько имен якоря, если он служит якорем по-разному.
На позиционируемом элементе нужно задать несколько свойств, чтобы привязать его. Сначала выведите элемент из потока документа, чтобы он плавал: установите position: absolute или position: fixed.
Затем укажите, к какому якорю привязываться: задайте position-anchor равным имени, которое вы указали у якоря.
Наконец, задайте, как именно позиционировать элемент. О свойстве position-area речь пойдет дальше в этом модуле.
1 2 3 4 5 6 7 8 9 | |
Неявная привязка¶
Popover привязать еще проще. Когда вы открываете popover кнопкой с popovertarget или передаете source в showPopover({source}), у popover уже есть неявный якорь. Popover и так плавает: по умолчанию у него position: fixed, поэтому для размещения достаточно задать позицию.
1 2 3 4 5 6 | |
Нужно также сбросить внешний отступ popover через unset. По умолчанию margin у popover равен auto, и это влияет на позиционирование.
Область видимости возможных якорей¶
Якорное позиционирование удобно встраивать в компонент, чтобы паттерн вроде раскрывающегося меню можно было использовать в нескольких местах. Если одно и то же имя anchor-name встречается много раз, как сделать так, чтобы каждый позиционируемый элемент нашел правильный якорь?
В решениях на JavaScript каждому якорю дают уникальный идентификатор, а затем ссылаются на него из позиционируемого элемента. Это быстро становится громоздким. В CSS есть более простой механизм: anchor-scope.
Свойство anchor-scope задает, какие имена якорей сопоставляются только внутри элемента и его потомков. Оно принимает список из одного или нескольких имен якорей либо ключевое слово all, чтобы ограничить область всех объявленных имен.
anchor-scope лучше всего ставить на предка, который содержит и позиционируемый элемент, и якорь, но не содержит других якорей с тем же именем. Часто это корневой элемент переиспользуемого компонента.
В следующем примере видно, что меняет anchor-scope, если его применить к повторяющимся элементам с одним и тем же anchor-name. Здесь все элементы <img> и баннеры изображений ссылаются на имя якоря --image. Когда anchor-scope задан у элементов <li>, объявление position-anchor: --image совпадет только с тем <img>, который находится в том же <li>, что и баннер. Иначе совпадением станет последний отрисованный <img>.
Алгоритм поиска якоря сложный, но главное вот что: якорь должен быть полностью сверстан до позиционируемого элемента. Поэтому нельзя привязаться к элементу в более высоком слое (например, внутри popover) и к абсолютно позиционированным элементам, которые идут после позиционируемого элемента. Если якорей с одним именем несколько, победит последний в документе среди тех, что уже сверстаны.
Позиционирование¶
Элемент привязан к якорю, и теперь его нужно разместить. Якорное позиционирование дает два способа: свойство position-area и функцию anchor().
position-area¶
Свойство position-area размещает элемент вокруг якоря с помощью одного или двух ключевых слов. Этого хватает для многих обычных случаев, и с него часто удобно начинать.
Как работает position-area¶
position-area создает для позиционируемого элемента новый содержащий блок в области, которую образуют края якоря и исходный содержащий блок этого элемента.
Поскольку position-area создает новый содержащий блок для позиционируемого элемента, проценты в padding или margin считаются от этого нового содержащего блока.
Ключевых слов у position-area много, но их можно разбить на несколько групп, чтобы в них было проще разобраться. Anchor-tool.com хорошо подходит, чтобы исследовать синтаксис.
Физические ключевые слова¶
Можно использовать физические ключевые слова top, left, bottom, right и center. Например, position-area: top right поместит позиционируемый элемент выше и правее якоря. У этих слов есть эквиваленты по физическим осям: y-start, x-start, y-end и x-end.
Логические ключевые слова¶
Можно использовать и логические ключевые слова: block-start, block-end, inline-start и inline-end. Например, position-area: block-end inline-start в языках вроде английского поместит позиционируемый элемент ниже и левее якоря. В режиме записи документа это значит: после якоря по блочной оси и перед якорем по строчной оси. center можно сочетать с логическим ключевым словом.
Если вы указываете логические ключевые слова, ось можно опустить: сначала идет блочная ось, затем строчная. position-area: start end совпадает с position-area: block-start inline-end и даже с position-area: inline-end block-start.
Логические свойства размещают элемент по режиму записи его содержащего блока. Если нужен собственный режим записи позиционируемого элемента, добавьте модификатор self, например self-block-start вместо block-start.
Охват нескольких ячеек сетки¶
Пока что эти варианты позволяют поместить позиционируемый элемент только в одну ячейку сетки. Префикс span у физического или логического ключевого слова добавляет соседнюю центральную ячейку. При position-area: span-top right элемент окажется справа от якоря и займет область от нижнего края якоря до верхнего края исходного содержащего блока позиционируемого элемента.
Для раскрывающегося меню часто берут position-area: block-end span-inline-end.
Ключевое слово span-all охватывает 3 ряда или 3 колонки.
Одно ключевое слово¶
Если задано только одно ключевое слово, вторая ось вычисляется автоматически. В целом это работает так, как вы ожидаете, но полезно понимать сам механизм.
Если ключевое слово однозначно указывает свою ось, вторая ось вычисляется как span-all. Поэтому position-area: bottom равносильно position-area: bottom span-all: позиционируемый элемент окажется под якорем, и ему будет доступна вся ширина содержащего блока.
Если же ключевое слово не указывает ось однозначно, оно повторяется. position-area: start равносильно start start и в языках с письмом слева направо помещает элемент в левый верхний угол относительно якоря.
Функция anchor()¶
Для более сложных случаев position-area может не хватить. Функция anchor() задает отдельные inset-свойства по положению другого элемента. Она вычисляется в длину CSS, поэтому ее можно использовать в вычислениях и вместе с другими функциями CSS. Кроме того, разные стороны можно привязать к разным якорям.
Функция anchor() принимает имя якоря и сторону якоря. Если у элемента есть якорь по умолчанию, заданный через position-anchor или неявно, например у popover, имя якоря можно опустить.
1 2 3 4 5 6 | |
Запасные значения¶
Если для функции anchor() якорь не найден, все объявление становится недействительным. Так бывает, когда якорь отрисовывается после позиционируемого элемента или когда нет элемента с подходящим anchor-name. Чтобы это обработать, задайте запасную длину или процент.
1 2 3 | |
В предыдущем примере левое значение позиционируемого элемента привязано к --focused-anchor, но это anchor-name существует только когда первая кнопка в состоянии hover или focus. Поскольку anchor() вычисляется в длину, в качестве запасного значения можно использовать другой якорь. Если запасное значение не задать, позиционируемый элемент не будет спозиционирован.
Ключевые слова стороны якоря¶
Значение стороны якоря выбирает, к какому краю якоря прижиматься. Как и у position-area, здесь несколько видов синтаксиса.
| Тип | Значения | Описание |
|---|---|---|
| Физические | top, left, bottom, right | Физические ключевые слова соответствуют конкретной стороне якоря, но их можно использовать только на той же оси, что и inset позиционируемого элемента, который вы задаете. Например, top: anchor(bottom) ставит верх элемента на низ якоря, а left: anchor(top) не сработает. |
| Сторона | inside, outside | Ключевое слово inside соответствует той же стороне, что и inset-свойство, а outside — противоположной стороне на той же оси. Например, inset-block-start: anchor(inside) указывает на сторону block-start якоря, а inset-inline-end: (outside) — на сторону inline-start якоря. |
| Логические | start, end, self-start, self-end | Логические ключевые слова указывают на стороны якоря по режиму записи позиционируемого элемента (self-start и self-end) или по режиму записи его содержащего блока (start и end). |
| Проценты | 0% - 100% | Процент размещает позиционируемый элемент вдоль оси от начала якоря к его концу на заданной оси. 0% — это сторона start якоря, 100% — сторона end. center равносильно 50%. Если процент задан для inset конечной стороны, например bottom, направление не переворачивается: 0% по-прежнему сторона start якоря. |
В этом примере видно, что процент всегда идет от начала к концу на заданной оси:
Использование anchor()¶
Поскольку anchor() — это длина, функция очень гибкая. Значение можно обрабатывать функциями CSS вроде max() и calc().
Ограничение одно: функции anchor() допустимы только в inset-свойствах.
В предыдущем примере за открытой панелью details рисуется фон. Он плавно анимируется, когда открывается другая панель, и растягивается так, чтобы захватить панель под курсором. Для этого min() выбирает меньшую длину из двух якорей.
1 2 3 4 5 6 7 8 9 10 11 | |
В том же примере calc() добавляет строчный промежуток вокруг открытой панели.
Использование размера якоря¶
Функция anchor-size() позволяет взять размеры якоря для размера, позиции или внешнего отступа позиционируемого элемента.
anchor-size() принимает имя якоря или использует якорь по умолчанию. По умолчанию берется размер якоря по той оси, на которой функция используется, поэтому width: anchor-size() вернет ширину якоря. Другую ось можно запросить явно: физическими ключевыми словами width и height или логическими block, inline, self-block и self-inline.
Обработка переполнения¶
Вы собрали компонент раскрывающегося меню и с помощью якорного позиционирования поставили меню туда, куда нужно. Затем меню переезжает на другую сторону экрана или используется как меню пользователя с очень длинным именем. Меню внезапно оказывается за пределами экрана. Что дальше?
У якорного позиционирования есть встроенная система, которая позволяет быстро собрать надежный набор запасных положений, если позиционируемый элемент выходит за свой содержащий блок.
Запасные варианты¶
Свойство position-try-fallbacks принимает список запасных вариантов. Когда позиция по умолчанию переполняется, варианты пробуются по порядку, пока не найдется положение без переполнения.
В качестве запасного варианта можно использовать любое значение position-area. В этом примере в режимах записи слева направо, как в английском, позиционируемый элемент сначала пытается встать снизу от якоря, охватывая центральную и правую колонки. Если это переполняется, он пытается встать снизу от якоря, охватывая левую и центральную колонки. Если переполняется и это, позиция возвращается к варианту по умолчанию, даже если он тоже переполняется.
1 2 3 4 | |
Есть и несколько ключевых слов flip-, которые закрывают типичные запасные случаи. flip-block и flip-inline пробуют отразить элемент по блочной и строчной осям. Их можно объединить как flip-block flip-inline, чтобы отразить по обеим осям. Значение flip-start отражает позиционируемый элемент по диагонали от начального угла якоря к конечному.
Свой запасной вариант можно описать через @position-try. Так задают внешние отступы, выравнивание и даже смену якоря.
1 2 3 4 5 6 7 8 | |
flip-block и flip-inline можно добавить к запасным вариантам @position-try, чтобы получить вариант с отражением.
1 2 3 | |
В предыдущем примере браузер идет по таким шагам и останавливается, как только находит положение без переполнения.
- Элемент ставится с
position-area: end, внизу справа от якоря. - Если это переполняется, элемент ставится пользовательским запасным вариантом
--bottom-span-right:position-area: bottom span-rightи дополнительный внешний отступ снизу. - Если и это переполняется, элемент ставится как
flip-inline --bottom-span-right: пользовательский запасной вариант вместе сflip-inline, по сутиposition-area: bottom span-left. - Если и это переполняется, элемент ставится пользовательским запасным вариантом
--use-alternate, который помещает его под совершенно другой якорь. - Если и это переполняется, элемент возвращается к исходному размещению с
position-area: end, хотя уже известно, что оно переполняется.
Порядок запасных вариантов¶
По умолчанию, когда исходная позиция переполняется, браузер перебирает варианты из position-try-fallbacks, пока не найдет положение без переполнения. Это поведение можно переопределить свойством position-try-order: проверить каждый запасной вариант и взять тот, у которого больше всего места на заданной оси.
Ось задается логическими ключевыми словами most-block-size и most-inline-size либо физическими most-height и most-width.
position-try-order и position-try-fallbacks можно объединить в сокращенное свойство position-try: порядок указывается первым.
Прокрутка¶
При прокрутке пользователь ожидает, что страница будет двигаться плавно. Поэтому у браузеров есть ограничения на то, как якорное позиционирование может работать во время прокрутки.
Позиционируемый элемент можно привязать к якорям в разных контейнерах прокрутки, но двигаться он будет только в ответ на прокрутку одного из них. Это якорь по умолчанию: неявный якорь popover либо значение position-anchor.
Позиционируемый элемент остается видимым, даже когда якорь уходит за пределы видимой области. Чтобы скрывать позиционируемый элемент вместе с якорем, задайте position-visibility: anchors-visible. Это срабатывает не только когда якорь прокручен за край, но и когда он скрыт иначе, например через visibility: hidden.
Источник — Anchor positioning