Архитектура приложения

Принципы архитектуры интерфейса

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

Основная идея заключается в том, что библиотека предоставляет поведенческие и доступные primitives, а визуальное оформление и структура интерфейса полностью остаются в зоне ответственности приложения.

Ключевые архитектурные принципы:

1. Композиция вместо монолитных компонентов

Radix UI разбивает сложные интерфейсные элементы на набор независимых частей.

Пример структуры компонента Dialog:

Dialog.Root
Dialog.Trigger
Dialog.Portal
Dialog.Overlay
Dialog.Content
Dialog.Title
Dialog.Description
Dialog.Close

Каждый элемент отвечает только за одну функцию:

  • Root — управление состоянием
  • Trigger — элемент открытия
  • Portal — перенос в DOM-корень
  • Overlay — затемнение фона
  • Content — основной контейнер

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


Слоистая архитектура интерфейса

При разработке приложений с Radix UI чаще всего применяется многоуровневая структура интерфейса.

Типичная архитектура включает следующие уровни:

Application Layer
↓
Feature Components
↓
Design System
↓
Radix Primitives
↓
DOM

Application Layer

Верхний уровень приложения. Содержит:

  • страницы
  • роутинг
  • бизнес-логику
  • управление глобальным состоянием

Пример:

app/
 ├─ pages/
 │   ├─ dashboard
 │   └─ settings
 ├─ providers
 └─ layout

На этом уровне Radix UI напрямую почти не используется.


Feature Components

Компоненты бизнес-логики, объединяющие несколько элементов интерфейса.

Примеры:

  • UserMenu
  • SettingsDialog
  • NotificationsPanel
  • ProductFilter

Такие компоненты используют готовые элементы дизайн-системы, которые в свою очередь основаны на Radix UI.

features/
 ├─ user-menu
 ├─ settings-dialog
 └─ notifications

Design System Layer

Центральный уровень архитектуры. Здесь создаются обертки над Radix primitives, формирующие единый стиль интерфейса.

Например:

ui/
 ├─ button
 ├─ dialog
 ├─ dropdown-menu
 ├─ sel ect
 └─ tooltip

Каждый компонент инкапсулирует:

  • Radix primitives
  • стили
  • API дизайн-системы
  • единые паттерны поведения

Пример обертки Dialog.

import * as Dialog fr om "@radix-ui/react-dialog"

export function Modal({ children, trigger }) {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        {trigger}
      </Dialog.Trigger>

      <Dialog.Portal>
        <Dialog.Overlay className="overlay" />

        <Dialog.Content className="modal">
          {children}
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}

В результате приложение использует Modal, а не напрямую Radix.


Radix Primitives Layer

Нижний уровень — собственно библиотека Radix UI.

Этот слой предоставляет:

  • управление состоянием
  • обработку клавиатуры
  • accessibility
  • фокус-менеджмент
  • ARIA-атрибуты
  • правильную семантику DOM

Благодаря этому дизайн-система может сосредоточиться только на логике и визуальном стиле.


Архитектура управления состоянием

Radix UI придерживается controlled/uncontrolled модели состояния.

Компоненты могут работать:

1. Неконтролируемо

<Dialog.Root>

Состояние открытия управляется внутри Radix.


2. Контролируемо

const [open, setOpen] = useState(false)

<Dialog.Root open={open} onOpenCha nge={setOpen}>

Состояние контролируется приложением.

Такая архитектура позволяет:

  • интегрировать компоненты с Redux
  • использовать Zustand
  • синхронизировать состояние с URL
  • управлять интерфейсом программно

Контекстная архитектура компонентов

Radix UI активно использует React Context для передачи состояния между примитивами.

Пример:

Dialog.Root
   ├─ Dialog.Trigger
   ├─ Dialog.Content
   └─ Dialog.Close

Внутри:

  • Root создаёт контекст
  • остальные компоненты получают доступ к состоянию

Это позволяет:

  • избегать prop drilling
  • упрощать API
  • обеспечивать независимость компонентов

Порталы и архитектура DOM

Многие компоненты Radix используют React Portal.

Особенно:

  • Dialog
  • Dropdown Menu
  • Tooltip
  • Popover
  • Context Menu

Пример:

<Dialog.Portal>
  <Dialog.Content />
</Dialog.Portal>

Portal перемещает DOM-узел в конец документа:

<body>
  <div id="root"></div>

  <div data-radix-portal>
     dialog content
  </div>
</body>

Это решает несколько архитектурных проблем:

1. Z-index конфликты

Модальные окна всегда поверх интерфейса.

2. overflow hidden

Выпадающие элементы не обрезаются контейнерами.

3. изоляция слоев

Компоненты рендерятся независимо от структуры DOM.


Архитектура accessibility

Radix UI изначально проектировался как доступная библиотека интерфейсов.

Архитектура компонентов включает:

  • ARIA роли
  • управление фокусом
  • навигацию клавиатурой
  • правильную семантику

Пример для Dialog:

role="dialog"
aria-labelledby
aria-describedby

При открытии:

  1. фокус перемещается внутрь диалога
  2. tab-навигация ограничивается модальным окном
  3. Escape закрывает окно
  4. фокус возвращается на Trigger

