这是用户在 2024-5-20 10:41 为 https://evilmartians.com/chronicles/5-best-practices-for-preventing-chaos-in-tailwind-css 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?

5 best practices for preventing chaos in Tailwind CSS
防止 Tailwind CSS 中出现混乱的 5 个最佳实践

Cover for 5 best practices for preventing chaos in Tailwind CSS

Topics 主题

Share this post on 分享这篇文章


Translations

If you’re interested in translating or adapting this post, please contact us first.
如果您有兴趣翻译或改编这篇文章,请先联系我们。

Working with Tailwind CSS is pretty fast and easy (that’s why it’s received such wide recognition). You just paste a list of different classes in your HTML—and your interface immediately becomes attractive! But, as the application grows, the lists of classes grow. Then, one day you realize you can’t understand your code, you’re confused with the structure of the application and magic variables, and work becomes a struggle. This article is all about avoiding this scenario, sharing some best practices to ensure you stay aloft when using Tailwind CSS.
使用 Tailwind CSS 非常快速且简单(这就是它获得如此广泛认可的原因)。您只需将不同类的列表粘贴到 HTML 中,您的界面就会立即变得有吸引力!但是,随着应用程序的增长,类列表也会增长。然后,有一天你意识到你无法理解你的代码,你对应用程序的结构和神奇的变量感到困惑,工作变成了一场斗争。本文旨在避免这种情况,分享一些最佳实践,以确保您在使用 Tailwind CSS 时保持领先。

We can prevent any headaches and resolve any problems (for the most part) by using Tailwind accurately and wisely. But, there are two requirements your project must met, and if it does not, Tailwind can make your job very difficult instead.
通过准确而明智地使用 Tailwind,我们可以防止任何头痛并解决任何问题(在大多数情况下)。但是,您的项目必须满足两个要求,如果不满足,Tailwind 可能会让您的工作变得非常困难。

First, you should have a design system in your project. Tailwind’s philosophy goes alongside the design system where designers and developers use consistent design tokens. Design tokens are atomic values (like colors, spacing, or typography scales) that define a design’s properties and that are reused throughout the project.
首先,你的项目中应该有一个设计系统。 Tailwind 的理念与设计系统相一致,设计师和开发人员使用一致的设计令牌。设计标记是原子值(如颜色、间距或版式比例),它们定义设计的属性并在整个项目中重复使用。

Let’s imagine that we have a standard button and some tabs that need to be the same color as that button:
假设我们有一个标准按钮和一些需要与该按钮颜色相同的选项卡:

.button {
  background-color: oklch(45% 0.2 270);
}

.tab {
  background-color: oklch(45% 0.2 270);
}

If we decide to change the color scheme of the project a little, we’ll need to find every instance of this color (which looks like a magic variable) and update them everywhere. This can be inconsistent and harder to maintain.
如果我们决定稍微改变项目的配色方案,我们需要找到这种颜色的每个实例(看起来像一个神奇的变量)并在各处更新它们。这可能会不一致并且更难以维护。

Design tokens help prevent these problems and ensure uniformity across UI elements.
设计令牌有助于防止这些问题并确保 UI 元素之间的一致性。

Luckily, to implement design tokens, we only need to to define the tokens in tailwind.config.js:
幸运的是,要实现设计令牌,我们只需要在 tailwind.config.js 中定义令牌:

module.exports = {
  theme: {
    colors: {
      primary: 'oklch(45% 0.2 270)'
    }
  }
}

After adding a new color with the name primary, we can use bg-primary for our background color or text-primary for the text color throughout the application:
添加名称为 primary 的新颜色后,我们可以在整个应用程序中使用 bg-primary 作为背景颜色或 text-primary 作为文本颜色:

<button class="bg-primary">Standard button</button>
<div class="bg-primary">First tab</div>

This way, when you want to change the color scheme in the project, you only need to replace the color in one place: tailwind.config.js.
这样,当你想改变项目中的配色方案时,只需要替换一处的颜色即可: tailwind.config.js

