# Дизайн-система Headless UI

## Предварительный просмотр интерфейса дизайн-системы Headless UI

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

## Справочник дизайн-системы Headless UI

У Headless UI нет официальной шкалы визуальных токенов. Все числовые значения и цвета ниже относятся к этому примеру OpenDesign; в связанной документации описаны поведение и API состояний. Проверенный пакет: @headlessui/react 2.2.10.

### Оригинальные цветовые роли CSS

| Название / правило | Значение / конфигурация | Применение |
| --- | --- | --- |
| --canvas | #f5f5f4 | Рабочая область документа. |
| --surface | #ffffff | Фон страницы, панель доступа и диалоговое окно. |
| --ink | #1c1917 | Основной текст. |
| --muted | #57534e | Второстепенный текст. |
| --line | #d6d3d1 | Границы панели и полей ввода. |
| --accent | #4338ca | Основное действие и контур фокуса. |
| --accent-soft | #eef2ff | Фон выбранного элемента и аватара. |
| --error | #b91c1c | Текст и граница для недопустимого адреса электронной почты. |
### Оригинальные интервалы и геометрия

| Название / правило | Значение / конфигурация | Применение |
| --- | --- | --- |
| --space-1 / --space-2 | 4px / 8px | Небольшие шаги. |
| --space-3 / --space-4 | 12px / 16px | Интервалы между элементами управления и отступ на мобильном экране. |
| --space-6 / --space-8 | 24px / 32px | Внутренние отступы панели и основные интервалы. |
| --radius-control | 8px | Кнопки и поля ввода. |
| --radius-panel | 16px | Фон страницы и диалогового окна. |
| .dialog-panel width | min(100%, 460px) | Ограничение наложения на узких экранах. |
| .dialog-panel max-height | calc(100% - 16px) | Оставляет свободное место внутри контейнера с фиксированным позиционированием. |
### Оригинальные настройки шрифтов и адаптивности

| Название / правило | Значение / конфигурация | Применение |
| --- | --- | --- |
| Body | 16px / 1.5 system-ui | Внешний шрифт не требуется. |
| Screen title | 28px / 1.25 | Идентификация документа. |
| Document headline | 32px; 26px below 700px | Иерархия редакционного содержимого. |
| Dialog heading | 20px / 1.3 | Обозначает задачу общего доступа. |
| Labels and helper copy | 14px | Контекст поля. |
| Error and access description | 13px | Постоянное отображение обратной связи. |
| Breakpoint | 700px | Панели документа и доступа располагаются одна под другой. |
### Контракты нативного взаимодействия

| Название / правило | Значение / конфигурация | Применение |
| --- | --- | --- |
| Dialog open / onClose | Controlled boolean / dismissal callback | Нажатие Escape и взаимодействие вне диалогового окна запрашивают его закрытие, но не сохраняют данные. |
| DialogPanel | Content boundary | Подложка и контейнер позиционирования находятся за этой границей. |
| Listbox value / onChange | Controlled permission string | Выбор с клавиатуры обновляет выбранное разрешение. |
| data-focus / data-selected | Native state attributes | CSS отделяет фокус с клавиатуры от подтверждённого выбора. |
| Switch checked / onChange | Controlled boolean | Атрибут data-checked задаёт визуальное состояние. |
## Полное руководство по дизайн-системе Headless UI

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

Подробные рекомендации, отправная точка для реализации и ссылки на источники.

## Дизайн-система Headless UI: сначала составьте модель взаимодействия, затем оформляйте экран

### Сначала составьте модель взаимодействия, затем оформляйте экран

> Dialog, Listbox и Switch представляют разные виды взаимодействия. Dialog управляет ограниченной задачей и контекстом фокуса; Listbox представляет один зафиксированный выбор; Switch отвечает за независимый логический параметр. Их можно объединить, не объединяя состояние: черновик приглашения, выбранное разрешение и настройка публичной ссылки остаются раздельными.

