Headless UI v1.6,Tailwind UI 团队管理,Tailwind Play 改进,以及更多

Adam Wathan

距离我上次写我们正在做的工作已经有一段时间了,所以我有很多内容要分享!老实说太多了——我发布更新的主要动机是因为我们下周还有更多的内容要推出,我觉得在分享我们已经推出的所有内容之前,我不能分享那些内容。

所以穿上你的泳衣,坐在你的躺椅上,准备吸收一些维生素 CSS。


Headless UI v1.6 已发布

几周前,我们发布了 Headless UI 的新小版本,这是我们构建的未样式 UI 库,使其可以为 Tailwind UI 添加 React 和 Vue 支持。

查看 发布说明 获取所有细节,但这里有一些亮点。

多选支持

我们为 ComboboxListbox 组件添加了新的 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 文档 获取更多信息。

可为空的组合框

在 v1.6 之前,如果您删除组合框的内容并切换到其他地方,它将恢复之前选择的选项。这在许多情况下是有意义的,但有时您确实希望清除组合框的值。

我们添加了一个新的 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 表单支持

现在,如果您为 ListboxComboboxSwitchRadioGroup 等表单组件添加 name 属性,我们将自动创建一个隐藏输入,该输入与组件的值同步。

这使得通过常规表单提交或使用 Remix 中的 <Form> 组件将数据发送到服务器变得非常简单。

<form action="/projects/1/assignee" method="post">
<Listbox
value={selectedPerson}
onChange={setSelectedPerson}
name="assignee"
>
{/* ... */}
</Listbox>
<button>提交</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>付款成功</Dialog.Title>
{/* ... */}
</div>
</Dialog.Panel>
</Dialog>

查看 更新的对话框文档 以获取更多使用新面板组件而不是覆盖层的完整示例。

更好的焦点捕获

对话框是地球上构建的最难的东西之一,原因之一是焦点捕获。我们第一次尝试涉及劫持 Tab 键并手动聚焦下一个/上一个元素,以便我们可以在到达焦点捕获的末尾时循环回第一个项目。

这还好,直到人们开始在焦点捕获内部使用门户。现在基本上管理变得不可能,因为您可以 Tab 到一个日期选择器或类似的东西,这在概念上在对话框内部,但实际上并不是因为它是为了样式原因在门户中渲染的。

Robin 想出了一个 非常酷的解决方案,非常简单——不再试图手动控制 Tab 键的工作方式,只需在焦点捕获开始处抛出一个不可见的可聚焦元素,并在末尾再抛出一个。现在每当这些哨兵元素之一获得焦点时,您只需根据您是否在第一个元素或最后一个元素以及用户是向前 Tab 还是向后 Tab 来移动焦点到它应该去的地方。

采用这种方法,您根本不需要劫持 Tab 键——您只需让浏览器完成所有工作,仅在您的哨兵元素获得焦点时手动移动焦点。

在弄清楚这一点后,我们注意到其他几个库已经在做同样的事情,因此这并不是什么突破或新鲜事,但我觉得这非常聪明,值得分享给没有想过这种技术的人。


Tailwind UI 的团队管理特性

当我们第一次发布 Tailwind UI 时,"团队" 只是我和 Steve,所以如果我们想让这两个人能顺利推出这个项目,我们必须保持很多事情简单。

其中一件事情就是团队许可。我们在发布时没有任何华丽的团队成员邀请流程,我们只是要求人们与他们的团队分享 Tailwind UI 的凭据。这对我们来说已经足够好了,因为 Tailwind UI 并不以用户特定的方式进行任何操作,而且您团队的每个成员都获得相同的体验。

而且对我们来说,获取您团队每个人的电子邮件地址,将它们输入某个表单,向每个人发送邀请电子邮件,并让他们接受邀请感到像是行政地狱,尤其是在每个人在登录后获得相同的体验时。

但与此同时,分享任何事物的凭据感觉很低端,这并不是我们自豪的设计决定。我用的 Tailwind UI 密码(slayerfan1234)和我银行账户的密码是一样的——我不想与任何人分享这些!

因此几周前,我们决定搞定这件事情并构建一些东西。

带有可复制的邀请链接和团队成员列表的界面

我们最终确定的是一个纯链接的邀请系统,您只需复制您的邀请链接,与您的团队在 Slack/Discord/其他地方分享,并在需要时重置您的链接。您还可以给人们赋予“成员”或“拥有者”权限,这些权限控制他们是否可以管理团队成员或查看账单历史。

