
在构建现代 Web 应用时,最大的痛点之一是构建自定义组件,如选择菜单、下拉菜单、切换按钮、模态框、标签页、单选组——这些组件在各个项目中都非常相似,但从来没有完全相同。
🌐 One of the biggest pain points when building modern web applications is building custom components like select menus, dropdowns, toggles, modals, tabs, radio groups — components that are pretty similar from project to project, but never quite the same.
你可以使用现成的组件包,但它们通常都紧密绑定自己的样式。这最终会让它们很难与你自己的项目外观和感觉匹配,而且几乎总是需要编写大量的 CSS 覆盖,这在使用 Tailwind CSS 时感觉像是倒退了一大步。
🌐 You could use an off-the-shelf package, but they usually come tightly coupled with their own provided styles. It ends up being very hard to get them to match the look and feel of your own project, and almost always involves writing a bunch of CSS overrides, which feels like a big step backwards when working Tailwind CSS.
另一种选择是从零开始构建你自己的组件。一开始看起来很简单,但随后你会想到还需要支持键盘导航、管理 ARIA 属性、焦点控制,结果一下子你就花了三到四周的时间去尝试构建一个真正稳固的下拉菜单。
🌐 The other option is building your own components from scratch. At first it seems easy, but then you remember you need to add support for keyboard navigation, managing ARIA attributes, focus trapping, and all of a sudden you're spending 3-4 weeks trying to build a truly bullet-proof dropdown menu.
我们认为有更好的选择,所以我们正在构建它。
🌐 We think there's a better option, so we're building it.
Headless UI 是一套完全无样式、可完全访问的 React 和 Vue UI 组件 (很快也将支持 Alpine.js),它让你可以轻松构建这类自定义组件,而无需自己处理任何复杂的实现细节,同时也不会牺牲使用简单工具类从零开始自定义样式的能力。
下面是使用 @headlessui/react 构建自定义下拉菜单的示例(库中包含的众多组件之一),支持完整的键盘导航和 ARIA 属性管理,并使用简单的 Tailwind CSS 工具进行样式设置:
🌐 Here's what it looks like to build a custom dropdown (one of many components the library includes) using @headlessui/react, with complete keyboard navigation support and ARIA attribute management, styled with simple Tailwind CSS utilities:
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> );}以下是该示例中免费提供的内容,无需你自己编写任何相关代码:
🌐 Here's what you're getting for free in that example, without having to write a single line of code related to it yourself:
- 点击、空格键、回车键或使用箭头键时,下拉面板会打开
- 按下 Esc 键或点击下拉菜单外部时,下拉菜单会关闭
- 你可以使用上下箭头键导航项目
- 你可以使用
Home键跳到第一个项目,使用End键跳到最后一个项目 - 使用键盘导航时,禁用的项目会自动跳过
- 使用键盘导航后,将鼠标悬停在某个项目上将切换到基于鼠标位置的聚焦。
- 使用键盘导航时,项目会正确地向屏幕阅读器朗读。
- 下拉按钮会正确地向屏幕阅读器显示为控制菜单
- ……可能还有更多我忘记的东西。
所有这一切都无需在自己的代码中写入 aria 字母,也无需编写任何事件监听器。而你仍然可以完全掌控设计!
🌐 All without writing the letters aria anywhere in your own code, and without writing a single event listener. And you still have complete control over the design!
这个组件有超过3000行的测试。你自己不需要去做这些测试,真是太好了,对吧?
🌐 There are over 3000 lines of tests for this component. Pretty nice that you didn't have to do that yourself, right?
这是一个完整样式的实时演示 (取自 Tailwind UI),让你可以看到它的实际效果:
🌐 Here's a fully-styled live demo (taken from Tailwind UI) so you can see it in action:
请务必使用键盘或屏幕阅读器进行尝试,以真正体会它的魅力!
🌐 Make sure to try it with the keyboard or a screen reader to really appreciate it!
我们刚刚标记了 v0.2.0 版本,目前包含以下组件:
🌐 We just tagged v0.2.0, which currently includes the following components:
要了解更多并深入学习,请访问 Headless UI 网站 并阅读文档。
🌐 To learn more and dive in, head over to the Headless UI website and read the documentation.
如果你过去几年一直在网上关注我的作品,你可能还记得我对无渲染 UI 组件的迷恋——这是我在2017 年底开始真正投入研究的东西。我一直希望有这样一个库存在,但在我们开始扩充团队之前,我们根本没有足够的资源去实现它。
🌐 If you've followed my work online for the last few years, you might remember my fascination with renderless UI components — something I was really started getting into towards the end of 2017. I've wanted a library like this to exist for years, but until we started growing the team we just didn't have the resources to make it happen.
今年早些时候,我们聘请了Robin Malfait,从那时起他就一直全职从事 Headless UI 的工作。
🌐 Earlier this year we hired Robin Malfait, and he's been working on Headless UI full-time ever since.
这个项目的最大动力是我们非常希望为 Tailwind UI 添加可投入生产的 JS 示例,而该项目目前仅支持 HTML,并且是那种需要自带 JavaScript 的类型。这对于那些希望完全掌控所有功能的客户来说非常好,但对于许多人来说,这也是一个阻力点。
🌐 The biggest motivation for this project is that we'd really like to add production-ready JS examples to Tailwind UI, which is currently an HTML-only, bring-your-own-JavaScript sort of project. This is great for lots of our customers who want full control over how everything works, but for many others it's a point of friction.
我们不想在每个组件示例中都添加 200 行复杂的 JavaScript,所以我们开始开发 Headless UI,作为一种将所有这些冗余代码“抽取”出来的方式,同时在实际的 UI 设计中仍然保持灵活性。
🌐 We didn't want to add 200 lines of gnarly JS to every component example, so we started working on Headless UI as a way to extract all of that noise, without giving up any flexibility in the actual UI design.
为什么要重新发明轮子?(Why reinvent the wheel?)
我们并不是第一个尝试解决这个问题的人。Downshift 是我在 2017 年看到的第一个让我对这个想法感到兴奋的库,Reach UI 和 Reakit 在 2018 年开始开发,而 React Aria 是最近刚发布的,就在今年早些时候。
🌐 We're not the first people to try and tackle this problem. Downshift was the first library I saw that got me excited about this idea back in 2017, Reach UI and Reakit started development in 2018, and React Aria was released most recently, just earlier this year.
我们决定尝试自己解决这个问题,原因如下:
🌐 We decided to try our own take on the problem for a few reasons:
- 现有的解决方案几乎完全集中在 React 上,我们希望将这些想法带到其他生态系统,例如 Vue、Alpine,并希望未来能扩展到更多生态系统。
- 这些库将成为向 Tailwind UI 添加 JS 支持的基础,而这正是业务持续运转的关键,因此,拥有对这些库如何工作以及它们支持哪些功能的完全决策权至关重要。
- 我们对于这些组件的 API 应该是什么样子有自己的想法,并且希望能够自由地探索这些想法。
- 我们希望确保使用 Tailwind 为这些组件设计样式始终非常容易,而无需编写自定义 CSS。
我们认为我们目前提出的方案在灵活性和开发者体验之间取得了良好的平衡,我们也非常感激有其他人在解决类似的问题,我们可以向他们学习并分享我们的想法。
🌐 We think what we've come up with so far hits a great balance between flexibility and developer experience, and we're grateful there are other people working on similar problems that we can learn from and share our ideas with.
下一步是什么(What's next)
我们还有很多组件需要为 Headless UI 开发,包括:
🌐 We've got quite a few more components to develop for Headless UI, including:
- 模态框
- 单选按钮组
- 标签页
- 手风琴
- 组合框
- 日期选择器
……可能还有更多。我们也即将开始支持 Alpine.js,并且希望能够在年底前为 React、Vue 和 Alpine 发布 v1.0 版本。
之后,我们将开始探索其他框架,希望最终能够为 Svelte、Angular 和 Ember 等生态系统提供相同的工具,无论是一流的还是与社区合作伙伴合作的。
🌐 After that we'll start exploring other frameworks, with the hope that we can eventually offer the same tools for ecosystems like Svelte, Angular, and Ember, either first-class or with community partners.
如果你想了解我们的最新动态,请务必在 GitHub 上关注该项目。
🌐 If you'd like to keep up with what we're doing, be sure to follow the project on GitHub.
想讨论这篇文章吗? 在 GitHub 上讨论 →