Tailwind UI 中的所有组件都是为最新版本的 Tailwind CSS 设计的,目前是 Tailwind CSS v4.0。要确保您使用的是最新版本的 Tailwind,请通过 npm 更新
npm install tailwindcss@latest
如果您是 Tailwind CSS 的新手,您还需要阅读 Tailwind CSS 文档,以便充分利用 Tailwind UI。
我们所有的 Tailwind UI 示例都使用了 Inter,因为它是一种漂亮的 UI 设计字体,并且完全开源且免费。使用自定义字体很好,因为它可以使组件在所有浏览器和操作系统上看起来都相同。
当然,您可以在自己的项目中使用任何字体,但是如果您想使用 Inter,最简单的方法是先通过 CDN 添加它
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
然后将“InterVariable”添加到您的 Tailwind 主题中的“sans”字体系列
@theme {
--font-sans: InterVariable, sans-serif;
}
如果您仍然使用 Tailwind CSS v3.x,则可以在您的 tailwind.config.js
文件中执行此操作
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['InterVariable', '...defaultTheme.fontFamily.sans'],
},
},
},
// ...
}
Tailwind UI 中的所有组件都以三种格式提供:React、Vue 和原生 HTML。
React 和 Vue 示例开箱即用,并且由 Headless UI 提供支持——这是一个我们设计的无样式组件库,可以与 Tailwind CSS 完美集成。
原生 HTML 示例不包含任何 JavaScript,专为喜欢自己编写任何必要 JavaScript 的人,或者希望与 React 或 Vue 以外的框架集成的人而设计。
绝大多数组件根本不需要 JavaScript,并且开箱即用,但像下拉列表、对话框等交互式组件需要您编写一些 JS 才能使它们按您期望的方式工作。
在这些情况下,我们在 HTML 中提供了一些简单的注释来解释诸如不同状态(例如,切换开关打开或关闭)需要使用哪些类,或者我们建议用于在屏幕上或屏幕外转换元素的类(例如,打开对话框)之类的内容。
我们尽最大努力确保 Tailwind UI 中的所有标记都尽可能可访问,但是当您构建交互式组件时,许多可访问性最佳实践只能通过 JavaScript 实现。
例如
aria-expanded="true"
,在切换开关打开时将 aria-checked
设置为 true,在导航自动完成中的选项时更新 aria-activedescendant
等)如果您将 Tailwind UI 与 React 或 Vue 一起使用,则所有这些复杂性都由 Headless UI 自动为您处理,但是如果您提供自己的 JS,在添加交互行为时,您有责任遵循可访问性最佳实践。
要了解有关构建可访问 UI 组件的更多信息,我们建议您学习 W3C 发布的 WAI-ARIA 创作实践。
当元素需要根据某种状态(例如,切换开关打开或关闭)应用不同的类时,我们在元素正上方的注释中列出每个状态的类
<!-- On: "bg-indigo-600", Off: "bg-gray-200" -->
<span aria-checked="false" class="bg-gray-200 relative inline-block shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:shadow-outline" role="checkbox" tabindex="0">
<!-- On: "translate-x-5", Off: "translate-x-0" -->
<span aria-hidden="true" class="translate-x-0 inline-block size-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
</span>
我们提供的 HTML 始终预配置为定义的状态之一,并且当切换状态时需要更改的类始终位于类列表的最开头,以便于查找。
例如,要使此 HTML 适应 Alpine.js,您可以根据在 x-data
中声明的某个状态,有条件地使用 :class
指令应用正确的类。
<span
x-data="{ isOn: false }"
@click="isOn = !isOn"
:aria-checked="isOn"
:class="{'bg-indigo-600': isOn, 'bg-gray-200': !isOn }"
class="bg-gray-200 relative inline-block shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:shadow-outline"
role="checkbox"
tabindex="0"
>
<span
aria-hidden="true"
:class="{'translate-x-5': isOn, 'translate-x-0': !isOn }"
class="translate-x-0 inline-block size-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"
></span>
</span>
我们在这里包含了一个基本的点击处理程序来演示一般的思路,但请在构建类似组件时参考 WAI-ARIA 创作实践,以确保您实现所有必要的键盘交互并正确管理任何需要的 ARIA 属性。
对于应该动态显示或隐藏的元素(例如下拉列表中的面板),我们在动态元素正上方的注释中包含了推荐的过渡样式。
<div class="relative ...">
<button type="button" class="...">
Options
</button>
<!--
Show/hide this element based on the dropdown state
Entering: "transition ease-out duration-100 transform"
From: "opacity-0 scale-95"
To: "opacity-100 scale-100"
Closing: "transition ease-in duration-75 transform"
From: "opacity-100 scale-100"
To: "opacity-0 scale-95"
-->
<div class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg">
<div class="rounded-md bg-white shadow-xs">
<!-- Snipped -->
</div>
</div>
</div>
例如,要使此 HTML 适应 Alpine.js,您可以使用 x-transition
指令在过渡生命周期的每个点应用正确的类。
<div x-data="{ isOpen: false }" class="relative ...">
<button type="button" @click="isOpen = !isOpen" class="...">
Options
</button>
<div
x-show="isOpen"
x-transition:enter="transition ease-out duration-100 transform"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75 transform"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg"
>
<div class="rounded-md bg-white shadow-xs">
<!-- Snipped -->
</div>
</div>
</div>
我们在这里包含了一个基本的点击处理程序来演示一般的思路,但请在构建类似组件时参考 WAI-ARIA 创作实践,以确保您实现所有必要的键盘交互并正确管理任何需要的 ARIA 属性。
由于 Tailwind UI 中包含的原始 HTML 示例无法利用循环之类的功能,因此在真实的项目中,HTML 从一些动态数据源生成时,实际上不会有大量的重复。例如,我们可能会给您一个包含 5 个列表项的列表组件,每个列表项上都重复了所有实用程序,而在您的项目中,您实际上将通过循环遍历数组来生成这些列表项。
当为自己的项目改编我们的示例时,我们建议根据需要创建可重用的模板局部或 JS 组件来管理任何重复。
在 Tailwind CSS 网站上的 “提取组件”文档中了解更多信息。
用于 React 的 Tailwind UI 依赖于 Headless UI 来驱动所有交互行为,并依赖 Heroicons 来提供图标,因此您需要将这两个库添加到您的项目中。
npm install @headlessui/react @heroicons/react
这些库和 Tailwind UI 本身都需要 React >= 16。.
所有 React 示例都以简单的单个组件形式提供,并且不假设您希望如何分解代码、希望公开哪些 prop API 或从哪里获取任何数据。
一些数据已被提取到基本的局部变量中,只是为了清理重复并使代码更易于阅读和理解,但我们已尽量少做,以避免强制执行任何不必要的僵化观点。
当您为自己的项目改编 Tailwind UI 中的代码时,您应该根据需要将示例分解为更小的组件,以实现项目所需的任何重用级别。
例如,您可能从这个堆叠列表组件开始
const people = [
{
name: 'Calvin Hawkins',
email: '[email protected]',
image:
'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
{
name: 'Kristen Ramos',
email: '[email protected]',
image:
'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
{
name: 'Ted Fox',
email: '[email protected]',
image:
'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
]
export default function Example() {
return (
<ul className="divide-y divide-gray-200">
{people.map((person) => (
<li key={person.email} className="py-4 flex">
<img className="size-10 rounded-full" src={person.image} alt="" />
<div className="ml-3">
<p className="text-sm font-medium text-gray-900">{person.name}</p>
<p className="text-sm text-gray-500">{person.email}</p>
</div>
</li>
))}
</ul>
)
}
在为自己的项目改编内容、将其分解为单独的组件并连接数据源之后,它可能看起来更像这样
function HockeyTeamItem({ team }) {
return (
<li className="py-4 flex">
<img className="size-10 rounded-full" src={team.logo} alt="" />
<div className="ml-3">
<p className="text-sm font-medium text-gray-900">{team.name}</p>
<p className="text-sm text-gray-500">{team.city}</p>
</div>
</li>
)
}
export default function HockeyTeamList({ teams }) {
return (
<ul className="divide-y divide-gray-200">
{teams.map((team) => <HockeyTeamItem key={team.id} team={team} />)}
</ul>
)
}
Tailwind UI 更像是一组蓝图、模式和想法,而不是一个僵化的 UI 工具包。最终得到的代码是您的,您可以随意进行分解。
用于 Vue 的 Tailwind UI 依赖于 Headless UI 来驱动所有交互行为,并依赖 Heroicons 来提供图标,因此您需要将这两个库添加到您的项目中。
npm install @headlessui/vue @heroicons/vue
这些库和 Tailwind UI 本身都需要 Vue 3+。我们目前不提供对 Vue 2 的支持。
所有 Vue 示例都以简单的单个组件形式提供,并且不假设您希望如何分解代码、希望公开哪些 prop API 或从哪里获取任何数据。
一些数据已被提取到基本的局部变量中,只是为了清理重复并使代码更易于阅读和理解,但我们已尽量少做,以避免强制执行任何不必要的僵化观点。
当您为自己的项目改编 Tailwind UI 中的代码时,您应该根据需要将示例分解为更小的组件,以实现项目所需的任何重用级别。
例如,您可能从这个堆叠列表组件开始
<template>
<ul class="divide-y divide-gray-200">
<li v-for="person in people" :key="person.email" class="py-4 flex">
<img class="size-10 rounded-full" :src="person.image" alt="" />
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">{{ person.name }}</p>
<p class="text-sm text-gray-500">{{ person.email }}</p>
</div>
</li>
</ul>
</template>
<script>
const people = [
{
name: 'Calvin Hawkins',
email: '[email protected]',
image:
'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
{
name: 'Kristen Ramos',
email: '[email protected]',
image:
'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
{
name: 'Ted Fox',
email: '[email protected]',
image:
'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
},
]
export default {
setup() {
return {
people,
}
},
}
</script>
在为自己的项目改编内容、将其分解为单独的组件并连接数据源之后,它可能看起来更像这样
<!-- HockeyTeamList.vue -->
<template>
<ul class="divide-y divide-gray-200">
<HockeyTeamItem v-for="team in teams" :key="team.id" :team="team"/>
</ul>
</template>
<script>
export default {
props: {
teams: Array
},
}
</script>
<!-- HockeyTeamListItem.vue -->
<template>
<li class="py-4 flex">
<img class="size-10 rounded-full" :src="team.logo" alt="" />
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">{{ team.name }}</p>
<p class="text-sm text-gray-500">{{ team.city }}</p>
</div>
</li>
</template>
<script>
export default {
props: {
team: Object
},
}
</script>
Tailwind UI 更像是一组蓝图、模式和想法,而不是一个僵化的 UI 工具包。最终得到的代码是您的,您可以随意进行分解。
我们在 Tailwind UI 中使用的所有图标都来自 Heroicons,这是一个免费的 MIT 许可图标集,我们在开始开发 Tailwind UI 时自己设计和开发的。
Tailwind UI 中的图片几乎全部来自 Unsplash。如果您需要可免费用于您的项目的摄影作品,这是一个很好的资源。
Tailwind UI 中的一些示例使用了来自 Lucid Illustrations 包的免费插图,该包由 Pixsellz 提供。您可以在 他们的网站上获取整套插图并查看他们的其他设计资源。
我们已停止提供 Figma 资源,以便我们可以专注于使用 Tailwind CSS 构建更多精彩示例。
我们过去为 Tailwind UI 提供 Figma 资源,但它们维护起来工作量非常巨大,而且很少有人使用它们。我们做出了非常艰难的决定,停止提供它们,以便我们可以将更多时间花在实际代码上,我们认为这可以提供最大的价值。
Tailwind UI 的客户可以下载我们发布的最终 Figma 文件,但请注意Figma 文件不会收到更新,并且不包含 2021 年 7 月 14 日之后发布的任何示例。