Большой визуальный гайд по CSS Anchor Positioning: от базовой привязки элементов до создания прочных соединителей и цепочек якорей.

CSS Anchor Positioning позволяет размещать абсолютно позиционированный элемент относительно других элементов — якорей; в Chrome эта возможность появилась в мае 2024 года, в версии 125.

У обычного position: absolute отступы top, right, bottom и left отсчитываются от границ содержащего блока (containing block), который обязательно определяется по цепочке родительских элементов. Anchor Positioning позволяет привязаться к элементу вне этой цепочки. Именно так устроены наши примеры: у якоря .anchor и зависимого элемента .target разные родители.

Разметка примеров выглядит так:

<main class="stage">
  <div class="work-field">
    <div class="anchor"></div>
  </div>
  <div class="parts-field">
    <div class="target"></div>
  </div>
</main>

Мы будем связывать элементы с классами .anchor и .target. Обратите внимание: они находятся в разных родительских элементах.

1. Позиционирование через position-area

Вот исходное состояние: элементы .anchor и .target, которые мы будем связывать, пока находятся каждый внутри своего контейнера — как и ожидается.

.anchor {
  /* Имя пока не задано. */
}

.target {
  position: absolute;
}
Исходное состояние: элемент в отдельной панели, якорь без имени
Исходное состояние: элемент в отдельной панели, якорь без имени

Чтобы связать элементы через position-area, якорю задаём имя свойством anchor-name, а заякорённому элементу — это же имя в position-anchor и область размещения в position-area.

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  position-area: right;
}

Элемент переместился к якорю и расположился справа от него.

Элемент размещён справа от якоря a1
Элемент размещён справа от якоря a1

Если поменять расположение родительских контейнеров, привязка сохранится: элемент по-прежнему будет справа от якоря.

Привязка сохраняется при расположении исходной панели слева и снизу
Привязка сохраняется при расположении исходной панели слева и снизу

Браузер сам пересчитывает положение элемента относительно якоря: сначала рассчитывает новые границы якоря, затем размещает элемент в выбранной области справа от него.

position-anchor — выбор якоря

Поэкспериментируем с выбором якоря и посмотрим, как меняется положение элемента. В примере три якоря с именами --a1, --a2 и --a3. Пока у .target задано position-anchor: none, он ни к одному из них не привязан и находится в своём контейнере.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }
.anchor--three { anchor-name: --a3; }

.target {
  position: absolute;
  position-anchor: none;
  position-area: top;
}
Три именованных якоря и элемент без выбранного якоря
Три именованных якоря и элемент без выбранного якоря

Теперь по очереди зададим в position-anchor имена --a1, --a2 и --a3. Элемент окажется над выбранным якорем: размещение сверху задано свойством position-area: top.

Выбор a1, a2 и a3 через position-anchor
Выбор a1, a2 и a3 через position-anchor

position-area — выбор области размещения

Теперь поэкспериментируем с расположением элемента относительно одного якоря. В исходном состоянии .target привязан к --a1 и находится над ним — задано position-area: top.

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  position-area: top;
}

По очереди зададим в position-area значения top, right, bottom и left. Элемент расположится сверху, справа, снизу и слева от якоря соответственно.

Размещение сверху, справа, снизу и слева от якоря
Размещение сверху, справа, снизу и слева от якоря

Границы якоря и содержащего блока образуют сетку 3×3, в центре которой находится якорь. Значение top выбирает всю верхнюю полосу этой сетки, right — всю правую; bottom и left работают аналогично.

Внутри выбранной полосы действует стандартное выравнивание: элемент прижимается к якорю по одной оси и центрируется относительно него по другой. Поэтому на картинках он оказывается у середины соответствующей стороны якоря.

2. Позиционирование через anchor()

position-area задаёт общую область размещения относительно якоря, а anchor() позволяет точнее управлять положением элемента, связывая отдельные стороны. Сторону заякорённого элемента выбираем свойством top, right, bottom или left, а сторону якоря указываем внутри функции. Например, left: anchor(right) совмещает левую сторону элемента с правой стороной якоря.

