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>  );}

在那个例子中,你无需编写一行与它相关的代码就可以免费获得以下内容

  • 下拉面板在单击、空格键、回车键或使用箭头键时打开
  • 当您按下 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 上讨论 →

将我们的所有更新直接发送到您的收件箱。
注册我们的新闻通讯。