在构建现代 Web 应用程序时,最大的问题之一是构建自定义组件,例如选择菜单、下拉菜单、切换、模态框、选项卡、单选组——这些组件在项目之间非常相似,但从未完全相同。
您可以使用现成的包,但它们通常与自己提供的样式紧密耦合。这使得使它们与您自己的项目的外观和感觉相匹配变得非常困难,并且几乎总是需要编写一堆 CSS 覆盖,这在使用 Tailwind CSS 时感觉像是一个巨大的退步。
另一个选择是从头开始构建自己的组件。开始时看起来很简单,但随后您会记得您需要添加键盘导航支持、管理 ARIA 属性、聚焦限制,突然间您花费了 3-4 周的时间尝试构建一个真正强大的下拉菜单。
我们认为有更好的选择,因此我们正在构建它。
无样式的 UI 是一组完全无样式、完全可访问的 UI 组件,适用于 React 和 Vue (并即将支持 Alpine.js),使您能够轻松构建这些自定义组件,而无需担心任何复杂的实现细节,并且仍然能够使用简单的实用工具类从头开始进行样式设置。

以下是使用 @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> );}
以下是您在该示例中免费获得的功能,而无需自己编写任何相关代码:
- 下拉面板可通过点击、空格键、回车或使用箭头键打开
- 下拉菜单在按下 Esc 键或点击外部时关闭
- 您可以使用上箭头和下箭头键导航项目
- 您可以使用
Home
键跳转到第一个项目,使用End
键跳转到最后一个项目 - 在使用键盘导航时,禁用的项目会被自动跳过
- 在使用键盘导航后,使用鼠标悬停在项目上将切换到基于鼠标位置的焦点
- 使用键盘导航时,项目会正确传递给屏幕阅读器
- 下拉按钮被正确地传递给屏幕阅读器,作为控制菜单的元素
- ...还有很多我忘记的功能。
这一切都不需要在您自己的代码中写一字 aria
,也不需要写一个事件监听器。并且您仍然对设计有完全的控制权!
该组件有 超过 3000 行的测试。您不需要亲自完成这些测试,真不错吧?
这是一个完整样式的实时演示 (取自 Tailwind UI),让您看到它的实际效果:
确保尝试使用键盘或屏幕阅读器真正体验它!
我们刚刚发布了 v0.2.0,目前包括以下组件:
要了解更多并深入研究,请访问 无样式的 UI 网站 并阅读文档。
如果您在过去几年的工作中关注过我,您可能会记得我对 无渲染 UI 组件 的着迷——这是我在 2017 年底 开始真正探索的东西。我多年来一直希望有这样一个库的存在,但在我们开始扩展团队之前,我们没有足够的资源来实现它。
今年早些时候 我们聘请了 Robin Malfait,自那时起,他一直全职致力于无样式的 UI。
这个项目的最大动机是我们真的希望为 Tailwind UI 添加生产就绪的 JavaScript 示例,目前这只是一个仅支持 HTML 的项目,需自带 JavaScript。这对于许多希望全面控制一切工作方式的客户来说很好,但对许多其他客户来说,这是一个摩擦点。
我们不想在每个组件示例中添加 200 行复杂的 JavaScript,因此我们开始开发无样式的 UI,以便提取所有的噪音,而无需放弃实际 UI 设计中的灵活性。
为什么要重新发明轮子?
我们并不是第一个尝试解决这个问题的人。Downshift 是我在 2017 年首次看到的让我对这个想法感到兴奋的库,Reach UI 和 Reakit 于 2018 年开始开发,而 React Aria 最近发布,就在今年早些时候。
我们决定以自己的方式尝试解决这个问题,原因有几个:
- 现有解决方案几乎完全专注于 React,我们希望将这些想法带到其他生态系统,比如 Vue、Alpine,以及未来希望能支持更多生态系统。
- 这些库将成为为 Tailwind UI 添加 JavaScript 支持的基础,因为这维持着业务运转,因此我们觉得在库的工作原理和支持内容上拥有完整的决策权是很重要的。
- 我们对这些组件的 API 应该是什么样子有自己的想法,希望能够自由探索这些想法。
- 我们希望确保总是可以轻松使用 Tailwind 为这些组件进行样式设置,而不是必须编写自定义 CSS。
我们认为我们目前得到的结果在灵活性和开发者体验之间达到了良好的平衡,我们感激有其他人在处理类似的问题,我们可以从他们身上学习并分享我们的想法。
接下来是什么
我们还有相当多的组件需要为无样式的 UI 开发,包括:
- 模态框
- 单选组
- 选项卡
- 手风琴
- 组合框
- 日期选择器
...并可能还有更多。我们还即将开始支持 Alpine.js,并希望能在年底之前为 React、Vue 和 Alpine 发布 v1.0。
在此之后,我们将开始探索其他框架,希望最终能够为 Svelte、Angular 和 Ember 等生态系统提供相同的工具,无论是作为第一方支持还是与社区合作伙伴合作。
如果您想跟上我们的工作,请确保 关注 GitHub 上的项目。
想讨论这篇文章吗? 在 GitHub 上讨论 →