Кастомизация overlay

В библиотеке Radix UI overlay — это элемент интерфейса, который используется для создания модальных окон, тултипов, дропдаунов и других компонентов, требующих затемнения или визуального отделения контента. Overlay по умолчанию предоставляет базовую функциональность, но библиотека позволяет гибко кастомизировать его поведение и визуальное оформление.


Структура overlay

Overlay в Radix UI обычно состоит из двух ключевых компонентов:

  1. Root компонент – контейнер, который управляет состоянием видимости overlay.
  2. Primitive компонент Overlay – собственно визуальный слой, который рендерится поверх основного контента.

Пример базового использования:

import * as Dialog from '@radix-ui/react-dialog';

<Dialog.Root>
  <Dialog.Trigger>Открыть окно</Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay className="overlay" />
    <Dialog.Content className="content">
      Содержимое окна
    </Dialog.Content>
  </Dialog.Portal>
</Dialog.Root>

Здесь Dialog.Overlay создаёт затемнённый фон, а Dialog.Content — сам модальный блок.


Кастомизация стилей overlay

Overlay в Radix UI полностью управляется через классы CSS или стилизованные компоненты (например, styled-components или @stitches/react). Основные параметры, доступные для кастомизации:

  • Цвет фона и прозрачность Используется свойство background-color с RGBA или HSLA для плавного эффекта затемнения.

  • Анимации появления и скрытия Взаимодействие с overlay становится более «живым» через CSS-анимации или keyframes.

  • Позиционирование и размер Overlay может занимать весь экран, часть окна или ограниченную область, управляется через position: fixed/absolute и inset.

Пример с кастомными стилями и анимацией:

import { keyframes, styled } from '@stitches/react';

const fadeIn = keyframes({
  '0%': { opacity: 0 },
  '100%': { opacity: 1 },
});

const fadeOut = keyframes({
  '0%': { opacity: 1 },
  '100%': { opacity: 0 },
});

const CustomOverlay = styled(Dialog.Overlay, {
  backgroundColor: 'rgba(0, 0, 0, 0.5)',
  position: 'fixed',
  inset: 0,
  animation: `${fadeIn} 200ms ease-out`,
  '&[data-state="closed"]': {
    animation: `${fadeOut} 200ms ease-in`,
  },
});

Использование кастомного overlay:

<Dialog.Portal>
  <CustomOverlay />
  <Dialog.Content className="content">Содержимое окна</Dialog.Content>
</Dialog.Portal>

Управление поведением overlay

Radix UI предоставляет данные о состоянии overlay через атрибуты data-state и data-open. Это позволяет управлять логикой появления и скрытия через CSS или JS:

  • data-state="open" — overlay видим.
  • data-state="closed" — overlay скрыт.

Можно реагировать на события:

<Dialog.Overlay
  onCl ick={() => console.log('Overlay кликнут')}
  className="overlay"
/>

Такое управление позволяет реализовать закрытие окна по клику на overlay или запуск анимации при изменении состояния.


Интеграция с анимациями и переходами

Для плавного появления overlay используют CSS-транзишены или анимации keyframes. Radix UI совместим с большинством популярных библиотек анимаций, включая Framer Motion:

import { motion } from 'framer-motion';

const MotionOverlay = motion(Dialog.Overlay);

<Dialog.Portal>
  <MotionOverlay
    initial={{ opacity: 0 }}
    animate={{ opacity: 0.5 }}
    exit={{ opacity: 0 }}
    transition={{ duration: 0.2 }}
  />
  <Dialog.Content>Контент окна</Dialog.Content>
</Dialog.Portal>

Кастомные слои для вложенных overlay

Radix UI позволяет создавать несколько overlay с разными z-index и уровнями прозрачности. Для управления стеком используется:

  • Portal — рендеринг overlay в отдельном DOM-узле.
  • CSS z-index — определение порядка отображения overlay при множестве слоев.

Пример вложенного overlay:

<Dialog.Root>
  <Dialog.Trigger>Главное окно</Dialog.Trigger>
  <Dialog.Portal>
    <CustomOverlay style={{ zIndex: 10 }} />
    <Dialog.Content>Первый уровень</Dialog.Content>

    <Dialog.Root>
      <Dialog.Trigger>Вложенное окно</Dialog.Trigger>
      <Dialog.Portal>
        <CustomOverlay style={{ zIndex: 20 }} />
        <Dialog.Content>Второй уровень</Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  </Dialog.Portal>
</Dialog.Root>

Особенности производительности

  • Overlay, рендерящийся через Portal, не блокирует основной DOM, что улучшает производительность.
  • Для сложных анимаций рекомендуется использовать GPU-ускоренные свойства (transform, opacity) вместо top/left или width/height.
  • Минимизация re-render контента внутри overlay повышает отзывчивость интерфейса.

Варианты кастомизации на практике

  1. Полупрозрачный фон с размытой подложкой
.backdrop {
  backdrop-filter: blur(4px);
  background-color: rgba(0,0,0,0.3);
}
  1. Overlay с градиентом и мягкой анимацией
const GradientOverlay = styled(Dialog.Overlay, {
  background: 'linear-gradient(180deg, rgba(0,0,0,0.3), rgba(0,0,0,0.6))',
  animation: `${fadeIn} 300ms ease`,
});
  1. Кастомные события закрытия
<Dialog.Overlay onPointerD own={(e) => e.target === e.currentTarget && close()} />

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