Anchor positioning

CSS Anchor Positioning позволяет позиционировать элемент относительно другого элемента (якоря) без JavaScript. anchor-name задаёт якорь, position-anchor привязывает к нему, anchor вычисляет координаты.

Зачем нужно

Тултипы, дропдауны, поповеры традиционно требуют JavaScript для расчёта позиции. Anchor positioning решает это на уровне CSS: элемент автоматически позиционируется рядом с якорем и подстраивается при скролле, ресайзе и переполнении.

Где используется

  • Тултипы и подсказки
  • Дропдауны и меню
  • Поповеры (popover API + anchor positioning)
  • Комментарии/аннотации привязанные к тексту
  • Контекстные панели

Предпосылки

Базовый синтаксис

1. Определить якорь

.trigger {
  anchor-name: --my-anchor;
}

2. Привязать элемент к якорю

.tooltip {
  position: fixed;           /* или absolute */
  position-anchor: --my-anchor;

  /* Позиция через position-area */
  position-area: top;        /* Над якорем */
}

position-area — простое позиционирование

Размещает элемент в одной из 9 зон вокруг якоря:

┌──────────┬──────────┬─���────────┐
│ top left │   top    │ top right│
├───���──────┼──────────┼──────────┤
│   left   │ (anchor) │  right   │
├──────────┼──────────┼──────────┤
│bottom    │  bottom  │ bottom   │
│left      │          │ right    │
└──────────┴──────────┴──────────┘
/* Тултип сверху */
.tooltip-top {
  position: fixed;
  position-anchor: --btn;
  position-area: top;
}

/* Меню справа */
.menu-right {
  position: fixed;
  position-anchor: --btn;
  position-area: right;
}

/* Дропдаун снизу по левому краю */
.dropdown {
  position: fixed;
  position-anchor: --trigger;
  position-area: bottom left;
}

anchor — точные координаты

Для более точного контроля:

.tooltip {
  position: fixed;
  position-anchor: --my-anchor;

  /* Расположить снизу якоря, выровнять по левому краю */
  top: anchor(bottom);
  left: anchor(left);

  /* Зазор */
  margin-top: 8px;
}

/* По центру над якорем */
.tooltip-centered {
  position: fixed;
  position-anchor: --my-anchor;
  bottom: anchor(top);
  left: anchor(center);
  translate: -50% 0;
  margin-bottom: 8px;
}

Значения anchor

.element {
  /* Вертикальные */
  top: anchor(top);       /* Верх якоря */
  top: anchor(bottom);    /* Низ якоря */
  top: anchor(center);    /* Центр якоря */

  /* Горизонтальные */
  left: anchor(left);     /* Левый край */
  left: anchor(right);    /* Правый край */
  left: anchor(center);   /* Центр */

  /* С процентами */
  left: anchor(50%);      /* 50% ширины якоря */
}

Полный пример: тултип

<button class="btn" popovertarget="tip">Наведи</button>
<div id="tip" class="tooltip" popover>Подсказка</div>
.btn {
  anchor-name: --tooltip-anchor;
}

.tooltip {
  position: fixed;
  position-anchor: --tooltip-anchor;
  position-area: top;
  margin-bottom: 8px;

  background: #333;
  color: white;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 0.875rem;
  white-space: nowrap;

  /* Стрелка */
  &::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    translate: -50% 0;
    border: 6px solid transparent;
    border-top-color: #333;
  }
}

position-try — fallback при переполнении

Если элемент не помещается, попробовать другие позиции:

.tooltip {
  position: fixed;
  position-anchor: --btn;
  position-area: top; /* Предпочитаемая позиция */

  /* Если не помещается сверху, попробовать снизу, потом слева */
  position-try-fallbacks:
    --try-bottom,
    --try-left;
}

@position-try --try-bottom {
  position-area: bottom;
  margin-top: 8px;
  margin-bottom: 0;
}

@position-try --try-left {
  position-area: left;
  margin-right: 8px;
  margin-bottom: 0;
}

Встроенные fallback-ы

.tooltip {
  position-area: top;
  position-try-fallbacks: flip-block; /* Попробовать bottom */
}

.menu {
  position-area: bottom right;
  position-try-fallbacks: flip-inline; /* Попробовать bottom left */
}

.popover {
  position-area: top;
  position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}

Дропдаун-меню

<button class="dropdown-trigger">Меню</button>
<nav class="dropdown-menu">
  <a href="#">Профиль</a>
  <a href="#">Настройки</a>
  <a href="#">Выйти</a>
</nav>
.dropdown-trigger {
  anchor-name: --dropdown;
}

.dropdown-menu {
  position: fixed;
  position-anchor: --dropdown;
  position-area: bottom span-right;
  margin-top: 4px;

  position-try-fallbacks: flip-block;

  background: white;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  padding: 4px;
  min-width: anchor-size(width);

  display: none;

  .dropdown-trigger:focus + & {
    display: block;
  }
}

.dropdown-menu a {
  display: block;
  padding: 8px 16px;
  text-decoration: none;
  color: #333;
  border-radius: 4px;

  &:hover {
    background: #f5f5f5;
  }
}

anchor-size — размер якоря

.dropdown-menu {
  /* Минимальная ширина = ширина якоря */
  min-width: anchor-size(width);

  /* Максимальная высота = высота от якоря до низа viewport */
  max-height: calc(100vh - anchor(bottom) - 16px);
}

Частые ошибки

  1. Забыли position: fixed/absolute — anchor positioning требует позиционированного элемента
  2. Нет position-anchor — элемент не привязан ни к какому якорю
  3. Якорь не найденanchor-name должен быть уникальным на странице
  4. Не добавлены fallback-ы — тултип может вылезти за экран

Практика

  • Создать тултип с position-area: top и стрелкой
  • Добавить position-try-fallbacks: flip-block для автоматического переворота
  • Реализовать дропдаун-меню с anchor-size(width)
  • Попробовать anchor для точного позиционирования
  • Протестировать fallback при скролле

Связанные темы

Ресурсы