Все эти механизмы встроены в архитектуру Radix.


Архитектура позиционирования элементов

Компоненты вроде:

  • Tooltip
  • Popover
  • Dropdown Menu
  • Context Menu

используют библиотеку Floating UI для позиционирования.

Архитектура выглядит следующим образом:

Radix Component
↓
Floating UI
↓
DOM measurements
↓
position calculation

Floating UI вычисляет:

  • координаты
  • коллизии с границами экрана
  • оптимальное размещение

Например:

<Tooltip.Content side="top" align="center">

Radix автоматически:

  • изменяет сторону при нехватке места
  • корректирует позицию
  • обновляет координаты при скролле

Архитектура слотов (asChild)

Одной из ключевых архитектурных возможностей Radix является asChild.

Он позволяет заменить DOM-элемент компонента.

Пример:

<Dialog.Trigger asChild>
  <button className="custom-button">
    Open
  </button>
</Dialog.Trigger>

Без asChild:

<button>
  <button>Open</button>
</button>

С asChild:

<button class="custom-button">
  Open
</button>

Это критически важно для архитектуры дизайн-системы, потому что:

  • исключает лишние DOM-узлы
  • сохраняет семантику
  • позволяет использовать любые элементы

Архитектура композиции интерфейса

Radix UI проектировался вокруг принципа headless components.

Это означает, что:

  • логика отделена от представления
  • компоненты не имеют встроенных стилей
  • разработчик полностью контролирует внешний вид

Пример Dropdown Menu:

<DropdownMenu.Root>
  <DropdownMenu.Trigger>Menu</DropdownMenu.Trigger>

  <DropdownMenu.Content>
    <DropdownMenu.Item>Edit</DropdownMenu.Item>
    <DropdownMenu.Item>Delete</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu.Root>

Radix обеспечивает:

  • открытие меню
  • навигацию стрелками
  • фокус
  • закрытие по клику вне

Но не содержит ни одного CSS-правила.


Архитектура масштабирования интерфейса

При росте проекта архитектура на базе Radix обычно организуется следующим образом.

src
 ├─ app
 ├─ pages
 ├─ features
 ├─ widgets
 ├─ shared
 │   └─ ui
 │       ├─ button
 │       ├─ dialog
 │       ├─ select
 │       ├─ tooltip
 │       └─ dropdown-menu
 └─ lib

Такой подход обеспечивает:

  • изоляцию UI-компонентов
  • повторное использование
  • единый дизайн-стиль
  • минимальную связанность модулей

Архитектура стилизации

Radix UI не диктует систему стилизации.

На практике используются:

  • Tailwind
  • CSS Modules
  • Styled Components
  • Vanilla Extract
  • Stitches

Часто применяется архитектура:

component.tsx
styles.css
index.ts

Пример:

dialog/
 ├─ dialog.tsx
 ├─ dialog.css
 └─ index.ts

CSS:

.dialogOverlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
}

.dialogContent {
  background: white;
  border-radius: 8px;
}

Архитектура обработки событий

Radix UI предоставляет расширенные обработчики событий:

onOpenChange
onValueChange
onCheckedChange
onHighlightChange

Пример:

<DropdownMenu.Root
  onOpenCha nge={(open) => {
    console.log(open)
  }}
>

Такая архитектура обеспечивает:

  • прозрачное управление состоянием
  • интеграцию с аналитикой
  • синхронизацию UI

Архитектура фокус-менеджмента

Radix содержит отдельные механизмы управления фокусом.

Они решают проблемы:

  • ловушки фокуса (focus trap)
  • возврат фокуса
  • последовательность tab-навигации

Пример:

<Dialog.Content
  onOpenAutoFo cus={(event) => {
    event.preventDefault()
  }}
>

Это позволяет полностью контролировать фокус внутри компонентов.


Архитектура расширения компонентов

Компоненты Radix легко расширяются благодаря композиции.

Пример расширения Dropdown Menu:

function UserMenu() {
  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger>
        Avatar
      </DropdownMenu.Trigger>

      <DropdownMenu.Content>
        <DropdownMenu.Item>
          Profile
        </DropdownMenu.Item>

        <DropdownMenu.Item>
          Settings
        </DropdownMenu.Item>

        <DropdownMenu.Separator />

        <DropdownMenu.Item>
          Logout
        </DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu.Root>
  )
}

Такая архитектура позволяет:

  • переиспользовать элементы
  • комбинировать компоненты
  • строить сложные интерфейсы без дублирования логики.

Архитектура независимых пакетов

Radix UI распространяется как набор отдельных npm-пакетов.

Например:

@radix-ui/react-dialog
@radix-ui/react-dropdown-menu
@radix-ui/react-tooltip
@radix-ui/react-select

Это дает преимущества:

  • минимальный размер бандла
  • tree-shaking
  • независимые обновления компонентов
  • возможность подключать только нужные элементы интерфейса.

Архитектура масштабируемого UI-слоя

При правильной организации архитектуры Radix UI становится фундаментом дизайн-системы.

Принцип выглядит следующим образом:

Radix primitives
↓
UI components
↓
Feature components
↓
Application

Каждый уровень:

  • изолирован
  • переиспользуем
  • легко тестируется
  • расширяем

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