В примере используется компонент React API с прямым импортом, например DialogPanel и ListboxButton. В диалоговом окне находится список Listbox, поэтому позиционирование всплывающего окна и его закрытие должны работать внутри уже открытого оверлея. Сначала реализуйте эту композицию, а затем добавьте вокруг неё панель документа и сводку по доступу. Переданный CSS задаёт единое оформление этих нативных элементов.

## Анатомия Dialog, Listbox и Switch в дизайн-системе Headless UI

### Продумайте границы слоя

DialogBackdrop — соседний элемент контейнера с фиксированным позиционированием. DialogPanel — ограниченная белая панель внутри этого контейнера; она не заполняет область просмотра. DialogTitle называет задачу, а Description объясняет последствия закрытия окна. Нативный элемент ввода содержит data-autofocus, а Headless UI управляет поведением фокуса в окружающем диалоговом окне.

Прочитать вспомогательную спецификацию

### Listbox выбирает значение, Switch меняет логический параметр

ListboxButton показывает выбранную роль. В ListboxOptions задан anchor="bottom" и ширина, основанная на --button-width, поэтому всплывающее меню выравнивается относительно кнопки, которая его открывает. У каждого ListboxOption есть постоянное строковое значение. Нативные атрибуты data-focus и data-selected задают два разных варианта оформления CSS.

Переключатель публичной ссылки размещён в Field вместе с Label, а не представлен как стилизованная кнопка без подписи. При изменении его состояния меняются и доступный элемент управления, и видимый поясняющий текст. Локальное приглашение не зависит от настройки публичной ссылки.

Прочитать вспомогательную спецификацию

## Дизайн-система Headless UI: разделяйте выбор, закрытие и локальное подтверждение

### Используемая здесь модель состояния

При открытии создаётся новый черновик приглашения и назначается роль «Может просматривать». Кнопка «Отмена», клавиша Escape и щелчок за пределами окна закрывают диалог; ни одно из этих действий не добавляет получателя. При отправке проверяется адрес электронной почты и отклоняется адрес, который уже есть в списке. Затем приглашение добавляется в локальное состояние, а диалог закрывается. В подтверждении прямо указано, что письмо не отправлено.

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

- Некорректный ввод: оставьте диалог открытым и свяжите сообщение об ошибке с полем адреса электронной почты.

- Повторный адрес: не изменяйте существующий список доступа.

- Локальное приглашение принято: обновите список и сообщите, что результат отображается только в предварительном просмотре.

## Дизайн-система Headless UI: независимый выбор стиля и положения фокуса при навигации с клавиатуры

### Независимый выбор стиля и положения фокуса при навигации с клавиатуры

ListboxOption предоставляет атрибуты data-focus и data-selected для разных состояний. Для элемента в фокусе используется светлый акцентный стиль, а выбранное разрешение отмечается галочкой. Поэтому пользователь, работающий с клавиатуры, может перемещаться по вариантам, не теряя из виду текущий выбор.

Это различие сохраняется и за пределами всплывающего окна: роль для ошибки относится к полю некорректного адреса получателя, а состояние Switch «включено» — к доступу по общедоступной ссылке. Ограничивайте CSS состоянием компонента, которое он описывает. Широкий селектор, одинаково окрашивающий все элементы в фокусе или включённом состоянии, скрыл бы различия в их значении.

```
:root { --accent: #4338ca; --accent-soft: #eef2ff; --error: #b91c1c; }
.role-option[data-focus] { background: var(--accent-soft); }
.role-option[data-selected] .selected { visibility: visible; }
```

Справочник по коду

## Дизайн-система Headless UI: понятное отображение сведений о документе и прав доступа

### Настройки текста, которые легко воспроизвести

Размер заголовка документа — 28px, межстрочный интервал — 1.25; заголовка диалога — 20px и 1.3 соответственно. Основной текст набран системным шрифтом платформы размером 16px с межстрочным интервалом 1.5. Подписи полей и вспомогательный текст имеют размер 14px. Точные значения указаны в style.css — читателям не нужно определять их по снимку экрана.

Для адреса электронной почты в списке доступа задано overflow-wrap:anywhere, чтобы он не расширял столбец. Разрешение отображается на отдельной строке. В диалоговом окне полное название документа и выбранное разрешение видны в момент добавления приглашения.