Начнём с исходного состояния: элементы .anchor и .target находятся каждый в своём контейнере. Якорь уже выбран через position-anchor, но стороны ещё не связаны, поэтому .target остаётся на прежнем месте.

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  left: auto;
  top: auto;
}
Исходное положение элемента до задания координат
Исходное положение элемента до задания координат

Привяжем левую сторону элемента к правой стороне якоря. Для этого зададим left: anchor(right), оставив top: auto.

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  left: anchor(right);
  top: auto;
}

Элемент сместился по горизонтали: его левая сторона теперь находится на пунктирной линии, продолжающей правую границу якоря. Положение по вертикали не изменилось.

Левая сторона элемента на вертикали правой границы якоря
Левая сторона элемента на вертикали правой границы якоря

Теперь привяжем верхнюю сторону элемента к верхней стороне якоря с помощью top: anchor(top). Привязку через left сохраним.

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  left: anchor(right);
  top: anchor(top);
}

Элемент сместился по вертикали и оказался справа от якоря. Их верхние стороны совпали по высоте, а верхний левый угол элемента находится на пересечении пунктирных линий.

Левая и верхняя стороны элемента привязаны к правой и верхней границам якоря
Левая и верхняя стороны элемента привязаны к правой и верхней границам якоря

Поэкспериментируем со стороной якоря, указанной внутри anchor(). В верхнем ряду меняем аргумент функции у left: элемент перемещается по горизонтали. В нижнем — у top: элемент перемещается по вертикали. Вторую привязку в каждом случае сохраняем.

Варианты аргументов left и top с неизменной второй координатой
Варианты аргументов left и top с неизменной второй координатой

Обратите внимание на center: в left: anchor(center) через середину якоря проходит левая сторона элемента, а в top: anchor(center) — верхняя. Сам элемент при этом не центрируется. Пунктир на картинках продолжает отмечать правую и верхнюю границы якоря — по ним удобно сравнивать смещение.

right и bottom — привязка правой и нижней сторон

Попробуем привязать правую и нижнюю стороны элемента через right и bottom. Начнём с исходного состояния: якорь выбран, но у обоих свойств задано auto, поэтому элемент пока находится в своём контейнере.

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  right: auto;
  bottom: auto;
}
Исходное состояние перед привязкой через right и bottom
Исходное состояние перед привязкой через right и bottom

Чтобы связать правую сторону элемента с левой стороной якоря, зададим right: anchor(left), а для совмещения нижних сторон — bottom: anchor(bottom).

.anchor {
  anchor-name: --a1;
}

.target {
  position: absolute;
  position-anchor: --a1;
  right: anchor(left);
  bottom: anchor(bottom);
}

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

Правая сторона элемента на левой границе якоря, нижние стороны совпадают
Правая сторона элемента на левой границе якоря, нижние стороны совпадают

Теперь поменяем аргументы anchor(): в верхнем ряду у right зададим center и right, а в нижнем у bottom — center и top. Элемент переместится так, чтобы его правая или нижняя сторона совпала с выбранной координатой якоря. Вторая привязка сохранится.

Варианты аргументов right и bottom
Варианты аргументов right и bottom

anchor() — явный выбор якоря

Имя якоря можно указать прямо внутри anchor(). Посмотрим, как это работает на примере с тремя якорями. В исходном состоянии position-anchor закомментирован, стороны ещё не связаны, и .target находится в своём контейнере.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }
.anchor--three { anchor-name: --a3; }

.target {
  position: absolute;
  /* position-anchor: --a1; */
  left: auto;
  top: auto;
}
Три якоря и элемент без привязки, position-anchor закомментирован
Три якоря и элемент без привязки, position-anchor закомментирован

Чтобы привязать элемент к --a1, укажем это имя в обеих функциях: left: anchor(--a1 right) свяжет левую сторону элемента с правой стороной якоря, а top: anchor(--a1 top) — верхние стороны.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }
.anchor--three { anchor-name: --a3; }

.target {
  position: absolute;
  /* position-anchor: --a1; */
  left: anchor(--a1 right);
  top: anchor(--a1 top);
}

Элемент расположился справа от --a1, их верхние стороны совпали по высоте. Привязка работает с закомментированным position-anchor: имя якоря задано в самих функциях.

