自从我们发布 Tailwind CSS v3.0 已经大约六个月了,尽管从那时起我们一直在代码库中收集许多小改进,但我们还没有那个让你说“好吧,该发布了”的 那一个功能。
🌐 It's been about six months since we released Tailwind CSS v3.0, and even though we've been collecting a lot of little improvements in the codebase since then, we just didn't have that-one-feature yet that makes you say "okay, it's release-cuttin' time".
然后就在几周前的某个随机周六晚上,我在我们的 Discord 上和 Robin 聊天,讨论如何使用 :has 和文档中更深层的类来定位 html 元素,并解释了我认为如果我们添加对任意变体的支持会是什么样子——这是我想解决一年多的事情:
🌐 Then on a random Saturday night a couple of weeks ago, I was talking to Robin in our Discord about coming up with a way to target the html element using :has and a class deeper in the document, and explained how I thought it would look if we added support for arbitrary variants — something I've wanted to tackle for over a year:
![Adam Wathan: I think if we do arbitrary variants, the syntax should just be that exact thing, '[html:has(&)]:bg-blue-500'. Feel like that is pretty flexible, like anything you can do with a real variant you can also do with an arbitrary variant since they are the same thing. '[&>*:not(:first-child)]:pl-4'.
Robin: This is going to break my brain haha because '[html:has(&)]:bg-blue-500' would be used as a literal inside the '&'. That in combination with other variants... 🤯.
Adam Wathan: 😅 it'll be a brain melter for sure. The CSS would be this lol 'html:has([html:has(&)]:bg-blue-500 {"{"} background: blue 500 }'.
Robin: exactly haha. ok, now I want to try that brb.](/_next/static/media/discord-message.85a07f7f.png)
二十分钟后,Robin 就有了一个可运行的概念验证(仅用六行代码!),再经过大约一个小时 Jordan 在我们的类检测引擎中施展正则表达式的奇迹,任意变体 诞生了,我们拥有了可以发布的功能。
🌐 Twenty minutes later Robin had a working proof of concept (in six lines of code!), and after another hour or so of Jordan performing regex miracles in our class detection engine, arbitrary variants were born and we had our release-worthy feature.
终于来了——Tailwind CSS v3.1!想查看所有修复和改进的完整列表,请查看发行说明,以下是亮点:
🌐 So here it is — Tailwind CSS v3.1! For a complete list of every fix and improvement check out the release notes, but here's the highlights:
- 第一方 TypeScript 类型
- CLI 中对 CSS 导入的内置支持
- 使用主题功能时更改颜色不透明度
- 更简单的 CSS 变量颜色配置
- 边距间距工具
- 启用和可选变体
- 偏好对比变体
- 本地风格对话背景
- 任意值但适用于变体
通过从 npm 安装最新版本的 tailwindcss 来升级你的项目:
🌐 Upgrade your projects by installing the latest version of tailwindcss from npm:
npm install tailwindcss@latest或者启动一个 Tailwind Play,在浏览器中尝试所有的新功能。
🌐 Or spin up a Tailwind Play to play around with all of the new goodies right in the browser.
第一方 TypeScript 类型(First-party TypeScript types)
我们现在为你在使用 Tailwind 时使用的所有 JS API 提供类型,最显著的是 tailwind.config.js 文件。这意味着你可以获得各种有用的 IDE 支持,并且在修改配置时,不必频繁参考文档,会更容易一些。
🌐 We're now shipping types for all of our JS APIs you work with when using Tailwind, most notably the tailwind.config.js file. This means you get all sorts of useful IDE support, and makes it a lot easier to make changes to your configuration without referencing the documentation quite as much.
要进行设置,只需在配置定义上方添加类型注释即可:
🌐 To set it up, just add the type annotation above your config definition:
/** @type {import('tailwindcss').Config} */module.exports = { content: [ // ... ], theme: { extend: {}, }, plugins: [],};如果你是 TypeScript 的发烧友,你可能会喜欢研究一下实际的type 定义——里面有很多有趣的东西,用来支持这样一个可能非常复杂的对象。
🌐 If you're a big TypeScript nerd you might enjoy poking around the actual type definitions — lots of interesting stuff going on there to support such a potentially complex object.
CLI 中内置 CSS 导入支持(Built-in support for CSS imports in the CLI)
如果你正在使用我们的 CLI 工具来编译 CSS,postcss-import 现在已经内置,所以你可以将自定义 CSS 分成多个文件组织,而无需任何额外配置。
🌐 If you're using our CLI tool to compile your CSS, postcss-import is now baked right in so you can organize your custom CSS into multiple files without any additional configuration.
@import "tailwindcss/base";@import "./select2-theme.css";@import "tailwindcss/components";@import "tailwindcss/utilities";如果你没有使用我们的 CLI 工具,而是将 Tailwind 作为 PostCSS 插件使用,你仍然需要像安装 autoprefixer 一样自己安装和配置 postcss-import,但如果你使用的是我们的 CLI 工具,那么现在这一切将完全可以正常工作。
🌐 If you're not using our CLI tool and instead using Tailwind as a PostCSS plugin, you'll still need to install and configure postcss-import yourself just like you do with autoprefixer, but if you are using our CLI tool this will totally just work now.
如果你使用我们的独立 CLI并且完全不想安装任何 Node 依赖,这尤其方便。
🌐 This is especially handy if you're using our standalone CLI and don't want to install any node dependencies at all.
使用主题功能时更改颜色透明度(Change color opacity when using the theme function)
我觉得很多人可能都不知道这件事,但 Tailwind 向你的 CSS 文件提供了一个 theme() 函数,它让你可以从配置文件中获取值——有点像把它们变成可以重复使用的变量。
🌐 I don't think tons of people know about this, but Tailwind exposes a theme() function to your CSS files that lets you grab values from your config file — sort of turning them into variables that you can reuse.
.select2-dropdown { border-radius: theme(borderRadius.lg); background-color: theme(colors.gray.100); color: theme(colors.gray.900);}/* ... */不过有一个限制是,你无法调整以这种方式获取的颜色的 alpha 通道。所以在 v3.1 中,我们增加了使用斜杠语法来调整不透明度的支持,就像你可以用现代的 rgb 和 hsl CSS 颜色函数一样:
🌐 One limitation though was that you couldn't adjust the alpha channel any colors you grabbed this way. So in v3.1 we've added support for using a slash syntax to adjust the opacity, like you can with the modern rgb and hsl CSS color functions:
.select2-dropdown { border-radius: theme(borderRadius.lg); background-color: theme(colors.gray.100 / 50%); color: theme(colors.gray.900);}/* ... */我们也已经在你的 tailwind.config.js 文件中使用 theme 函数实现了这一功能:
🌐 We've made this work with the theme function in your tailwind.config.js file, too:
module.exports = { content: [ // ... ], theme: { extend: { colors: ({ theme }) => ({ primary: theme("colors.blue.500"), "primary-fade": theme("colors.blue.500 / 75%"), }), }, }, plugins: [],};你甚至可以将此功能设置为任意值,这非常疯狂 - 说实话,对于奇怪的自定义渐变和其他内容来说,它出奇地有用:
🌐 You can even use this stuff in arbitrary values which is pretty wild — honestly surprisingly useful for weird custom gradients and stuff:
<div class="bg-[image:linear-gradient(to_right,theme(colors.red.500)_75%,theme(colors.red.500/25%))]"> <!-- ... --></div>有什么方法可以避免编辑 CSS 文件吗?
🌐 Anything to avoid editing a CSS file am I right?
更简单的 CSS 变量颜色配置(Easier CSS variable color configuration)
如果你想将颜色定义并配置为 CSS 变量,你现在可能在你的 tailwind.config.js 文件中有一些糟糕的样板代码,如下所示:
🌐 If you like to define and configure your colors as CSS variables, you probably have some horrible boilerplate like this in your tailwind.config.js file right now:
function withOpacityValue(variable) { return ({ opacityValue }) => { if (opacityValue === undefined) { return `rgb(var(${variable}))`; } return `rgb(var(${variable}) / ${opacityValue})`; };}module.exports = { theme: { colors: { primary: withOpacityValue("--color-primary"), secondary: withOpacityValue("--color-secondary"), // ... }, },};在 v3.1 中,我们通过添加使用格式字符串定义颜色的支持(无需使用函数),使这种方式变得不那么糟糕:
🌐 We've made this way less awful in v3.1 by adding support for defining your colors with a format string instead of having to use a function:
module.exports = { theme: { colors: { primary: "rgb(var(--color-primary) / <alpha-value>)", secondary: "rgb(var(--color-secondary) / <alpha-value>)", // ... }, },};你不需要写一个接收 opacityValue 参数的函数,你可以直接写一个带有 <alpha-value> 占位符的字符串,Tailwind 会根据工具类将该占位符替换为正确的 alpha 值。
🌐 Instead of writing a function that receives that opacityValue argument, you can just write a string with an <alpha-value> placeholder, and Tailwind will replace that placeholder with the correct alpha value based on the utility.
如果你以前没有见过这些内容,请查看我们更新的使用 CSS 变量文档以获取更多详细信息。
🌐 If you haven't seen any of this before, check out our updated Using CSS variables documentation for more details.
边框间距工具(Border spacing utilities)
我们为 border-spacing 属性添加了一组新的工具,这样你就可以在使用分割边框时控制表格边框之间的间距:
🌐 We've added new set of utilities for the border-spacing property, so you can control the space between table borders when using separate borders:
| State | City |
|---|---|
| Indiana | Indianapolis |
| Ohio | Columbus |
| Michigan | Detroit |
<table class="border-separate border-spacing-2 ..."> <thead> <tr> <th class="border border-slate-300 ...">State</th> <th class="border border-slate-300 ...">City</th> </tr> </thead> <tbody> <tr> <td class="border border-slate-300 ...">Indiana</td> <td class="border border-slate-300 ...">Indianapolis</td> </tr> <!-- ... --> </tbody></table>我知道你在想什么——“我这辈子从没想过要做一张看起来像那样的桌子……”——但先听我说一秒钟!
🌐 I know what you're thinking — "I have never in my life wanted to build a table that looks like that..." — but listen for a second!
有一种情况尤其适用:当你构建一个带有固定标题行的表格,并且希望标题下方有一个固定的底部边框时,这个功能会非常有用:
🌐 One situation where this is actually super useful is when building a table with a sticky header row and you want a persistent bottom border under the headings:
滚动此表以查看固定表头的效果
| Name | Role | |
|---|---|---|
| Courtney Henry | courtney.henry@example.com | Admin |
| Tom Cook | tom.cook@example.com | Member |
| Whitney Francis | whitney.francis@example.com | Admin |
| Leonard Krasner | leonard.krasner@example.com | Owner |
| Floyd Miles | floyd.miles@example.com | Member |
| Emily Selman | emily.selman@example.com | Member |
| Kristin Watson | kristin.watson@example.com | Admin |
| Emma Dorsey | emma.dorsey@example.com | Member |
| Alicia Bell | alicia.bell@example.com | Admin |
| Jenny Wilson | jenny.wilson@example.com | Owner |
| Anna Roberts | anna.roberts@example.com | Member |
| Benjamin Russel | benjamin.russel@example.com | Member |
| Jeffrey Webb | jeffrey.webb@example.com | Admin |
| Kathryn Murphy | kathryn.murphy@example.com | Member |
<table class="border-separate border-spacing-0"> <thead class="bg-gray-50"> <tr> <th class="sticky top-0 z-10 border-b border-gray-300 ...">Name</th> <th class="sticky top-0 z-10 border-b border-gray-300 ...">Email</th> <th class="sticky top-0 z-10 border-b border-gray-300 ...">Role</th> </tr> </thead> <tbody class="bg-white"> <tr> <td class="border-b border-gray-200 ...">Courtney Henry</td> <td class="border-b border-gray-200 ...">courtney.henry@example.com</td> <td class="border-b border-gray-200 ...">Admin</td> </tr> <!-- ... --> </tbody></table>你可能会认为这里可以直接使用 border-collapse,因为你实际上不希望边框之间有任何间距,但你会错的。如果没有 border-separate 和 border-spacing-0,边框会随着滚动而滚走,而不会固定在标题下方。CSS 很有趣,不是吗?
🌐 You might think you could just use border-collapse here since you actually don't want any space between the borders but you'd be mistaken. Without border-separate and border-spacing-0, the border will scroll away instead of sticking under the headings. CSS is fun isn't it?
查看边框间距文档了解更多细节。
🌐 Check out the border spacing documentation for some more details.
已启用和可选的变体(Enabled and optional variants)
我们为 :enabled 和 :optional 伪类添加了新的变体,它们会在表单元素处于启用且可选状态时生效。
🌐 We've added new variants for the :enabled and :optional pseudo-classes, which target form elements when they are, well, enabled and optional.
“但是亚当,我为什么会需要这些,启用和可选甚至都不是状态,它们本来就是默认的。你到底会不会做网站?”
🌐 "But Adam why would I ever need these, enabled and optional aren't even states, they are the defaults. Do you even make websites?"
哎呀,这很伤人,因为这是真的 - 我现在几乎只是在 GitHub 上一遍又一遍地写电子邮件和回答同样的问题。
🌐 Ouch, that hurts because it's true — I pretty much just write emails and answer the same questions over and over again on GitHub now.
但请看一下这个已禁用按钮的示例:
🌐 But check out this disabled button example:
<button type="button" class="bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75 ..." disabled>Processing...</button>注意,当你将鼠标悬停在按钮上时,即使它被禁用,背景颜色仍会变化?在此版本发布之前,你通常会这样修复它:
🌐 Notice how when you hover over the button, the background still changes color even though it's disabled? Before this release, you'd usually fix that like this:
<button type="button" class="bg-indigo-500 hover:bg-indigo-400 disabled:opacity-75 disabled:hover:bg-indigo-500 ..." disabled> Processing...</button>但是使用新的 enabled 修饰符,你可以改成这样写:
🌐 But with the new enabled modifier, you can write it like this instead:
<button type="button" class="bg-indigo-500 hover:enabled:bg-indigo-400 disabled:opacity-75 ..." disabled> Processing...</button>与其在按钮禁用时将悬停颜色重置为默认颜色,我们结合使用 hover 和 enabled 变体,从一开始就不在按钮禁用时应用悬停样式。我觉得这样更好!
🌐 Instead of overriding the hover color back to the default color when the button is disabled, we combine the hover and enabled variants to just not apply the hover styles when the button is disabled in the first place. I think that's better!
这是一个示例,将新的 optional 修饰符与我们的 兄弟状态功能 结合使用,以隐藏对非必填字段的小“必填”提示:
🌐 Here's an example combining the new optional modifier with our sibling state features to hide a little "Required" notice for fields that aren't required:
<form> <div> <label for="email" ...>Email</label> <div> <input required class="peer ..." id="email" /> <div class="peer-optional:hidden ...">Required</div> </div> </div> <div> <label for="name" ...>Name</label> <div> <input class="peer ..." id="name" /> <div class="peer-optional:hidden ...">Required</div> </div> </div> <!-- ... --></form>这让你可以为所有表单组使用相同的标记,并让 CSS 为你处理所有的条件渲染,而不是你自己处理。挺不错的!
🌐 This lets you use the same markup for all of your form groups and letting CSS handle all of the conditional rendering for you instead of handling it yourself. Kinda neat!
Prefers-contrast 变体(Prefers-contrast variants)
你知道有一个 prefers-contrast 媒体查询吗?事实上是有的,而且现在 Tailwind 已经开箱即用地支持它了。
🌐 Did you know there's a prefers-contrast media query? Well there is, and now Tailwind supports it out of the box.
当用户请求更高或更低的对比度时,通常通过操作系统的辅助功能偏好设置(如 macOS 上的“增加对比度”),使用新的 contrast-more 和 contrast-less 变体来修改你的设计。
🌐 Use the new contrast-more and contrast-less variants to modify your design when the user has requested more or less contrast, usually through an operating system accessibility preference like "Increase contrast" on macOS.
尝试在你的开发者工具中模拟 `prefers-contrast: more` 以查看变化
<form> <label class="block"> <span class="block text-sm font-medium text-slate-700">Social Security Number</span> <input class="border-slate-200 placeholder-slate-400 contrast-more:border-slate-400 contrast-more:placeholder-slate-500" /> <p class="mt-2 text-sm text-slate-600 opacity-10 contrast-more:opacity-100">We need this to steal your identity.</p> </label></form>我为此写了一些文档,但老实说,我在这里写的比在那里写的还多。
🌐 I wrote some documentation for this but honestly I wrote more here than I did there.
设置原生对话框背景样式(Style native dialog backdrops)
有一个相当新的 HTML <dialog> 元素,其 浏览器支持 出乎意料地不错,如果你喜欢尝试最新技术,非常值得玩一玩。
🌐 There's a pretty new HTML <dialog> element with surprisingly decent browser support that is worth playing with if you like to live on the bleeding edge.
对话框有这个新的 ::backdrop 伪元素,它会在对话框打开时渲染,而 Tailwind CSS v3.1 增加了一个新的 backdrop 修饰符,你可以用它来为这个元素设置样式:
🌐 Dialogs have this new ::backdrop pseudo-element that's rendered while the dialog is open, and Tailwind CSS v3.1 adds a new backdrop modifier you can use to style this baby:
<dialog class="backdrop:bg-slate-900/50 ..."> <form method="dialog"> <!-- ... --> <button value="cancel">Cancel</button> <button>Submit</button> </form></dialog>如果你想更深入地了解这个内容,我建议阅读 MDN Dialog 文档 —— 这是很令人兴奋的内容,但需要了解的东西很多。
🌐 I recommend reading the MDN Dialog documentation if you want to dig in to this thing more — it's exciting stuff but there's a lot to know.
变体可设置任意值(Arbitrary values but for variants)
好吧,这个对我来说是真正的亮点——你知道我们给你提供addVariant API来创建你自己的自定义变体,对吧?
🌐 Okay so this one is the real highlight for me — you know how we give you the addVariant API for creating your own custom variants?
const plugin = require("tailwindcss/plugin");module.exports = { // ... plugins: [ plugin(function ({ addVariant }) { addVariant("third", "&:nth-child(3)"); }), ],};...你知道我们是如何在 HTML 中直接使用这个工具来使用任意值的 任意值 吗?
<!--] --><div class="top-[117px]"> <!-- ... --></div>好吧,Tailwind CSS v3.1 引入了任意变体,让你可以直接在 HTML 中创建自定义的临时变体:
🌐 Well Tailwind CSS v3.1 introduces arbitrary variants, letting you create your own ad hoc variants directly in your HTML:
<div class="[&:nth-child(3)]:py-0"> <!-- ... --></div>这对那些感觉需要参数化的变体非常有用,例如,如果浏览器支持特定的 CSS 特性,可以使用 @supports 查询来添加样式:
🌐 This is super useful for variants that sort of feel like they need to be parameterized, for example adding a style if the browser supports a specific CSS feature using a @supports query:
<div class="bg-white [@supports(backdrop-filter:blur(0))]:bg-white/50 [@supports(backdrop-filter:blur(0))]:backdrop-blur"> <!-- ... --></div>你甚至可以使用此功能针对具有任意变体(如 [&>*])的子元素:
🌐 You can even use this feature to target child elements with arbitrary variants like [&>*]:
Kristen Ramos
kristen.ramos@example.com
Floyd Miles
floyd.miles@example.com
Courtney Henry
courtney.henry@example.com
<ul role="list" class="space-y-4 [&>*]:rounded-lg [&>*]:bg-white [&>*]:p-4 [&>*]:shadow"> <li class="flex"> <img class="h-10 w-10 rounded-full" src="..." alt="" /> <div class="ml-3 overflow-hidden"> <p class="text-sm font-medium text-slate-900">Kristen Ramos</p> <p class="truncate text-sm text-slate-500">kristen.ramos@example.com</p> </div> </li> <!-- ... --></ul>你甚至可以在第二个子 li 中的 div 内为第一个 p 设置样式,但只能在 hover 上进行:
🌐 You can even style the first p inside the div in the second child li but only on hover:
试着将鼠标悬停在“Floyd Miles”文字上
Kristen Ramos
kristen.ramos@example.com
Floyd Miles
floyd.miles@example.com
Courtney Henry
courtney.henry@example.com
<ul role="list" class="space-y-4 [&>*]:rounded-lg [&>*]:bg-white [&>*]:p-4 [&>*]:shadow hover:[&>li:nth-child(2)>div>p:first-child]:text-indigo-500"> <!-- ... --> <li class="flex"> <img class="h-10 w-10 rounded-full" src="..." alt="" /> <div class="ml-3 overflow-hidden"> <p class="text-sm font-medium text-slate-900">Floyd Miles</p> <p class="truncate text-sm text-slate-500">floyd.miles@example.com</p> </div> </li> <!-- ... --></ul>那么你应该现在这样做吗?可能不会经常,但说实话,当你试图为无法直接更改的 HTML 进行样式设置时,它确实可以是一个相当有用的逃生出口。这是一把锋利的刀,但最好的厨师可不是用安全剪刀来准备食物的。
🌐 Now should you do this? Probably not very often, but honestly it can be a pretty useful escape hatch when trying to style HTML you can't directly change. It's a sharp knife, but the best chefs aren't preparing food with safety scissors.
稍微玩一玩它们,我敢打赌你会发现它们在需要的时候是很有用的工具。我们在正在制作的新网站模板中的几个棘手的地方使用了它们,体验比创建自定义类要好得多。
🌐 Play with them a bit and I'll bet you find they are a great tool when the situation calls for it. We're using them in a couple of tricky spots in these new website templates we're working on and the experience is much nicer than creating a custom class.
这就是 Tailwind CSS v3.1!这只是一个小版本更新,因此没有重大更改,你应该可以通过安装最新版本来更新你的项目:
🌐 So that's Tailwind CSS v3.1! It's only a minor version change, so there are no breaking changes and you should be able to update your project by just installing the latest version:
npm install tailwindcss@latest有关包括错误修复和一些我这里没有提到的小改进在内的完整更改列表,请查阅 GitHub 上的发行说明。
🌐 For the complete list of changes including bug fixes and a few minor improvements I didn't talk about here, dig in to the release notes on GitHub.
我已经对 Tailwind CSS v3.2 有了一堆想法(也许甚至终于有文本阴影了?!),但现在我们正努力推进这些新的网站模板 到完成阶段。请在接下来的一两周内关注该话题的更新!
🌐 I've already got a bunch of ideas for Tailwind CSS v3.2 (maybe even text shadows finally?!), but right now we're working hard to push these new website templates over the finish line. Look for another update on that topic in the next week or two!