## Headless UI Полное позиционирование оверлея в дизайн-системе и адаптивность CSS

### Макет не спрятан за предварительным условием

Предоставленная таблица стилей охватывает весь оверлей: фиксированное позиционирование по границам окна, непрозрачность подложки, z-index, максимальную ширину, вертикальную прокрутку и отступы. При ширине менее 700px панели документа располагаются одна под другой, а внутренние отступы диалогового окна уменьшаются. Сборка Tailwind не предполагается.

Не смешивайте контейнер позиционирования с DialogPanel. Контейнер занимает доступную область окна и задаёт отступы от краёв; панель формирует ограниченную по размеру поверхность с содержимым. Для длинного содержимого диалогового окна предусмотрите прокручиваемую область, чтобы при узкой ширине экрана оставались доступны поле получателя, выбор разрешения и элементы закрытия.

```
.dialog-position { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; padding: 16px; overflow-y: auto; }
.dialog-panel { width: min(100%, 460px); max-height: calc(100% - 16px); overflow-y: auto; }
```

Справочник по коду

## Headless UI Используйте встроенное поведение состояния и фокуса компонентов дизайн-системы

### Проверка с клавиатуры

Откройте диалоговое окно и проверьте, что начальный фокус установлен на поле получателя. Нажимая Tab, перейдите к кнопке выбора разрешения; с помощью клавиш со стрелками и Enter выберите роль. Клавиша Escape закрывает активное всплывающее окно или диалоговое окно в соответствии с текущим контекстом фокуса нативного компонента. После закрытия диалогового окна фокус должен вернуться к кнопке открытия панели общего доступа.

В поле электронной почты для пользовательского сообщения об ошибке используются aria-invalid и aria-describedby. Галочка в варианте выбора носит декоративный характер, поскольку Listbox уже предоставляет семантику выбора. У элемента управления публичной ссылкой есть видимая подпись, а поясняющее предложение остаётся на месте после переключения. При уменьшении движения CSS убирается декоративная анимация переключателя.

Прочитать вспомогательную спецификацию

## Дизайн-система Headless UI Последовательно используйте одно поколение React API

### Последовательно используйте одно поколение React API

Полная запись React монтирует App, таблица стилей которого задаёт геометрию наложения и селекторы состояний. В проверенном примере версии 2.2.10 напрямую импортируются DialogPanel, DialogBackdrop, ListboxButton и ListboxOptions. Старые варианты составных компонентов следует заменить осознанно, а не смешивать с этой композицией.

ListboxOptions использует anchor="bottom" и встроенную переменную --button-width, чтобы выровнять список параметров относительно кнопки-триггера. Эти детали — часть интеграции, а не необязательное оформление: если удалить контейнер позиционирования или заменить ширину несвязанным фиксированным значением, вложенное всплывающее окно может работать неправильно, даже если закрытое поле выглядит корректно.

1. Установите @headlessui/react в проект React.

2. Сохраните примеры App.jsx и style.css, затем замените main.jsx, как показано.

3. Запустите сервер разработки и проверьте закрытие диалогового окна, выбор уровня доступа и обработку повторного адреса электронной почты.

## Дизайн-система Headless UI: проверьте реальное взаимодействие с настройками доступа

### Условия приёмки

Начните проверку с документа и уже добавленного получателя, а не с пустой галереи компонентов. Добавьте корректное приглашение с уровнем доступа «Может комментировать», затем откройте диалог снова и попробуйте указать тот же адрес электронной почты. Убедитесь, что исходная строка получателя сохранена, а при повторном добавлении отображается конкретная ошибка. Включите публичную ссылку и проверьте отдельное пояснение к ней.

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

## Примеры компонентов дизайн-системы Headless UI

### App.jsx — композиция для управления доступом на встроенных примитивах

Полноценные управляемые компоненты Dialog и Listbox для выбора уровня доступа, Switch с меткой и локальная проверка данных.