It’s better to avoid using Tailwind if you haven’t considered a design system because you’ll have to write magic values in the class lists (like 'p-[123px] mb-[11px] gap-[3px]') or add a lot of new tokens (15px, 16px, 17px in the spacing config), and this will eventually bring a lot of mess to your code.
如果您没有考虑过设计系统,最好避免使用 Tailwind,因为您必须在类列表中写入魔法值(如 'p-[123px] mb-[11px] gap-[3px]' )或添加大量新标记( 15px16px17px 在间距配置中),这最终会给你的代码带来很多混乱。

Having a consistent design system is good because it can help the development and design teams understand each other better.
拥有一致的设计系统是件好事,因为它可以帮助开发和设计团队更好地相互理解。

For instance, within Figma, you can have a single shared source of truth for any values in your design system. But to make this system truly maintainable, you’ll need to introduce some conventions regarding token grouping and naming—which we’ll get into later in this article.
例如,在 Figma 中,您可以为设计系统中的任何值拥有单一的共享事实来源。但为了使这个系统真正可维护,您需要引入一些有关令牌分组和命名的约定 - 我们将在本文后面介绍。

This is the second requirement your project needs to meet: you should already be using a component-based approach. The utility-first approach can lead to quite cluttered and verbose HTML structures since Tailwind classes apply directly to elements. This can mean the markup is harder to read and maintain, especially noticeable as your project grows.
这是您的项目需要满足的第二个要求:您应该已经在使用基于组件的方法。由于 Tailwind 类直接应用于元素,实用程序优先的方法可能会导致相当混乱和冗长的 HTML 结构。这可能意味着标记更难以阅读和维护,随着项目的增长尤其明显。

The solution: actively using a component-based approach that encapsulates frequently used patterns (in our case, HTML elements appearing more than once) as separate components.
解决方案:积极使用基于组件的方法,将常用模式(在我们的例子中,HTML 元素出现多次)封装为单独的组件。

With this approach, we can keep things DRY. Moreover, we’ll still have a single source of truth for our Tailwind styles, and we can easily update it together in one place:
通过这种方法,我们可以保持事物干燥。此外,我们的 Tailwind 样式仍然有单一的事实来源,并且我们可以轻松地在一个地方一起更新它:

<!-- Reusable button with a long list of Tailwind classes: -->
<button class="bg-yellow-700 border-2 font-semibold border border-gray-300 text-green p-4 rounded">
Custom Button
</button>

<!-- Instead of repeating this structure over and over again, create a reusable component: -->
<CustomButton>Custom Button</CustomButton>

If your development tool doesn’t allow you to split your code into components, it’s likely that the utility-first approach of Tailwind will only make development harder, and you should probably look to other CSS frameworks-for example, CSS Modules.
如果您的开发工具不允许您将代码拆分为组件,那么 Tailwind 的实用程序优先方法可能只会使开发变得更加困难,您可能应该考虑其他 CSS 框架,例如 CSS 模块。

And one last thing regarding a component-based approach: avoid using the @apply directive:
关于基于组件的方法的最后一件事:避免使用 @apply 指令:

.block {
  @apply bg-red-500 text-white p-4 rounded-lg active:bg-blue-700 active:text-yellow-300 hover:bg-blue-500 hover:text-yellow-300;
}

Yes, using this directive, your code may look cleaner, but it throws away the key advantages of Tailwind: less mental overload when coming up with names for CSS classes, and the absence of regressions when changing styles (since with @apply they won’t be isolated within the component). Further, using it increases CSS bundle size.
是的,使用这个指令,你的代码可能看起来更干净,但它抛弃了 Tailwind 的主要优点:在为 CSS 类命名时减少了精神负担,并且在更改样式时没有回归(因为使用 @apply 他们赢了不在组件内被隔离)。此外,使用它会增加 CSS 包的大小。

The creators of Tailwind have also highlighted the importance of using the @apply directive with caution in the documentation.
Tailwind 的创建者还在文档中强调了谨慎使用 @apply 指令的重要性。

If you met both requirements, Tailwind CSS is likely a good framework option for you! Here are the most helpful practices for improving your long-term experience with it.
如果您满足这两个要求,Tailwind CSS 可能是您的一个不错的框架选择!以下是改善您的长期使用体验的最有用的做法。

1. Use fewer utility classes when possible
1. 尽可能使用更少的实用程序类

