一、为什么需要 Headless UI?

在现代前端开发中,我们经常在 “开发效率”“高度定制” 之间反复横跳。

  • 使用 Ant Design、MUI 等 强 UI 框架 开箱即用,但样式和交互被深度绑定

  • 手写组件自由度高,但要自己处理 键盘交互、ARIA、可访问性,成本极高

Headless UI 正好切中了这个痛点:

只提供“行为与状态”,不提供任何样式

你获得的是:

  • 100% 可定制的 UI

  • 官方级别的可访问性支持

  • 与 Tailwind CSS、现代 React/Vue 完美契合

这也是为什么它在 Tailwind 生态、ShadCN UI、Radix UI 等方案中占据重要位置。


二、Headless UI 是什么?

Headless UI 是由 Tailwind Labs 出品的无样式组件库,目前支持:

  • React

  • Vue

它并不是一个传统意义上的 UI 库,而是:

一套可访问的交互逻辑抽象

例如:

  • <Menu /> 负责:

    • 打开 / 关闭状态

    • 键盘上下选择

    • ESC 关闭

    • 焦点管理

  • 至于:

    • 颜色

    • 布局

    • 动画

全部交给你自己决定


三、核心设计理念

1.Headless(无头)

  • 不输出任何样式

  • 不关心你用 Tailwind、CSS Modules 还是 Styled Components

2.可访问性优先(Accessibility First)

所有组件:

  • 自动注入 ARIA 属性

  • 完整键盘支持

  • 符合 WAI-ARIA 规范

这在企业级、SaaS、ToB 项目中尤为重要

3.状态即 UI

Headless UI 强调:

状态变化 → UI 自由映射

这与 React 的声明式理念高度一致。


四、安装与基础使用

先创建一个项目

pnpm create vite@latest headlessui-app --template react-ts

再添加headlessui

pnpm add @headlessui/react

一个最简单的 Menu 示例:

import { Menu } from '@headlessui/react'

export function SimpleMenu() {
  return (
    <Menu>
      <Menu.Button>更多操作</Menu.Button>
      <Menu.Items>
        <Menu.Item>
          {({ active }) => (
            <button className={active ? 'bg-gray-100' : ''}>
              编辑
            </button>
          )}
        </Menu.Item>
      </Menu.Items>
    </Menu>
  )
}

你会发现:

  • 没有任何 CSS

  • 但:键盘、焦点、ARIA 全部可用

<button id="headlessui-menu-button-_r_2_" type="button" aria-haspopup="menu" aria-expanded="false" data-headlessui-state="">更多操作</button>
<div aria-labelledby="headlessui-menu-button-_r_2_" id="headlessui-menu-items-_r_3_" role="menu" tabindex="0" data-headlessui-state="open" data-open="" style="--button-width: 104.375px;"><button class="" id="headlessui-menu-item-_r_a_" role="menuitem" tabindex="-1" data-headlessui-state="">编辑</button></div>


五、常用组件详解

1.Menu(下拉菜单)

适用场景:

  • 操作菜单

  • 更多按钮

  • 用户头像菜单

关键能力:

  • ⬆⬇ 键盘切换

  • Enter 选择

  • ESC 关闭

  • 自动焦点回收


2.Dialog(模态框)

import { Dialog } from "@headlessui/react";
import { useState } from "react";

export function MyDialog() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open Dialog</button>
        <Dialog open={isOpen} onClose={setIsOpen}>
            <Dialog.Panel>
                <Dialog.Title>删除确认</Dialog.Title>
                <p>确定要删除吗?</p>
            </Dialog.Panel>
        </Dialog>
    </>
    );
}

export default MyDialog;

内置支持:

  • 焦点陷阱(Focus Trap)

  • 禁止背景滚动

  • ESC 关闭

这些细节如果手写,成本极高。

<div id="headlessui-portal-root"><div data-headlessui-portal=""><button type="button" data-headlessui-focus-guard="true" aria-hidden="true" style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px;"></button><div><div id="headlessui-dialog-_r_v_" role="dialog" tabindex="-1" aria-modal="true" data-headlessui-state="open" data-open="" aria-labelledby="headlessui-dialog-title-_r_16_"><div id="headlessui-dialog-panel-_r_15_" data-headlessui-state="open" data-open=""><h2 id="headlessui-dialog-title-_r_16_" data-headlessui-state="open" data-open="">删除确认</h2><p>确定要删除吗?</p></div></div></div><button type="button" data-headlessui-focus-guard="true" aria-hidden="true" style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px;"></button></div></div>

3.Listbox(选择器)

可替代:

  • <select>

  • 自定义下拉选择

非常适合:

  • 与 Tailwind / ShadCN 风格统一


4.Transition(动画)

<Transition
  enter="transition duration-200"
  enterFrom="opacity-0 scale-95"
  enterTo="opacity-100 scale-100"
>
  • 与 Tailwind 天然融合

  • 无 JS 动画心智负担


六、与 Tailwind / ShadCN UI 的关系

Headless UI vs ShadCN UI

对比项 Headless UI ShadCN UI
是否无样式 ❌(有默认样式)
交互封装
上手速度
定制自由度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐

ShadCN UI = Headless UI / Radix + 一套设计系统


七、在企业项目中的最佳实践

什么时候选 Headless UI?

  • 有明确的设计规范

  • 使用 Tailwind CSS

  • 重视可访问性

  • 中大型 React 项目

什么时候不适合?

  • Demo / 原型项目

  • 希望“复制即用”的团队


八、常见坑与注意事项

1.不要忘记包裹结构

Headless UI 强依赖组件层级结构,随意拆分可能导致失效。

2.样式全部自己负责

它不会帮你处理:

  • z-index

  • overflow

  • positioning

3.与 Portal 的关系

Dialog / Menu 默认使用 Portal,需要注意:

  • 微前端

  • Shadow DOM


九、总结

Headless UI 并不是一个“新手友好”的 UI 框架,但它:

  • 非常“React 思维”

  • 极度适合设计驱动的团队

  • 在可访问性上几乎是业界标杆

如果你正在:

  • 构建自己的设计系统

  • 使用 Tailwind CSS

  • 写中大型、长期维护的项目

Headless UI 值得成为你的基础设施之一。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