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

日期

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

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

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

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

无头 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="px-4 py-2 rounded bg-blue-600 text-white ...">Options</Menu.Button>
      <Menu.Items className="absolute mt-1 right-0">
        <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 上讨论 →