When you build a list of utility classes for an HTML element, each new class adds additional complexity for the developers, and they’ll have to analyze and work with the code later (and this includes you, too). Of course, these lists are an essential and inherent feature of Tailwind, but nevertheless, it’s better to write as little utility classes as possible.
当您为 HTML 元素构建实用程序类列表时,每个新类都会为开发人员增加额外的复杂性,并且他们稍后必须分析和使用代码(这也包括您)。当然,这些列表是 Tailwind 的基本和固有特征,但尽管如此,最好编写尽可能少的实用程序类。

Here are a few ways you can decrease the number of classes and get exactly the same results:
您可以通过以下几种方法来减少类的数量并获得完全相同的结果:

  • Instead of setting pt-4 pb-4, you can just use py-4. This also applies with the px, mx, and my properties.
    您可以只使用 py-4 ,而不是设置 pt-4 pb-4 。这也适用于 pxmxmy 属性。
  • Instead of flex flex-row justify-between, you can just use flex justify-between. This is because flex-row is the default value of theflex-direction property in CSS. In general, it can be valuable to remember some default values of other CSS properties (flex-wrap, for example) to make it easier to spot use cases like this.
    您可以使用 flex justify-between 来代替 flex flex-row justify-between 。这是因为 flex-row 是 CSS 中 flex-direction 属性的默认值。一般来说,记住其他 CSS 属性的一些默认值(例如 flex-wrap )可能很有价值,可以更轻松地发现此类用例。
  • Instead of writing a long class list like border border-dotted border-2 border-black border-opacity-50, you can set border-dotted border-2 border-black/50 and this will have the same effect: border-2 implies that border is set, and border-black/50 represents a shorthand for the RGBA format.
    您可以设置 border-dotted border-2 border-black/50 ,而不是编写像 border border-dotted border-2 border-black border-opacity-50 这样的长类列表,这将具有相同的效果: border-2 意味着设置了 borderborder-black/50 表示RGBA格式的简写。

With a shorter list of classes, the next time you inspect the structure of your application, it’ll be much easier to analyze what’s going on.
使用较短的类列表,下次检查应用程序的结构时,分析正在发生的事情会容易得多。

2. Group design tokens and name them semantically
2. 对设计标记进行分组并按语义命名

When working on a team, you probably agree that some clean coding practices (like the clear naming of variables) are really important for long-term development. That said, even if you’re working alone, it also can be worth setting some rules for code clarity, otherwise, you could get confused about your own project (for example, when returning after a break).
在团队中工作时,您可能会同意一些干净的编码实践(例如变量的清晰命名)对于长期开发非常重要。也就是说,即使您独自工作,也值得为代码清晰度设置一些规则,否则,您可能会对自己的项目感到困惑(例如,在休息后返回时)。

This approach is especially important while working with Tailwind because reckless usage of such a large number of classes and design tokens can really bring confusion into your code.
这种方法在使用 Tailwind 时尤其重要,因为鲁莽地使用如此大量的类和设计标记确实会给您的代码带来混乱。

As discussed above, using design tokens is a great practice, but just pasting them haphazardly can lead to chaos in your tailwind.config.js file.
如上所述,使用设计标记是一种很好的做法,但随意粘贴它们可能会导致 tailwind.config.js 文件混乱。

To remedy this, group related tokens together in tailwind.config.js. This means that design tokens for breakpoints, colors, and so on, will be in specific areas and won’t mess with each other:
要解决此问题,请将相关标记分组到 tailwind.config.js 中。这意味着断点、颜色等的设计标记将位于特定区域,并且不会相互干扰:

module.exports = {
  theme: {
    colors: {
      primary: 'oklch(75% 0.18 154)',
      secondary: 'oklch(40% 0.23 283)',
      error: 'oklch(54% 0.22 29)'
    },
    spacing: {
      'sm': '4px',
      'md': '8px',
      'lg': '12px'
    },
    screens: {
      'sm': '640px',
      'md': '768px'
    },
  },
  //...
}

Here’s another important thing: keeping a single semantic naming convention for your tokens will make it easier to find the necessary tokens and expand the system as the application grows.
这是另一件重要的事情:为您的令牌保留单一的语义命名约定将使您更容易找到必要的令牌并随着应用程序的增长扩展系统。