```
import React, { useState } from 'react';
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description, Listbox, ListboxButton, ListboxOptions, ListboxOption, Switch, Field, Label } from '@headlessui/react';
import './style.css';
const roles = ['Can view', 'Can comment', 'Can edit'];
export default function App() {
 const [open,setOpen]=useState(false),[role,setRole]=useState(roles[0]),[publicLink,setPublicLink]=useState(false),[email,setEmail]=useState(''),[error,setError]=useState(''),[notice,setNotice]=useState('');
 const [people,setPeople]=useState([{email:'alex@example.test',role:'Can edit'}]);
 const start=()=>{setEmail('');setError('');setRole(roles[0]);setOpen(true)};
 const submit=e=>{e.preventDefault();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)){setError('Enter a complete email address.');return;}if(people.some(p=>p.email===email)){setError('This person already appears in the access list.');return;}setPeople([...people,{email,role}]);setOpen(false);setNotice('Invitation added to this local example only. No email was sent.');};
 return <main className='sharing-shell'>
  <header><div><span className='eyebrow'>HEADLESS UI / CUSTOM SURFACE</span><h1>Quarterly brief</h1><p className='muted'>Research · Updated today</p></div><button className='primary' onClick={start}>Share document</button></header>
  <div className='document-grid'><article className='paper'><span className='tag'>WORKING DOCUMENT</span><h2>Make the next decision clearer.</h2><p>Three research priorities for the coming quarter, shared with the people who need to review them.</p><div className='rule'/><h3>01 / Understand the handoff</h3><p>Compare where context is lost between research notes and the next product decision.</p><h3>02 / Review access deliberately</h3><p>Keep editing rights separate from the ability to read a document.</p></article>
  <aside className='access'><h2>People with access</h2><p className='muted'>The list below is local demonstration state.</p>{people.map((p,i)=><div className='person' key={p.email}><span className='avatar'>{i===0?'AK':'+'}</span><div><strong>{p.email}</strong><small>{p.role}</small></div></div>)}<Field className='link-setting'><Label>Public link</Label><Switch checked={publicLink} onChange={setPublicLink} className='switch'><span /></Switch></Field><p className='link-detail'>{publicLink?'Anyone with the example link could view this document. This changes only the local preview.':'Restricted to the named people in this example.'}</p><p role='status' className='notice'>{notice}</p></aside></div>
  <Dialog open={open} onClose={()=>setOpen(false)} className='dialog-root'><DialogBackdrop className='backdrop'/><div className='dialog-position'><DialogPanel className='dialog-panel'><DialogTitle as='h2'>Share quarterly brief</DialogTitle><Description className='muted'>Choose one recipient and a permission. Closing discards this invitation draft.</Description><form noValidate onSubmit={submit}><label className='field-label' htmlFor='recipient'>Recipient email</label><input id='recipient' data-autofocus type='email' value={email} onChange={e=>{setEmail(e.target.value);setError('')}} aria-invalid={!!error} aria-describedby={error?'email-error':undefined} placeholder='jules@example.test'/>{error&&<p id='email-error' role='alert' className='error'>{error}</p>}<Listbox value={role} onChange={setRole}><Label className='field-label'>Permission</Label><ListboxButton className='role-button'>{role}<span aria-hidden='true'>⌄</span></ListboxButton><ListboxOptions anchor='bottom' className='role-options'>{roles.map(value=><ListboxOption key={value} value={value} className='role-option'><span>{value}</span><span className='selected' aria-hidden='true'>✓</span></ListboxOption>)}</ListboxOptions></Listbox><p className='muted permission-help'>{role==='Can edit'?'May change document content in this example.':role==='Can comment'?'May add comments without editing content.':'May read the document without changing it.'}</p><div className='dialog-actions'><button type='button' onClick={()=>setOpen(false)}>Cancel</button><button className='primary' type='submit'>Add invitation</button></div></form></DialogPanel></div></Dialog>
  <footer>Native Dialog, Listbox, and Switch behavior. Original CSS below supplies all visual values; Headless UI does not ship this palette.</footer>
 </main>;
}
```

Справочник по коду

- Адрес электронной почты не отправляется, запрос не выполняется.

- Escape и кнопка отмены отбрасывают несохранённое приглашение.

### style.css — полная исходная визуальная система

