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

Adam Wathan

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

您可以使用现成的软件包,但它们通常与自己提供的样式紧密耦合。想要让它们与您自己的项目的外观和风格相匹配变得非常困难,而且几乎总是需要编写大量的 CSS 覆盖样式,这在使用 Tailwind CSS 时感觉像是一大倒退。

另一个选择是从头开始构建您自己的组件。一开始看起来很容易,但后来您会记起需要添加键盘导航支持、管理 ARIA 属性、焦点陷阱等功能,突然间您花了 3-4 周时间试图构建一个真正坚固的下拉菜单。

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

Headless UI 是一组完全无样式、完全可访问的 UI 组件,适用于 React 和 Vue以及即将支持 Alpine.js),它让您可以轻松构建这些自定义组件,而无需自己担心任何复杂的实现细节,同时不会牺牲使用简单实用类从头开始设计它们的能力。

Headless UI 标志

以下是使用 @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 ...">选项</Menu.Button>      <Menu.Items className="absolute right-0 mt-1">        <Menu.Item>          {({ active }) => (            <a className={`${active && "bg-blue-500 text-white"} ...`} href="/account-settings">              账户设置            </a>          )}        </Menu.Item>        <Menu.Item>          {({ active }) => (            <a className={`${active && "bg-blue-500 text-white"} ...`} href="/documentation">              文档            </a>          )}        </Menu.Item>        <Menu.Item disabled>          <span className="opacity-75 ...">邀请好友(即将推出!)</span>        </Menu.Item>      </Menu.Items>    </Menu>  );}

在这个示例中,您免费获得以下功能,而无需自己编写任何相关代码:

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

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

这个组件有超过 3000 行测试代码。很高兴您不必自己编写这些,对吧?

以下是一个完整样式的实时演示(来自 Tailwind UI),让您可以亲自体验:

请务必使用键盘或屏幕阅读器来真正体验它!

我们刚刚发布了 v0.2.0,目前包括以下组件:

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


如果您在过去几年中关注过我在网上的工作,您可能还记得我对无渲染 UI 组件的迷恋 — 这是我在2017 年底开始真正深入研究的东西。我希望这样的库已经存在多年,但直到我们开始扩大团队,我们才有资源实现它。

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

这个项目的最大动机是我们真的希望为 Tailwind UI 添加生产就绪的 JS 示例,目前这是一个仅限 HTML 的项目,您需要自带 JavaScript。这对许多希望完全控制工作方式的客户来说非常好,但对许多其他客户来说,这是一个摩擦点。

我们不想在每个组件示例中添加 200 行复杂的 JS,因此我们开始开发 Headless UI 作为一种_提取_所有这些噪音的方法,而不会在实际的 UI 设计中放弃任何灵活性。

为什么要重新发明轮子?

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

我们决定尝试自己解决这个问题,原因有几个:

  • 现有解决方案几乎完全专注于 React,而我们希望将这些想法带到其他生态系统,如 Vue、Alpine,并希望将来更多。
  • 这些库将成为为 Tailwind UI 添加 JS 支持的基础,由于这是业务运行的关键,因此完全掌控这些库的工作方式和支持内容显得尤为重要。
  • 我们对这些组件的 API 有自己的想法,并希望能够自由探索这些想法。
  • 我们希望确保始终可以轻松地使用 Tailwind 对这些组件进行样式设置,而无需编写自定义 CSS。

我们认为到目前为止,我们的成果在灵活性和开发者体验之间取得了很好的平衡,我们很感激有其他人在解决类似问题,我们可以向他们学习并分享我们的想法。

接下来是什么

我们还有很多组件要为 Headless UI 开发,包括:

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

...可能还有更多。我们还即将开始支持 Alpine.js,并希望能够在年底前为 React、Vue 和 Alpine 标记 v1.0。

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

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

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

Get all of our updates directly to your inbox.
Sign up for our newsletter.