For example, to add a color for your error state, don’t just copy and paste the bright-red token from your Figma file into your Tailwind configuration: put it into the colors section and give a more concise name like error. This will make the system much more consistent.
例如,要为错误状态添加颜色,不要只是将 Figma 文件中的 bright-red 标记复制并粘贴到 Tailwind 配置中:将其放入颜色部分并给出更简洁的名称,例如 error 。这将使系统更加一致。

3. Keep class ordering
3.保持班级秩序

Here’s another clean coding convention: using a consistent order makes classes easier to read and understand. To illustrate, let’s take a look at some HTML elements with unsorted classes:
这是另一个干净的编码约定:使用一致的顺序使类更易于阅读和理解。为了说明这一点,让我们看一下一些带有未排序类的 HTML 元素:

<div class="p-2 w-1/2 flex bg-black h-2 font-bold">
  First block with unsorted classes
</div>

<div class="italic font-mono bg-white p-4 h-2 w-3 flex">
  Second block with unsorted classes
</div>

In the blocks above, there are classes for different categories: dealing with the box model, display, typography, and so on—but they don’t have any sort of presentational order. We can apply a unified order to sort classes by categories:
在上面的块中,有不同类别的类:处理盒模型、显示、排版等,但它们没有任何表示顺序。我们可以应用统一的顺序来按类别对类进行排序:

<div class="flex h-2 w-1/2 bg-black p-2 font-bold">
  First block with sorted classes
</div>

<div class="flex h-2 w-3 bg-white p-4 font-mono italic">
  Second block with sorted classes
</div>

Since maintaining class ordering manually requires a lot of time and attention, it’s much better to automate this work using the official Prettier plugin for Tailwind CSS. To learn more about how to get started with it and the methodology of how the classes are sorted, we recommend reading this article.
由于手动维护类顺序需要大量时间和精力,因此最好使用 Tailwind CSS 的官方 Prettier 插件来自动化这项工作。要了解有关如何开始使用它以及类排序方法的更多信息,我们建议阅读本文。

4. Minimize build size
4. 最小化构建尺寸

It’s important to keep bundle size as small as possible—heavy builds mean slow-loading pages, bad performance, and frustrated users.
保持包大小尽可能小很重要——繁重的构建意味着页面加载缓慢、性能不佳和用户感到沮丧。

Tailwind provides us with thousands of utility classes, and it’s unlikely we’ll use all of them within a single project. So, how can we make sure that any unused styles won’t end up in our production build?
Tailwind 为我们提供了数千个实用程序类,我们不太可能在一个项目中使用所有这些类。那么,我们如何确保任何未使用的样式不会出现在我们的生产版本中?

If you use Tailwind version 3.0 or above, the Just-in-Time (JIT) engine is enabled in your project by default—it ensures that CSS styles are generated as they are needed, and we won’t need to purge unused styles for production builds.
如果您使用 Tailwind 3.0 或更高版本,则默认情况下会在您的项目中启用 Just-in-Time (JIT) 引擎 - 它确保根据需要生成 CSS 样式,并且我们不需要清除未使用的样式生产构建。

But if you’re using an older version of Tailwind, you need to perform additional optimizations for your build—this can be done using PurgeCSS, a tool for removing unused CSS. This article explains how to do this in version 2.1 and older. You can also enable the JIT mode manually in your tailwind.config.js file, like so:
但如果您使用的是旧版本的 Tailwind,则需要对构建执行额外的优化 - 这可以使用 PurgeCSS 来完成,这是一个用于删除未使用的 CSS 的工具。本文介绍了如何在 2.1 及更早版本中执行此操作。您还可以在 tailwind.config.js 文件中手动启用 JIT 模式,如下所示:

module.exports = {
	mode: 'jit',
	...
}

This will make sure that we’re only including the necessary styles in our bundle.
这将确保我们只在捆绑包中包含必要的样式。

There is another important thing to consider: always minify the final CSS for the production build. Minification removes all unnecessary characters (like whitespace, comments, and so on) and this will noticeably reduce file size.
还有另一件重要的事情需要考虑:始终缩小生产构建的最终 CSS。缩小会删除所有不必要的字符(如空格、注释等),这将显着减小文件大小。

Using the Tailwind CLI, this can be done by setting --minify flag:
使用 Tailwind CLI,可以通过设置 --minify 标志来完成:

npx tailwindcss -o build.css --minify