Имя a1 явно задано в обеих функциях anchor
Имя a1 явно задано в обеих функциях anchor

Теперь по очереди укажем в обеих функциях имена --a2 и --a3. Элемент переместится к выбранному якорю, сохраняя расположение справа и выравнивание по верхней стороне.

Явный выбор a2 или a3 для двух координат
Явный выбор a2 или a3 для двух координат

Несколько элементов у одного якоря

К одному якорю можно привязать несколько элементов. В этом примере у якоря уже задано имя --a1, а элементы .target--one и .target--two пока находятся в отдельном контейнере без привязки.

.anchor--one {
  anchor-name: --a1;
}

.target--one,
.target--two {
  position: absolute;
}
Два элемента до привязки к одному якорю
Два элемента до привязки к одному якорю

В функциях anchor() обоих элементов укажем имя --a1, но выберем разные стороны якоря. Для первого зададим left: anchor(--a1 right) и top: anchor(--a1 top), для второго — left: anchor(--a1 left) и top: anchor(--a1 bottom).

.anchor--one {
  anchor-name: --a1;
}

.target--one {
  position: absolute;
  left: anchor(--a1 right);
  top: anchor(--a1 top);
}

.target--two {
  position: absolute;
  left: anchor(--a1 left);
  top: anchor(--a1 bottom);
}

Первый элемент расположился справа от якоря с выравниванием по верхней стороне, второй — снизу с выравниванием по левой. Пунктирные линии показывают, с какими границами якоря совпали стороны каждого элемента.

Два элемента привязаны к разным сторонам одного якоря
Два элемента привязаны к разным сторонам одного якоря

Разные якоря для сторон одного элемента

Стороны одного элемента можно привязать к разным якорям, указав разные имена в anchor(). В исходном состоянии у нас два якоря — --a1 и --a2, а элемент с отмеченными углами пока находится в своём контейнере.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.target {
  position: absolute;
  right: auto;
  bottom: auto;
}
Два якоря и элемент с отмеченными углами до привязки
Два якоря и элемент с отмеченными углами до привязки

Привяжем правую сторону элемента к левой стороне --a2 через right: anchor(--a2 left), а нижнюю — к верхней стороне --a1 через bottom: anchor(--a1 top).

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.target {
  position: absolute;
  right: anchor(--a2 left);
  bottom: anchor(--a1 top);
}

Элемент переместился: его правая сторона находится на вертикальном пунктире от --a2, а нижняя — на горизонтальном пунктире от --a1.

Правая и нижняя стороны элемента получают координаты разных якорей
Правая и нижняя стороны элемента получают координаты разных якорей

Положение элемента теперь зависит от двух якорей: --a2 задаёт его положение по горизонтали, а --a1 — по вертикали.

Чувствуете? Кажется, начинается…

Мы только что привязали разные стороны элемента к разным якорям. А если пойти дальше и привязать к ним два противоположных угла? Один угол — к первому якорю, другой — ко второму. Сам элемент тогда растянется между ними…

Подождите. Это же С-В-Я-З-Ь между элементами!

Та самая, которая будет тянуться от одного элемента к другому и подстраиваться под их положение. Соединители для схем, стрелки между карточками, линии между узлами. И всё это на CSS, без JavaScript для расчёта координат.

3. Суперприём: прочная связь между элементами

Начнём с двух якорей --a1 и --a2 и отдельного блока .route--one, который превратим в связь между ними. Пока его стороны не привязаны, а небольшой прямоугольник виден благодаря заранее заданным минимальным размерам. Ширину и высоту оставим автоматическими, чтобы блок мог растягиваться между якорями.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  position: absolute;
  left: auto;
  right: auto;
  top: auto;
  bottom: auto;
}
Блок связи до привязки сторон
Блок связи до привязки сторон

Сначала растянем блок по горизонтали: привяжем его левую сторону к правой стороне --a1, а правую — к левой стороне --a2.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  position: absolute;
  left: anchor(--a1 right);
  right: anchor(--a2 left);
  top: auto;
  bottom: auto;
}