Все визуальные токены, селекторы состояний, расположение наложений и правила адаптивности, необходимые для App.jsx. Это примеры решений, а не официальные настройки по умолчанию Headless UI.

```
:root{--canvas:#f5f5f4;--surface:#fff;--ink:#1c1917;--muted:#57534e;--line:#d6d3d1;--accent:#4338ca;--accent-soft:#eef2ff;--error:#b91c1c;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-6:24px;--space-8:32px;--radius-control:8px;--radius-panel:16px;font:16px/1.5 system-ui,sans-serif;color:var(--ink);background:var(--canvas)}*{box-sizing:border-box}body{margin:0}button,input{font:inherit}button{cursor:pointer;border:1px solid var(--line);border-radius:var(--radius-control);padding:10px 16px;background:var(--surface);color:var(--ink);font-weight:600}button:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.primary{background:var(--accent);color:white;border-color:var(--accent)}.sharing-shell{max-width:1120px;margin:auto;padding:32px}.sharing-shell>header{display:flex;align-items:center;justify-content:space-between;gap:24px;margin-bottom:32px}.eyebrow{font-size:11px;letter-spacing:.1em;color:var(--accent);font-weight:700}h1{font-size:28px;line-height:1.25;margin:8px 0}h2{font-size:20px;line-height:1.3;margin:0 0 12px}h3{font-size:16px;margin:24px 0 8px}p{margin:0 0 16px}.muted{color:var(--muted);font-size:14px}.document-grid{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(260px,1fr);gap:24px}.paper,.access{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-panel);padding:32px}.paper h2{font-size:32px;max-width:15ch;margin-top:24px}.paper p{max-width:48ch}.tag{font-size:11px;font-weight:600;border:1px solid var(--line);padding:4px 8px;border-radius:4px}.rule{height:1px;background:var(--line);margin:24px 0}.person{display:flex;gap:12px;padding:16px 0;border-bottom:1px solid var(--line)}.person strong{display:block;font-size:14px;overflow-wrap:anywhere}.person small{color:var(--muted)}.avatar{flex:none;width:32px;height:32px;border-radius:50%;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;font-size:12px}.link-setting{display:flex;align-items:center;justify-content:space-between;margin-top:24px;font-weight:600}.switch{padding:3px;width:44px;height:26px;border:0;border-radius:999px;background:#a8a29e}.switch span{display:block;width:20px;height:20px;border-radius:50%;background:white;transition:transform .15s}.switch[data-checked]{background:var(--accent)}.switch[data-checked] span{transform:translateX(18px)}.link-detail{margin-top:12px;font-size:13px;color:var(--muted)}.notice{font-size:13px;color:var(--accent)}footer{font-size:12px;color:var(--muted);margin-top:24px}.dialog-root{position:fixed;inset:0;z-index:20}.backdrop{position:fixed;inset:0;background:rgb(28 25 23/.45)}.dialog-position{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;overflow-y:auto}.dialog-panel{width:min(100%,460px);max-height:calc(100% - 16px);overflow-y:auto;background:var(--surface);padding:32px;border-radius:var(--radius-panel);box-shadow:0 24px 64px rgb(0 0 0/.22)}.field-label{display:block;font-size:14px;font-weight:600;margin:20px 0 8px}input[type=email]{display:block;width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:var(--radius-control);background:white;color:var(--ink)}input[aria-invalid=true]{border-color:var(--error)}.error{font-size:13px;color:var(--error);margin:8px 0}.role-button{display:flex;justify-content:space-between;width:100%;text-align:left}.role-options{z-index:30;width:var(--button-width);--anchor-gap:4px;background:var(--surface);border:1px solid var(--line);padding:4px;border-radius:8px;box-shadow:0 8px 24px rgb(0 0 0/.12);outline:none}.role-option{display:flex;justify-content:space-between;padding:10px;border-radius:4px;cursor:pointer;font-size:14px}.role-option[data-focus]{background:var(--accent-soft)}.role-option .selected{visibility:hidden;color:var(--accent)}.role-option[data-selected] .selected{visibility:visible}.permission-help{margin-top:12px}.dialog-actions{display:flex;justify-content:flex-end;gap:12px;margin-top:24px}@media(max-width:700px){.sharing-shell{padding:20px}.sharing-shell>header{align-items:flex-start;flex-direction:column}.document-grid{grid-template-columns:1fr}.paper,.access{padding:24px}.paper h2{font-size:26px}.dialog-panel{padding:24px}}@media(prefers-reduced-motion:reduce){.switch span{transition:none}}
```

