在 React 项目中,@headlessui/react
是一个由 Tailwind Labs 开发的无样式、可访问性良好的 UI 组件库,常用于构建完全自定义风格的对话框(Dialog)、菜单、列表等。
@headlessui/react
提供的 Dialog
、DialogBackdrop
、DialogTitle
是用来创建无障碍对话框(Modal)的核心组件。下面我给你详细介绍它们的作用和用法,并提供一个完整示例。
1. 基本概念
1.1 Dialog
- 核心容器组件,包裹整个对话框内容。
自动处理:
role="dialog"
和aria-modal="true"
- 焦点锁定(focus trap)
- ESC 键关闭(可选)
1.2 DialogBackdrop
- 对话框的背景遮罩层。
- 通常覆盖整个视口,阻止用户点击其他区域。
1.3 DialogTitle
- 对话框的标题,自动关联到
aria-labelledby
,提升无障碍性。
2. 安装
npm install @headlessui/react
# 或
yarn add @headlessui/react
3. 完整示例
import { useState, Fragment } from 'react';
import { Dialog, DialogBackdrop, DialogTitle } from '@headlessui/react';
export default function MyDialog() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button
onClick={() => setIsOpen(true)}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
打开对话框
</button>
<Dialog open={isOpen} onClose={() => setIsOpen(false)} className="relative z-50">
{/* 背景遮罩 */}
<DialogBackdrop className="fixed inset-0 bg-black/50" />
{/* 对话框内容 */}
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel className="mx-auto max-w-sm rounded bg-white p-6 shadow-lg">
<DialogTitle className="text-lg font-bold">
提示信息
</DialogTitle>
<p className="mt-2 text-gray-600">
这是使用 @headlessui/react 创建的无障碍对话框。
</p>
<div className="mt-4 flex justify-end gap-2">
<button
className="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300"
onClick={() => setIsOpen(false)}
>
取消
</button>
<button
className="px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600"
onClick={() => setIsOpen(false)}
>
确定
</button>
</div>
</Dialog.Panel>
</div>
</Dialog>
</>
);
}
4. 关键点说明
Dialog
open
控制是否显示onClose
用于关闭逻辑(点击遮罩或按 ESC 时触发)
DialogBackdrop
- 必须直接作为
Dialog
的子组件 - 负责遮罩层,默认无动画,可结合
Transition
添加渐入渐出效果
- 必须直接作为
DialogTitle
- 作为标题标签
- 自动关联到
aria-labelledby
,提升可访问性
Dialog.Panel
- 对话框的内容面板(注意
@headlessui/react
的最新版本用Dialog.Panel
,旧版本可以直接写内容)
- 对话框的内容面板(注意
5. 可选增强
如果你想要动画,可以结合 Transition
组件使用:
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Transition } from '@headlessui/react'
<Transition show={isOpen}>
<Dialog ...>
<Transition.Child
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<DialogBackdrop className="fixed inset-0 bg-black/50" />
</Transition.Child>
<Transition.Child
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<DialogPanel className="fixed inset-0 flex items-center justify-center">
...
</DialogPanel>
</Transition.Child>
</Dialog>
</Transition>
这样可以让背景和对话框都有过渡动画。
6. 总结
Dialog
:核心容器,提供无障碍、焦点管理、ESC 关闭等功能DialogBackdrop
:背景遮罩,阻止点击外部区域DialogTitle
:标题,自动关联 ARIA,提高无障碍性- 常配合
Dialog.Panel
、Transition
使用,实现完整的模态弹窗