Блок растянулся между двумя вертикальными пунктирными линиями: они продолжают выбранные стороны якорей. Высота и положение по вертикали пока не изменились.

Связь растянута между вертикальными границами двух якорей
Связь растянута между вертикальными границами двух якорей

Теперь зададим вертикальные привязки: верхнюю сторону блока совместим с серединой --a1 по высоте, а нижнюю — с серединой --a2.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  position: absolute;
  left: anchor(--a1 right);
  right: anchor(--a2 left);
  top: anchor(--a1 center);
  bottom: anchor(--a2 center);
}

Блок растянулся и по вертикали. Его верхний левый угол теперь находится у середины правой стороны первого якоря, а нижний правый — у середины левой стороны второго. Получили то, что задумали: два противоположных угла привязаны к разным якорям. Растяжение между заданными сторонами — стандартное поведение абсолютно позиционированного блока с автоматическими шириной и высотой.

Четыре направляющие определяют прямоугольник связи
Четыре направляющие определяют прямоугольник связи

Оформляем связь

Прямоугольник между якорями уже есть. Теперь нарисуем внутри него линию с помощью фонового изображения. Для оформления у блока подготовлен отдельный класс .route--bg; пока фона нет, видна только светлая подложка.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  position: absolute;
  left: anchor(--a1 right);
  right: anchor(--a2 left);
  top: anchor(--a1 center);
  bottom: anchor(--a2 center);
}

.route--bg {
  background-image: none;
  background-repeat: no-repeat;
  background-position: center;
}
Область связи без фонового рисунка
Область связи без фонового рисунка

Добавим в фон SVG размером 100×100 с линией от верхнего левого угла к нижнему правому.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  position: absolute;
  left: anchor(--a1 right);
  right: anchor(--a2 left);
  top: anchor(--a1 center);
  bottom: anchor(--a2 center);
}

.route--bg {
  background-image: url("images/route.svg");
  background-repeat: no-repeat;
  background-position: center;
}

Линия появилась в центре блока, но до якорей пока не дотягивается: изображение сохранило свои размеры 100×100.

SVG размером 100 на 100 в центре области связи
SVG размером 100 на 100 в центре области связи

Растянем фон на весь блок с помощью background-size: 100% 100%.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  position: absolute;
  left: anchor(--a1 right);
  right: anchor(--a2 left);
  top: anchor(--a1 center);
  bottom: anchor(--a2 center);
}

.route--bg {
  background-image: url("images/route.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
}

Теперь линия соединяет якоря: её начало и конец совпали с привязанными углами блока.

Фоновая линия заполняет связь и достигает обоих якорей
Фоновая линия заполняет связь и достигает обоих якорей

За растяжение фона отвечает обычный background-size. В самом SVG задано preserveAspectRatio="none", чтобы линия растягивалась вместе с блоком по обеим осям.

Если передвинуть один из якорей, блок изменит положение и размеры, а фоновая линия растянется вместе с ним. Посмотрим на нескольких вариантах:

Геометрия и фон связи меняются при перемещении якорей
Геометрия и фон связи меняются при перемещении якорей

Линия во всех вариантах остаётся соединённой с якорями. Браузер сам пересчитывает границы блока, а фон заполняет его целиком.

4. Суперприём: цепочка якорей

Наш маршрут уже привязан к двум якорям. А что, если теперь привязать к самому маршруту кораблик и разместить его посередине? Получится цепочка: якоря → маршрут → кораблик. Давайте соберём её и посмотрим, будет ли кораблик следовать за маршрутом, когда мы передвинем якоря.

В исходном состоянии маршрут соединяет два якоря, а кораблик находится в своём контейнере. У маршрута пока нет имени, к которому можно привязать кораблик.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  anchor-name: none;
}

.ship {
  position: absolute;
  position-anchor: none;
  position-area: none;
}
Связь без имени и отдельный элемент без привязки
Связь без имени и отдельный элемент без привязки

У нас есть два способа привязки: через position-area и через anchor(). Здесь нужно разместить кораблик по центру блока маршрута, поэтому выберем position-area: для этой задачи достаточно указать центральную область. Привязка отдельных сторон через anchor() нам сейчас не нужна.

