Tailwind CSS 最佳实践
我总结了使用 Tailwind CSS 编写类名的一些规范,目的是为了更好维护类名、更高性能以及解决 Tailwind CSS 特定的问题。
尽量少用 @apply 来自定义类名
因为使用 @apply 自定义的类名,CSS Ouput 会输出重复的声明。
不推荐
.custom-cls {
@apply text-black py-2 text-sm
}
<div class="custom-cls"></div>
<div class="py-2">
<div class="text-black text-sm"></div>
</div>
CSS Output
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem
}
.text-black {
--tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity))
}
.custom-cls {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
font-size: 0.875rem;
line-height: 1.25rem;
--tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity))
}
推荐
<div class="py-2 text-sm text-black"></div>
<div class="py-2">
<div class="text-black text-sm"></div>
</div>
CSS Output
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem
}
.text-black {
--tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity))
}
复用样式
尽管 Tailwind CSS 官方提供复用样式方案, 但是更推荐使用 Class Variance Authority 实现复用。
- 减少样式冲突:CVA 提供了一种结构化的方式来管理样式变体,减少了全局样式冲突的可能性。
- 方便扩展: 当需要添加新的样式变体时,CVA 使得扩展变得更加方便和高效。只需在现有的样式中添加新的变体,而不需要大规模地修改现有的样式代码。
- 提高代码可维护性: CVA 通过明确地管理样式的变体,使得代码更加可读和可维护。
更多了解 Large Tailwind Components — What to do About All Those ...,这个视频详细具体地介绍了使用 CVA 的优点。
(推荐) 使用 Class Variance Authority
import { cva } from "class-variance-authority";
const button = cva(["font-semibold", "border", "rounded"], {
variants: {
intent: {
primary: [
"bg-blue-500",
"text-white",
"border-transparent",
"hover:bg-blue-600",
],
// **or**
// primary: "bg-blue-500 text-white border-transparent hover:bg-blue-600",
secondary: [
"bg-white",
"text-gray-800",
"border-gray-400",
"hover:bg-gray-100",
],
},
size: {
small: ["text-sm", "py-1", "px-2"],
medium: ["text-base", "py-2", "px-4"],
},
},
compoundVariants: [
{
intent: "primary",
size: "medium",
class: "uppercase",
// **or** if you're a React.js user, `className` may feel more consistent:
// className: "uppercase"
},
],
defaultVariants: {
intent: "primary",
size: "medium",
},
});
button();
// => "font-semibold border rounded bg-blue-500 text-white border-transparent hover:bg-blue-600 text-base py-2 px-4 uppercase"
button({ intent: "secondary", size: "small" });
// => "font-semibold border rounded bg-white text-gray-800 border-gray-400 hover:bg-gray-100 text-sm py-1 px-2"
尽量少用 Arbitrary values
在开发之前,最好确定设计规范。然后通过 tailwind.config.js 修改默认值或者新增类名。尽量避免使用 Arbitrary values
, 可能会导致 CSS Output 存在重复的样式。
<!-- bad -->
<div class="p-4">
<div class="p-[0.5rem] m-2"></div>
<div class="p-[16px] m-2"></div>
</div>
CSS Output
/* 编译后 */
.m-2 {
margin: 0.5rem
}
.p-4 {
padding: 1rem
}
.p-\[0\.5rem\] {
padding: 0.5rem
}
.p-\[16px\] {
padding: 16px
}
``
<!-- good -->
<div class="p-4">
<div class="p-4 m-2"></div>
<div class="p-4 m-2"></div>
</div>
CSS Output
/* 编译后 */
.m-2 {
margin: 0.5rem
}
.p-4 {
padding: 1rem
}
类名冲突方案
尽管 CVA 的 API 设计旨在帮助避免样式冲突,但仍存在很小的错误余地。请使用 tailwind-merge 包。
import { cva, type VariantProps } from "class-variance-authority";
import { twMerge } from "tailwind-merge";
const buttonVariants = cva(["your", "base", "classes"], {
variants: {
intent: {
primary: ["your", "primary", "classes"],
},
},
defaultVariants: {
intent: "primary",
},
});
export interface ButtonVariants extends VariantProps<typeof buttonVariants> {}
export const button = (variants: ButtonVariants) =>
twMerge(buttonVariants(variants));
tailwind-merge
tailwind-merge 的 twMerge 不是银弹。把 twMerge 当做最后的手段,实在没办法处理冲突类名的情况下使用。因为 twMerge 运行时逻辑计算,比单纯的类名合并更耗时。
尽量少写自定义样式
使用 Tailwind CSS 提供的类名就能完成大部分的样式。不需要在项目里自定义样式,
不推荐
<div class="chat-notification">
<div class="chat-notification-logo-wrapper">
<img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
</div>
<div class="chat-notification-content">
<h4 class="chat-notification-title">ChitChat</h4>
<p class="chat-notification-message">You have a new message!</p>
</div>
</div>
<style>
.chat-notification {
margin: 0 auto;
padding: 24px;
/* ... */
}
.chat-notification-logo-wrapper { /* ... */ }
.chat-notification-logo { /* ... */ }
.chat-notification-content { /* ... */ }
.chat-notification-title { /* ... */ }
.chat-notification-message { /* ... */ }
</style>
推荐
<div class="max-w-sm mx-auto p-6 flex items-center bg-white rounded-xl shadow-md space-x-4">
<div class="shrink-0">
<img class="h-12 w-12" src="/img/logo.svg" alt="ChitChat Logo">
</div>
<div class="chat-notification-content">
<h4 class="text-base sm:text-xl font-medium text-black">ChitChat</h4>
<p class="text-sm sm:text-base text-slate-500">You have a new message!</p>
</div>
</div>
使用 PostCSS 作为预编译器
使用 cx
使用 cx (CVA 的 API)用来拼接类名以及逻辑运算 (也可以使用 'clx'、'classnames'、tailwind-merge 的 'tw-join')
const button = cx(full && 'w-full', 'font-semibold border rounded bg-blue-500 text-white border-transparent hover:bg-blue-600 text-base py-2 px-4 uppercase')