Справочник по коду

- Не смешивайте data-focus и data-selected.

- Tailwind не является обязательной зависимостью.

## Настройка агента дизайн-системы Headless UI

### Отправная точка для реализации

Полные файлы стилей и компонентов приведены выше. Требуется версия Node, поддерживаемая выбранным выпуском Vite. В демоверсию локально включён тот же пакет нативных компонентов.

```
# Terminal
npm create vite@latest sharing-demo -- --template react
cd sharing-demo
npm install
npm install @headlessui/react@2.2.10

# Save the complete examples as src/App.jsx and src/style.css.
# Replace src/main.jsx with:
import React from 'react';
import {createRoot} from 'react-dom/client';
import App from './App';
createRoot(document.getElementById('root')).render(<App/>);

# Terminal
npm run dev
```

### Запрос для вашего проекта

```
Используйте предоставленные App.jsx и style.css из Headless UI. Сохраните композицию нативных Dialog, Listbox и Switch, разделяйте черновые разрешения и подтверждённый доступ, а также оставьте все селекторы состояния фокуса и выбора. Замените локальное добавление приглашения на нашу авторизованную API, предусмотрев состояния ожидания и ошибки; никогда не сохраняйте данные при вызове onClose. Не представляйте пользовательскую палитру CSS как официальную тему Headless UI.
```

## Источники и область применения дизайн-системы Headless UI

Исходная визуальная конфигурация OpenDesign и пример React с использованием нативного поведения Headless UI 2.2.10. Значения CSS явно указаны как решения приложения; Headless UI не предоставляет эту тему. Приглашения и изменения разрешений хранятся только в памяти локального предпросмотра.

- Dialog API Официальное руководство по взаимодействию, композиции и состояниям.

- Listbox API Официальное руководство по взаимодействию, композиции и состояниям.

- Switch API Официальное руководство по взаимодействию, композиции и состояниям.

## Связанные ресурсы дизайн-системы Headless UI

### HeroUI

Справочник по HeroUI v3 с настоящими семантическими переменными CSS, составными элементами управления React, светлыми и тёмными поверхностями и рабочим рецептом экспорта изображений.

### Ionic Framework

Нативный интерфейс Ionic для настройки чтения с проверенными цветовыми ролями, геометрией диапазонов iOS и Material, настоящим поведением сегментов и переключателей и статьёй, которая реагирует на выбор шрифта, размера и фона.

### Kobalte

Kobalte предоставляет headless-компоненты для Solid, включая числовые поля с отдельными исходными и форматированными значениями, составную анатомию флажка и кнопки с состоянием нажатия. В руководстве эти примитивы связаны с сигналами Solid на примере масштабатора рецепта с вычисляемыми количествами и сбрасываемыми флажками ингредиентов.


- [Справочник по реализации](https://headlessui.com/react/dialog)
- [Источник](https://headlessui.com/react/dialog)
- [Источник](https://headlessui.com/react/listbox)
- [Прочитать вспомогательную спецификацию](https://headlessui.com/react/dialog)
- [Прочитать вспомогательную спецификацию](https://headlessui.com/react/listbox)
- [Справочник по коду](https://headlessui.com/react/listbox)
- [Справочник по коду](https://headlessui.com/react/dialog)
- [Прочитать вспомогательную спецификацию](https://headlessui.com/react/switch)
- [Dialog API Официальное руководство по взаимодействию, композиции и состояниям.](https://headlessui.com/react/dialog)
- [Listbox API Официальное руководство по взаимодействию, композиции и состояниям.](https://headlessui.com/react/listbox)
- [Switch API Официальное руководство по взаимодействию, композиции и состояниям.](https://headlessui.com/react/switch)