Вспомним три свойства: маршруту зададим имя через anchor-name: --r1, а кораблику — position-anchor: --r1 для выбора маршрута и position-area: center для размещения по центру.

.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }

.route--one {
  anchor-name: --r1;
}

.ship {
  position: absolute;
  position-anchor: --r1;
  position-area: center;
}

Кораблик переместился в центр маршрута. Цепочка сработала: маршрут привязан к двум якорям, а кораблик — к маршруту.

Светлая подложка показывает границы блока, по которому центрируется кораблик. В нашем рисунке линия проходит через центр этого блока, поэтому кораблик оказался прямо на ней.

Элемент привязан к центру связи r1
Элемент привязан к центру связи r1

Поэкспериментируем: передвинем исходные якоря и посмотрим, что произойдёт с корабликом. Маршрут изменит положение и размеры, а кораблик останется в его центре.

Элемент следует за центром связи при перемещениях исходных якорей
Элемент следует за центром связи при перемещениях исходных якорей

Примечание. В цепочке каждый якорь должен быть рассчитан раньше элемента, который к нему привязан. Поэтому замкнуть цепочку, привязав элементы друг к другу, не получится.

В нашем примере маршрут и кораблик абсолютно позиционированы относительно общего содержащего блока .stage, хотя находятся у разных родителей. Для такой пары важен порядок в HTML: блок маршрута должен идти раньше кораблика. Это следует из условий допустимости якоря в спецификации.

Добавляем второй маршрут

А как добавить ещё один якорь и ещё один маршрут? Добавим два элемента в конец .work-field. Так оба маршрута будут находиться в разметке раньше кораблика:

<div class="work-field">
  <!-- Первые два якоря и первый маршрут -->

  <div class="anchor anchor--three"></div>
  <div class="route route--bg route--two"></div>
</div>
<div class="parts-field">
  <div class="parts-field-cell">
    <div class="ship ship--one"></div>
  </div>
</div>

Новый маршрут пока не привязан, а кораблик остаётся на первом.

Второй блок связи ещё не привязан, элемент остаётся на первой связи
Второй блок связи ещё не привязан, элемент остаётся на первой связи

Повторим знакомые шаги: дадим третьему якорю имя --a3 и привяжем стороны второго маршрута к --a2 и --a3:

.anchor--three { anchor-name: --a3; }

.route--two {
  position: absolute;
  left: anchor(--a2 right);
  right: anchor(--a3 left);
  top: anchor(--a2 center);
  bottom: anchor(--a3 center);
}

Второй маршрут соединил второй и третий якоря.

Две связи между тремя якорями, элемент на первой связи
Две связи между тремя якорями, элемент на первой связи

Теперь переместим на него кораблик. Дадим маршруту имя --r2 и укажем его в position-anchor у кораблика. Заданное ранее position-area: center сохранится:

.route--two {
  anchor-name: --r2;
}

.ship {
  position-anchor: --r2;
}

Кораблик переместился в центр второго маршрута.

Элемент переключён на вторую связь
Элемент переключён на вторую связь

И всё это — на CSS. И всё это — уже сейчас!

Теперь понятно, почему все так ждали анкоров? В мае 2024 года они появились в Chrome, а с января 2026-го поддерживаются актуальными версиями всех основных браузеров. Если ваш проект ориентирован на эти версии, такую красоту уже можно делать в продакшне. Уберём вспомогательные подложки и просто полюбуемся.

Готовая композиция с двумя связями и зависимым элементом
Готовая композиция с двумя связями и зависимым элементом

Двигаем якоря, переключаем кораблик между маршрутами — и все связи сохраняются. Линии тянутся за якорями, кораблик держится в центре выбранного маршрута. Без единой строчки JavaScript для пересчёта координат.

Четыре состояния с перемещением якорей и сменой выбранной связи
Четыре состояния с перемещением якорей и сменой выбранной связи

А что вы уже делаете на анкорах? Показывайте свои примеры в комментариях — интересно посмотреть, где ещё пригодились такие связи.

Подписывайтесь на мой телеграм‑канал «CSS Боль», если хотите ещё больше интересного современного CSS.

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


  1. impwx
    09.10.2026 15:51

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