Or, if you’ve installed Tailwind as a PostCSS plugin, you can use the cssnano tool for minification by adding it to your plugin list.
或者,如果您已将 Tailwind 作为 PostCSS 插件安装,则可以通过将 cssnano 工具添加到插件列表中来使用 cssnano 工具进行缩小。

There's a notable size difference after minifying CSS and enabling JIT mode, in our case, 8KB compared to 12KB

If we don’t consider optimization, the size of our CSS can end up really big (more than several tens of kilobytes). Even in a small project with a few components with styles, there is a 30%+ size difference after minifying CSS and enabling JIT mode. To achieve this, you just need to add the minify flag and enable jit mode, as described above.
如果我们不考虑优化,CSS 的大小最终可能会非常大(超过几十 KB)。即使在一个只有少量带有样式的组件的小项目中,缩小 CSS 并启用 JIT 模式后,大小也会有 30% 以上的差异。要实现此目的,您只需添加 minify 标志并启用 jit 模式,如上所述。

If you want to learn more information about minification and compression for Tailwind, check this section of documentation.
如果您想了解有关 Tailwind 缩小和压缩的更多信息,请查看文档的此部分。

Tip: If you have design tokens in your project, make sure that they’re all actually being used. Unused design tokens confuse developers, make the configuration more complicated, and introduce unneeded messiness into your design system.
提示:如果您的项目中有设计令牌,请确保它们都已实际使用。未使用的设计令牌会让开发人员感到困惑,使配置更加复杂,并给您的设计系统带来不必要的混乱。

5. Prevent inconsistencies when overriding and extending styles
5. 防止覆盖和扩展样式时出现不一致

Imagine that we use a component with a custom button on our page:
想象一下,我们在页面上使用带有自定义按钮的组件:

<Button className="bg-black" />

And we have a Button component that has some default style:
我们有一个具有默认样式的 Button 组件:

export const Button = () => {
  return <button className="bg-white">Test button</button>
}

In this case, the button will remain white–Tailwind doesn’t automatically override style and apply the black color, so we need to specify it in the Button component:
在这种情况下,按钮将保持白色 - Tailwind 不会自动覆盖样式并应用黑色,因此我们需要在 Button 组件中指定它:

export const Button = ({ className = "bg-white" }) => {
  return <button className={className}>Test button</button>
}

There’s nothing inherently wrong about this aspect of Tailwind, but if we want to customize some appearance by overriding or extending a lot of styles, it can be cumbersome to pass classes via props each time.
Tailwind 在这方面本质上没有什么问题,但是如果我们想通过覆盖或扩展很多样式来自定义某些外观,每次通过 props 传递类可能会很麻烦。

Moreover, there is one more drawback to this approach: accepting utilities via props can make it harder to ensure a consistent component view. This approach encourages using any utility combination for the same component across the app which can lead to a lack of visual consistency.
此外,这种方法还有一个缺点:通过 props 接受实用程序可能会更难确保组件视图的一致性。这种方法鼓励对应用程序中的同一组件使用任何实用程序组合,这可能会导致缺乏视觉一致性。

So, what can we do with it?
那么,我们能用它做什么呢?

Instead of allowing any arbitrary utility classes to be passed via props, define a set of predefined variants:
不要允许通过 props 传递任何任意实用程序类,而是定义一组预定义的变体:

const BUTTON_VARIANTS = {
  primary: "bg-blue-500 hover:bg-blue-600 text-white",
  secondary: "bg-gray-500 hover:bg-gray-600 text-white",
  danger: "bg-red-500 hover:bg-red-600 text-white"
};

Then, change the Button component so it can accept a variant prop. To make constructing className more convenient, you can use clsx:
然后,更改 Button 组件,以便它可以接受 variant 属性。为了让构造 className 更加方便,可以使用clsx:

export const Button = ({ className, variant = BUTTON_VARIANTS.primary }) => {
  return <button className={clsx(className, variant)}>Test Button</button>
}

Tip: using clsx would be also especially handy if you need to construct classes conditionally.
提示:如果您需要有条件地构造类,那么使用 clsx 也会特别方便。

After constructing className for the component, just use it, passing the desired variant:
为组件构造 className 后,只需使用它,传递所需的变体:

