Позиционирование

Radix UI предоставляет мощный набор низкоуровневых компонентов для построения интерфейсов на React с полной кастомизацией и высокой доступностью. Одним из ключевых аспектов работы с этими компонентами является управление позиционированием всплывающих элементов, таких как модальные окна, тултипы, выпадающие меню и поповеры. Для этого Radix UI использует модуль @radix-ui/react-popover и внутренние утилиты для корректной работы с позиционированием через Popper.js-подобную логику.

Основы позиционирования

Все элементы, требующие позиционирования, в Radix UI строятся вокруг двух ключевых компонентов:

  • Root / Provider — контейнер, управляющий состоянием открытости и контекста позиции.
  • Content — непосредственно позиционируемый элемент, например, тултип или поповер.

Ключевым свойством является side, определяющее сторону, на которой будет отображаться контент относительно триггера. Возможные значения:

side: 'top' | 'right' | 'bottom' | 'left'

Пример:

<Popover.Root>
  <Popover.Trigger>Открыть</Popover.Trigger>
  <Popover.Content side="bottom">
    Контент появляется снизу
  </Popover.Content>
</Popover.Root>

Настройка смещения

Для тонкой настройки позиции используется align и sideOffset:

  • align определяет выравнивание по оси, перпендикулярной side:
align: 'start' | 'center' | 'end'
  • sideOffset задаёт отступ от триггера в пикселях:
<Popover.Content side="right" align="start" sideOffset={8}>
  Выравнивание по левому краю триггера
</Popover.Content>

Контроль переполнения

Radix UI автоматически обрабатывает случаи, когда контент может выйти за пределы окна. Для этого используется свойство collisionPadding, которое задаёт минимальное расстояние до границ окна:

<Popover.Content collisionPadding={12}>
  Контент будет смещён, если достигнет края окна
</Popover.Content>

Также поддерживаются стратегии поведения при переполнении через avoidCollisions (по умолчанию true).

Анимация и трансформация

Позиционируемый элемент можно плавно анимировать с помощью CSS или через библиотеку Radix Motion, применяя трансформации к свойствам side и align. Например, для появления снизу можно задать:

[data-state='open'] {
  transform: translateY(0);
  opacity: 1;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
[data-state='closed'] {
  transform: translateY(-10px);
  opacity: 0;
}

Radix добавляет атрибут data-state автоматически для управления анимацией.

Использование с порталом

Для корректного позиционирования вне родительского контекста используется Portal:

<Popover.Portal>
  <Popover.Content side="top">
    Контент рендерится в body
  </Popover.Content>
</Popover.Portal>

Это важно для избежания проблем с overflow: hidden или z-index родителя.

Расширенные возможности

Radix UI позволяет динамически изменять позицию при изменении размеров окна или прокрутке. Для этого используются:

  • onOpenChange — обработка открытия/закрытия с возможностью вручную корректировать позицию.
  • align + side комбинации — создают адаптивное поведение контента.
  • collisionBoundary — возможность задать кастомные границы, отличные от окна браузера.

Пример адаптивного поповера:

<Popover.Content
  side="bottom"
  align="center"
  sideOffset={10}
  collisionPadding={8}
  avoidCollisions
>
  Динамически позиционируемый контент
</Popover.Content>

Практические рекомендации

  • Для модальных окон и поповеров всегда использовать Portal, чтобы избежать проблем с родительскими стилями.
  • Использовать sideOffset для создания визуального пространства между триггером и контентом.
  • Применять collisionPadding и avoidCollisions для корректной работы на малых экранах и при изменении размеров окна.
  • Комбинировать side и align для достижения точного позиционирования и согласованного UX.
  • Анимации следует привязывать к data-state, чтобы обеспечить синхронизацию с внутренней логикой Radix.

Эти принципы позволяют создавать интерфейсы с предсказуемым, гибким и доступным позиционированием, сохраняя полную контроль над внешним видом и поведением компонентов Radix UI.