这使得邀请您的团队变得超级简单,而无需重复繁琐的数据输入,并且在需要时直接在 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 以通过这种方式暴露组件,但我们可能会很快做到这一点,这将使您可以大幅减少导入。


VS Code 的新 Tailwind CSS 语言模式

Tailwind 使用了一 bunch 非标准的 at-rules,比如 @tailwind@apply,因此如果您使用常规的 CSS 语言模式,您会在 VS Code 中收到 lint 警告。

为了绕过这个问题,我们一直建议人们使用 PostCSS 语言支持 插件,这样可以消除那些警告,但也会删除所有其他 CSS IntelliSense 支持。

因此几周前,我们作为我们 Tailwind CSS IntelliSense 扩展的一部分发布了一个第一方 Tailwind CSS 语言模式,它基于内置的 CSS 语言模式,添加了特定于 Tailwind 的语法高亮,并修复了您通常会看到的 lint 警告,而不会失去您希望保留的任何 CSS IntelliSense 功能。

示例 CSS 代码,当使用内置的 CSS 语言模式时会显示 lint 警告,而使用 Tailwind CSS 语言模式时没有 lint 警告。

通过下载最新版本的 Tailwind CSS IntelliSense 并选择“Tailwind CSS”作为您的 CSS 文件的语言模式来尝试一下。


Tailwind Play 中的 “生成的 CSS” 面板

在过去的几个月中,我们对 Tailwind Play 进行了一些小改进,我最喜欢的是新的“生成的 CSS”面板。

Tailwind Play 界面,带有显示该游乐场生成的 CSS 的面板。

它显示了从您的 HTML 生成的所有 CSS,并允许您按层过滤,这对故障排除非常有用。在内部,我们一直在使用这一功能来调试有关类未被检测到的问题,以便我们可以进行必要的 可怕的正则表达式修复

我们还为每个面板添加了一个“整理”按钮 (Cmd + S),该按钮将自动格式化您的代码(并对您的类进行排序!),还添加了一个“复制”按钮 (Cmd + A Cmd + C,但您已经知道了)。


重新设计 Refactoring UI 网站

当我们在 2018 年 12 月发布 Refactoring UI 时,Steve 和我实际上是在发布前的晚上大约凌晨 1 点设计并构建了最终的着陆页。

发生的事情是我们设计了整个性感的着陆页,然后我在写给我们邮件列表上每个人的公告电子邮件时,我们都觉得“这一邮件中的内容很棒,而且比我们在这个着陆页设计中拥有的内容要引人注目得多”。

但这些内容并不太适合我们设计的内容,所以在最后一刻,我们放弃了我们设计的一切,迅速制作了一个基于新内容的简单页面。它看起来 还不错,但不是我们真正想要的超级美丽的体验。

所以几周前,我们决定最终设计 一些新东西

重新设计的 Refactoring UI 网站的头部部分。

我仍然对这本书感到非常自豪——可能比我们所做的任何事情都要自豪。它在 Goodreads 上有 4.68 的评分,超过 1100 条评分和近 200 条评论,这对我来说感觉非常令人难以置信,因为这是一本自我出版的电子书。

期待有一天发行第二版,分享我们从中学到的所有内容!


Tailwind CSS 模板即将推出

我们在 Twitter 上对此做了一些预告,但在过去的几个月中,我们一直在努力制作一组完整的 Tailwind CSS 网站模板。

这里有一个它们的预览——一个使用 Next.js 和 Stripe 的新 Markdoc 库构建的文档网站模板:

包括移动和桌面布局以及浅色和深色配色方案的文档网站设计的画板。

我对推出这些内容感到不合理的兴奋。我为 Tailwind UI 作为一款产品感到非常自豪,但 可复制粘贴的代码片段 格式的一个局限是,我们无法真正向您展示如何组件化、最小化重复和构筑完整的生产就绪网站。

我们现在正在开发的模板将在填补这一差距方面表现出色。除了获得美观的模板作为您自己项目的起点外,您还可以深入研究代码,研究我们自己如何使用 Tailwind CSS 构建网站。

我们尚未确定这些模板的确切发布日期,但希望下个月收到一些内容。我们会在进展更新时分享更多信息!

Get all of our updates directly to your inbox.
Sign up for our newsletter.