Headless UI:无样式、可访问的 UI 组件

Adam Wathan

构建现代 Web 应用程序时,最大的痛点之一是构建自定义组件,例如选择菜单、下拉菜单、切换开关、模态框、选项卡、单选按钮组——这些组件在项目与项目之间非常相似,但永远不会完全相同

您可以使用现成的软件包,但它们通常与自己提供的样式紧密耦合。最终,它们很难与您自己项目的外观和感觉相匹配,并且几乎总是需要编写一堆 CSS 覆盖,这在使用 Tailwind CSS 时感觉像是一大步倒退。

另一种选择是从头开始构建自己的组件。乍一看似乎很容易,但随后您会想起需要添加对键盘导航、管理 ARIA 属性、焦点捕获的支持,突然之间您要花费 3-4 周的时间来构建一个真正万无一失的下拉菜单。

我们认为有更好的选择,所以我们正在构建它。

Headless UI 是一组完全无样式、完全可访问的 React 和 Vue UI 组件 (以及即将推出的 Alpine.js 组件),使您可以轻松构建这些类型的自定义组件,而无需担心任何复杂的实现细节,并且不会牺牲使用简单的实用程序类从头开始设置样式的能力。

Headless UI Logo

以下是使用 @headlessui/react 构建自定义下拉菜单(该库包含的众多组件之一)的样子,具有完整的键盘导航支持和 ARIA 属性管理,并使用简单的 Tailwind CSS 实用程序设置样式

import { Menu } from "@headlessui/react";
function MyDropdown() {
return (
<Menu as="div" className="relative">
<Menu.Button className="rounded bg-blue-600 px-4 py-2 text-white ...">Options</Menu.Button>
<Menu.Items className="absolute right-0 mt-1">
<Menu.Item>
{({ active }) => (
<a className={`${active && "bg-blue-500 text-white"} ...`} href="/account-settings">
Account settings
</a>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<a className={`${active && "bg-blue-500 text-white"} ...`} href="/documentation">
Documentation
</a>
)}
</Menu.Item>
<Menu.Item disabled>
<span className="opacity-75 ...">Invite a friend (coming soon!)</span>
</Menu.Item>
</Menu.Items>
</Menu>
);
}

以下是您在示例中免费获得的功能,而无需自己编写任何与之相关的代码

  • 下拉面板在单击、空格键、Enter 键或使用箭头键时打开
  • 当您按 Esc 键或在外部单击时,下拉菜单关闭
  • 您可以使用向上和向下箭头键导航项目
  • 您可以使用 Home 键跳转到第一个项目,使用 End 键跳转到最后一个项目
  • 使用键盘导航时,禁用的项目会自动跳过
  • 在使用键盘导航后将鼠标悬停在项目上,将切换到基于鼠标位置的焦点
  • 使用键盘导航项目时,项目会正确地向屏幕阅读器播报
  • 下拉按钮被正确地向屏幕阅读器播报为控制菜单
  • ...可能还有更多我忘记的内容。

所有这些都无需在您自己的代码中编写字母 aria,也无需编写任何事件侦听器。您仍然可以完全控制设计!

这个组件有 超过 3000 行的测试。您不必自己做这些,是不是很棒?

这是一个完全样式化的实时演示(取自 Tailwind UI,以便您可以看到它的实际效果

请务必使用键盘或屏幕阅读器尝试一下,以真正体会它的价值!

我们刚刚标记了 v0.2.0,目前包括以下组件

  • 菜单按钮(或下拉菜单)
  • 列表框(或自定义选择框)
  • 开关(或切换开关)
  • ...还有更多组件正在路上。

要了解更多信息并深入了解,请访问 Headless UI 网站并阅读文档。


如果您在过去几年中关注过我的在线作品,您可能还记得我对 无渲染 UI 组件的痴迷——我在 2017 年底开始真正接触这个领域。我多年来一直希望有一个这样的库,但直到我们开始壮大团队,我们才拥有资源来实现它。

今年早些时候,我们聘请了 Robin Malfait,从那时起,他就一直在全职开发 Headless UI。

这个项目最大的动机是,我们非常希望将生产就绪的 JS 示例添加到 Tailwind UI 中,Tailwind UI 目前是一个仅 HTML、自带 JavaScript 的项目。这对许多希望完全控制一切工作方式的客户来说很棒,但对许多其他人来说,这是一个摩擦点。

我们不想在每个组件示例中添加 200 行令人讨厌的 JS 代码,因此我们开始开发 Headless UI,以此作为提取所有这些噪音的方法,而又不放弃实际 UI 设计中的任何灵活性。

为什么要重新发明轮子?

我们不是第一个尝试解决这个问题的人。Downshift 是我在 2017 年看到的第一个让我对这个想法感到兴奋的库,Reach UIReakit 在 2018 年开始开发,React Aria 最近发布,就在今年早些时候。

我们决定尝试我们自己解决这个问题的方法,原因如下

  • 现有的解决方案几乎完全专注于 React,我们希望将这些想法带到其他生态系统,例如 Vue、Alpine,并希望将来能扩展到更多。
  • 这些库将成为将 JS 支持添加到 Tailwind UI 的基础,并且由于这是维持业务运转的关键,因此对库的工作方式及其支持的内容拥有完全的决策权至关重要。
  • 我们对这些组件的 API 应该是什么样子有自己的想法,并希望能够自由地探索这些想法。
  • 我们希望确保始终可以使用 Tailwind 非常轻松地设置这些组件的样式,而不是必须编写自定义 CSS。

我们认为我们迄今为止提出的方案在灵活性和开发者体验之间取得了很好的平衡,我们很感激有其他人在研究类似的问题,我们可以从中学习并分享我们的想法。

下一步是什么

我们还有相当多的组件要为 Headless UI 开发,包括

  • 模态框
  • 单选按钮组
  • 选项卡
  • 手风琴
  • 组合框
  • 日期选择器

...以及可能更多。我们也将开始支持 Alpine.js,并且我们希望能够在今年年底前为 React、Vue 和 Alpine 标记 v1.0 版本。

在那之后,我们将开始探索其他框架,希望最终能够为 Svelte、Angular 和 Ember 等生态系统提供相同的工具,无论是作为一流的还是与社区合作伙伴合作。

如果您想了解我们的最新动态,请务必在 GitHub 上关注该项目

想讨论这篇文章吗?在 GitHub 上讨论 →

直接在您的收件箱中获取我们所有的更新。
注册我们的新闻通讯。

版权所有 © 2025 Tailwind Labs Inc.·商标政策