<Button variant="secondary" />

Now, consistency is ensured, and despite the fact that we added a restriction on full customization, flexibility remains; we can add any new variant for the component or edit an existing one.
现在,一致性得到了保证,尽管我们添加了对完全定制的限制,但灵活性仍然存在;我们可以为该组件添加任何新的变体或编辑现有的变体。

And the other benefit of this approach is that it allows for simpler maintenance: changes to utility classes can be made in one place, and then propagated to every component of that variant in the app.
这种方法的另一个好处是它允许更简单的维护:可以在一个地方对实用程序类进行更改,然后传播到应用程序中该变体的每个组件。

If for some reason you don’t want to use the sets of predefined variants, you can try the package tailwind-merge, which provides utility function twMerge to merge Tailwind classes in JS without style conflicts–but it should be used carefully and only when necessary, since it is not the most lightweight and increases bundle size.
如果出于某种原因你不想使用预定义变体集,你可以尝试使用 tailwind-merge 包,它提供了实用函数 twMerge 来合并 JS 中的 Tailwind 类,而不会发生样式冲突,但应谨慎使用仅在必要时使用,因为它不是最轻的并且会增加包的大小。

Summing up: how to use and how not to use Tailwind
总结:如何使用和如何不使用 Tailwind

Tailwind is a powerful tool, but it’s important to use it while following some rules to prevent chaos from erupting in your project. Let’s sum up the principles that we listed above.
Tailwind 是一个强大的工具,但在使用它时遵循一些规则很重要,以防止项目中爆发混乱。让我们总结一下上面列出的原则。

First of all, to get the most out of these practice, you should use Tailwind when you already have a design system and consistent design tokens and have opted for a component-based approach. Without breaking reusable elements into components, using Tailwind will become painful sooner or later, leading to repetitive or verbose HTML structures.
首先,为了充分利用这些实践,当您已经拥有设计系统和一致的设计令牌并选择基于组件的方法时,您应该使用 Tailwind。如果不将可重用元素分解为组件,使用 Tailwind 迟早会变得痛苦,导致重复或冗长的 HTML 结构。

  1. Minimize the number of utility classes where possible
    尽可能减少实用程序类的数量
  2. Formulate code conventions within your team, for example, by grouping design tokens and naming them semantically
    在团队内制定代码约定,例如,通过对设计令牌进行分组并按语义命名它们
  3. Likewise, implement consistent class ordering and set up linters to ensure code cleanliness
    同样,实现一致的类排序并设置 linter 以确保代码整洁
  4. Minimize your bundle sizes: ensure you’ve included only the needed styles, and always minify the final CSS for production build
    最小化你的包大小:确保你只包含所需的样式,并始终最小化生产构建的最终 CSS
  5. When appropriate, try to define a set of predefined variants for your components; this will help avoid problems with inconsistencies and style overriding
    在适当的时候,尝试为您的组件定义一组预定义的变体;这将有助于避免不一致和样式覆盖的问题

By following these rules, you’ll be able to use Tailwind for the long haul–with pleasure and without problems–giving your team the chance to revel in all the benefits it provides.
通过遵循这些规则,您将能够长期使用 Tailwind - 愉快且没有任何问题 - 让您的团队有机会享受它提供的所有好处。

At Evil Martians, we transform growth-stage startups into unicorns, build developer tools, and create open source products. If you’re ready to engage warp drive, give us a shout!
在 Evil Martians,我们将成长阶段的初创公司转变为独角兽,构建开发人员工具并创建开源产品。如果您准备好使用曲速引擎,请告诉我们!

Solve your problems with 1-1 guidance
1-1指导解决您的问题

Are you fighting with the challenges of improving performance, scaling, product shipping, UI design, or cost-effective deployment? Our experts in developer-first startups can offer free tailored recommendations for you—and our engineering team to implement the bespoke strategy.
您是否正在应对提高性能、扩展、产品运输、UI 设计或经济高效部署方面的挑战?我们的开发人员优先初创公司专家可以为您和我们的工程团队提供免费的定制建议,以实施定制策略。

Launch with Martians

How can we help you?

instead
Martians at a glance
17
years in business

We transform growth-stage startups into unicorns, build developer tools, and create open source products.

If you prefer email, write to us at surrender@evilmartians.com