已经有一段时间没有写过我们在做什么了,所以我有很多要分享!说实话太多了 — 促使我发布这个更新的主要原因是我们下周还有更多内容要发布,而在分享那些内容之前,我觉得我必须先分享我们已经发布的内容。
所以穿上你的泳装,坐在躺椅上,准备沐浴一下 CSS 的阳光吧。
Headless UI v1.6 已发布
几周前我们发布了 Headless UI 的一个新次要版本,这是我们构建的无样式 UI 库,使得可以为 Tailwind UI 添加 React 和 Vue 支持。
查看发布说明了解所有细节,以下是一些亮点。
多选支持
我们为 Combobox 和 Listbox 组件都添加了新的 multiple 属性,这样人们就可以选择多个选项。
只需添加 multiple 属性并绑定一个数组作为你的 value 就可以开始使用了:
function MyCombobox({ items }) { const [selectedItems, setSelectedItems] = useState([]); return ( <Combobox value={selectedItems} onChange={setSelectedItems} multiple> {selectedItems.length > 0 && ( <ul> {selectedItems.map((item) => ( <li key={item}>{item}</li> ))} </ul> )} <Combobox.Input /> <Combobox.Options> {items.map((item) => ( <Combobox.Option key={item} value={item}> {item} </Combobox.Option> ))} </Combobox.Options> </Combobox> );}查看 combobox 文档 和 listbox 文档 了解更多。
可为空的 combobox
在 v1.6 之前,如果你删除了 combobox 的内容并切换到其他地方,它会恢复之前选择的选项。这在很多时候是有意义的,但有时你确实想要清除 combobox 的值。
我们添加了一个新的 nullable 属性,使得这成为可能 — 只需添加该属性,现在你可以删除值而不会恢复之前的值:
function MyCombobox({ items }) { const [selectedItem, setSelectedItem] = useState([]); return ( <Combobox value={selectedItem} onChange={setSelectedItem} nullable> <Combobox.Input /> <Combobox.Options> {items.map((item) => ( <Combobox.Option key={item} value={item}> {item} </Combobox.Option> ))} </Combobox.Options> </Combobox> );}简单的 HTML 表单支持
现在,如果你为 Listbox、Combobox、Switch 和 RadioGroup 等表单组件添加 name 属性,我们会自动创建一个与组件值同步的隐藏输入。
这使得通过常规表单提交或使用类似 Remix 中的 <Form> 组件发送数据变得超级简单。
<form action="/projects/1/assignee" method="post"> <Listbox value={selectedPerson} onChange={setSelectedPerson} name="assignee" > {/* ... */} </Listbox> <button>Submit</button></form>这适用于简单的值,如数字和字符串,也适用于对象 — 我们会自动将它们序列化为多个字段,使用 1996 年的方括号表示法:
<input type="hidden" name="assignee[id]" value="1" /><input type="hidden" name="assignee[name]" value="Durward Reynolds" />查看 文档 如果你想阅读我刚刚写的内容,但在不同的域名上。
可滚动对话框改进
对话框是地球上最难构建的东西。我们已经与一些棘手的 滚动 问题 斗争了一段时间,并认为我们终于在 v1.6 中解决了所有问题。
关键在于我们改变了“点击外部关闭”的工作方式。我们过去使用这个 Dialog.Overlay 组件,你把它放在实际对话框的后面,我们在上面有一个点击处理程序,点击时会关闭对话框。我实际上非常喜欢这种方法的简单性 — 检测特定元素何时被点击比检测何时点击特定元素以外的任何东西要少很多怪癖,特别是当你在对话框中渲染一些本身在门户中渲染其他东西的东西时。
这种方法的问题在于,如果你有一个需要滚动的长对话框,你的覆盖层会位于滚动条的上方,尝试点击滚动条会关闭对话框。这不是你想要的!
因此,为了以非破坏性的方式解决这个问题,我们添加了一个新的 Dialog.Panel 组件,你可以使用它,现在我们在你点击该组件外部的任何地方时关闭对话框,而不是在点击覆盖层时关闭对话框:
<Dialog open={isOpen} onClose={closeModal} className="fixed inset-0 flex items-center justify-center ..."> <Dialog.Overlay className="fixed inset-0 bg-black/25" /> <div className="fixed inset-0 bg-black/25" /> <div className="bg-white shadow-xl rounded-2xl ..."> <Dialog.Panel className="bg-white shadow-xl rounded-2xl ..."> <Dialog.Title>Payment successful</Dialog.Title> {/* ... */} </div> </Dialog.Panel></Dialog>查看 更新的对话框文档 了解更多使用新面板组件而不是覆盖层的完整示例。
更好的焦点捕获
对话框是地球上最难构建的东西的众多原因之一是焦点捕获。我们第一次尝试这个涉及劫持 tab 键并手动聚焦下一个/上一个元素,这样当你到达焦点捕获的末尾时,我们可以循环回到焦点捕获中的第一个项目。
这在人们开始在焦点捕获中使用门户之前效果还不错。现在基本上不可能管理,因为你可以 tab 到一个日期选择器或其他概念上在对话框内的东西,但实际上不是,因为它是为了样式原因在门户中渲染的。
Robin 想出了一个 非常酷的解决方案,非常简单 — 而不是尝试手动控制 tab 键的工作方式,只需在焦点捕获的开头和结尾放置一个不可见的可聚焦元素。现在,每当其中一个哨兵元素接收到焦点时,你只需根据你是在第一个元素还是最后一个元素以及用户是向前还是向后 tab,将焦点移动到实际应该的位置。
使用这种方法,你不必劫持 tab 键 — 你只需让浏览器完成所有工作,只有当你的哨兵元素之一接收到焦点时才手动移动焦点。
在弄清楚这一点后,我们注意到其他几个库也在做同样的事情,所以这并不是突破性的或新的,但我认为这非常聪明,值得分享给任何没有想到这种技术的人。
Tailwind UI 的团队管理功能
当我们首次发布 Tailwind UI 时,“团队”只是我和 Steve,所以如果我们希望仅靠我们两个人完成这个项目,我们必须保持很多事情的简单。
其中一件事是团队许可。我们没有提供任何花哨的团队成员邀请流程,我们只是要求人们与他们的团队共享他们的 Tailwind UI 凭据。这对我们来说足够了,因为 Tailwind UI 并没有以用户特定的方式做任何事情,你的团队中的每个成员都会获得相同的体验。
此外,对我们来说,获取你团队中每个人的电子邮件地址,将它们输入某个表单,向每个人发送邀请电子邮件,并让他们接受邀请感觉像是行政地狱,特别是当每个人在登录后都获得相同的体验时。
但同时,共享任何东西的凭据都是非常低端的,这不是我们引以为豪的设计决策。我使用与我的银行账户相同的密码 (slayerfan1234) 用于 Tailwind UI — 我不想与任何人分享!
所以几周前我们决定解决这个问题并构建一些东西。
我们最终选择了一个纯粹基于链接的邀请系统,你可以只复制你的邀请链接,在 Slack/Discord/其他地方与你的团队分享,并在需要时重置你的链接。你还可以给人们“成员”或“所有者”权限,这些权限控制他们是否可以管理团队成员或查看账单历史。
这使得邀请你的团队变得超级简单,而不需要一堆繁琐的数据输入,并且如果有人离开,可以在 UI 中撤销访问权限,而不是通过更改共享密码。
这现在对任何拥有 Tailwind UI 团队账户的人都可用 — 只需打开下拉菜单并点击“我的团队”来命名你的团队并开始邀请你的同事。
你可以在 Tailwind UI 网站上 购买团队许可证,或者如果你有个人许可证并希望开始与团队一起使用 Tailwind UI,可以 升级到团队许可证。
将 Tailwind UI 中的 Vue 示例更新为 <script setup>
自从为 Tailwind UI 发布 Vue 支持以来,Vue 3 中的新 <script setup> 语法已成为编写单文件组件的推荐方式。
我们已将 Tailwind UI 中的所有 Vue 示例更新为使用这种新格式,这消除了大量样板代码:
<template> <Listbox as="div" v-model="selected"> <!-- ... --> </Listbox></template><script setup> import { ref } from "vue"; import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from "@headlessui/vue"; import { CheckIcon, SelectorIcon } from "@heroicons/vue/solid"; const people = [ { id: 1, name: "Wade Cooper" }, // ... ]; const selected = ref(people[3]);</script>对我来说,最好的部分是你不再需要在 components 下显式注册任何东西 — 任何在范围内的组件都可以自动用于模板。
使用 <script setup> 还可以让你使用 命名空间组件 如 Listbox.Button,就像我们在 Headless UI 的 React 版本中所做的那样。我们还没有更新 Headless UI 以这种方式公开组件,但我们可能很快就会这样做,这将让你减少大量导入。
新的 Tailwind CSS 语言模式用于 VS Code
Tailwind 使用了一些非标准的 at-rules,如 @tailwind 和 @apply,所以如果你使用常规的 CSS 语言模式,你会在 VS Code 中看到 lint 警告。
为了解决这个问题,我们一直建议人们使用 PostCSS Language Support 插件,它消除了这些警告,但也删除了所有其他 CSS IntelliSense 支持。
所以几周前,我们发布了一个第一方 Tailwind CSS 语言模式,作为我们 Tailwind CSS IntelliSense 扩展的一部分,它基于内置的 CSS 语言模式,添加了 Tailwind 特定的语法高亮显示,并修复了你通常会看到的 lint 警告,而不会失去你想要保留的任何 CSS IntelliSense 功能。
通过下载最新版本的 Tailwind CSS IntelliSense 并选择“Tailwind CSS”作为 CSS 文件的语言模式来试用。
Tailwind Play 中的“生成的 CSS”面板
在过去的几个月里,我们对 Tailwind Play 进行了许多小改进,我最喜欢的是新的“生成的 CSS”面板。
它向你显示了从你的 HTML 生成的所有 CSS,并允许你按层过滤,这对于故障排除非常有用。在内部,我们一直在使用这个来调试类未被检测到的奇怪问题,以便我们可以执行任何 可怕的正则表达式手术 使其工作。
我们还在每个窗格中添加了一个“整理”按钮 (Cmd + S),它会自动格式化你的代码(并排序你的类!),以及一个“复制”按钮 (Cmd + A Cmd + C,但你已经知道了)。
重新设计 Refactoring UI 网站
当我们在 2018 年 12 月发布 Refactoring UI 时,Steve 和我在发布前一晚大约凌晨 1 点设计并构建了最终的登陆页面。
发生的事情是,我们设计了一个非常性感的登陆页面,然后我在写给我们邮件列表上的每个人发送的公告电子邮件时,我们都认为“这个电子邮件中的内容很棒,比我们在这个登陆页面设计中拥有的内容更有说服力”。
但这些内容并不真正适合我们设计的内容,所以在最后一刻,我们放弃了我们设计的一切,根据新内容拼凑了一个更简单的页面。它看起来还不错,但不是我们真正想要的超级美丽的体验。
所以几周前,我们决定最终设计 一些新的东西。
我仍然对这本书感到非常自豪 — 可能比我们制作的任何东西都更自豪。它在 Goodreads 上有 4.68 的评分,超过 1100 个评分和近 200 条评论,对于一本自出版的电子书来说,这让我感到非常不可思议。
期待有一天能用我们学到的所有东西做第二版!
Tailwind CSS 模板即将推出
我们在 Twitter 上 预告了一下,但在过去的几个月里,我们一直在努力制作一堆完整的 Tailwind CSS 网站模板。
这是其中一个的预览 — 一个使用 Next.js 和 Stripe 的新 Markdoc 库构建的文档站点模板:
我对这些模板的发布感到非常兴奋。我对 Tailwind UI 作为一个产品感到非常自豪,但 可复制和粘贴的代码片段 格式的一个限制是,我们没有机会真正向你展示如何组件化、最小化重复以及将其构建为一个完整的、生产就绪的网站。
我们现在正在制作的模板将非常适合填补这一空白。除了获得美丽的模板作为你自己项目的起点,你还可以深入研究代码,研究我们自己如何使用 Tailwind CSS 构建网站。
我们还没有确定这些模板的确切发布日期,但我们希望下个月能发布一些内容。随着我们取得更多进展,我们会分享更多信息!