在 Tailwind 项目中使用 CSS 级联层管理自定义样式

Avatar of Ollie Williams
Ollie Williams

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费信用额度!

如果一个实用程序类只做一件事,那么您可能不希望它被来自其他地方的任何样式覆盖。一种方法是使用 !important 来 100% 确保样式会被应用,无论特异性冲突如何。

Tailwind 配置文件有一个 !important 选项,它会自动将 !important 添加到每个实用程序类中。以这种方式使用 !important 并没有错,但如今有更好的方法来处理特异性。使用 CSS 级联层,我们可以避免使用 !important 的强硬方法。

级联层允许我们将样式分组到“层”中。层的优先级始终高于选择器的特异性。特异性只在每个层内起作用。建立合理的层级顺序有助于避免样式冲突和特异性冲突。这就是使 CSS 级联层成为管理自定义样式以及来自第三方框架(如 Tailwind)的样式的绝佳工具的原因。 管理自定义样式以及来自第三方框架的样式

Tailwind 源 .css 文件通常以类似这样的方式开始

@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind variants;

让我们看看关于指令的 官方 Tailwind 文档

指令是在您的 CSS 中可以使用的一些自定义 Tailwind 特定的 at-规则,它们为 Tailwind CSS 项目提供特殊的功能。使用 @tailwind 指令将 Tailwind 的 basecomponentsutilitiesvariants 样式插入到您的 CSS 中。

在生成的 CSS 文件中,Tailwind 的 CSS 重置(称为 Preflight)作为基础样式的一部分首先包含在内。其余的 base 包含 Tailwind 工作所需的 CSS 变量。components 是您添加自己的自定义类的地方。您在标记中使用的任何实用程序类都会接下来出现。变体是针对悬停和焦点状态以及响应式样式的样式,它们将最后出现在生成的 CSS 文件中。

Tailwind 的 @layer 指令

令人困惑的是,Tailwind 有自己的 @layer 语法。本文讨论的是 CSS 标准,但让我们快速了解一下 Tailwind 版本(它会被编译掉,不会出现在输出 CSS 中)。Tailwind 的 @layer 指令是一种将您自己的额外样式注入到输出 CSS 文件的指定部分的方法。

例如,要将您自己的样式追加到 base 样式,您可以执行以下操作

@layer base {
  h1 {
    font-size: 30px;
  }
}

默认情况下,components 层是空的——它只是一个放置您自己的类的地方。如果您以 Tailwind 的方式做事,您可能会使用 @apply(尽管 Tailwind 的创建者最近 建议不要这样做),但您也可以以常规方式编写类

@layer components {
  .btn-blue {
    background-color: blue;
    color: white;
  }
}

CSS 标准功能更强大。让我们回到那个话题……

使用 CSS 标准 @layer

以下是如何重写此代码以使用 CSS 标准 @layer

@layer tailwind-base, my-custom-styles, tailwind-utilities;

@layer tailwind-base {
  @tailwind base;
}

@layer tailwind-utilities {
  @tailwind utilities;
  @tailwind variants;
} 

与 Tailwind 指令不同,这些指令不会被编译掉。浏览器可以理解它们。事实上,Edge、Chrome、Safari 和 Firefox 中的开发者工具甚至会显示您定义的任何层。

CSS Cascade Layers with Tailwind CSS layers in DevTools.

您可以拥有任意数量的层——并为它们命名您想要的任何名称——但在本例中,我的所有自定义样式都在一个层(my-custom-styles)中。第一行建立了层级顺序

@layer tailwind-base, my-custom-styles, tailwind-utilities;

这需要提前提供。请确保在任何其他使用 @layer 的代码之前包含此行。列表中的第一个层将是最不强大的,而列表中的最后一个层将是最强大的。这意味着 tailwind-base 是最不强大的层,其中任何代码都将被所有后续层覆盖。这也意味着 tailwind-utilities 将始终胜过任何其他样式——无论源顺序或特异性如何。(实用程序和变体*可以*位于单独的层中,但 Tailwind 的维护人员将确保变体始终胜过实用程序,只要您将变体包含在实用程序指令下方即可。)

任何不在层中的内容都将覆盖任何在层中的内容(使用 !important 的样式除外)。因此,您也可以选择将 utilitiesvariants 保留在任何层之外

@layer tailwind-base, tailwind-components, my-custom-styles;

@layer tailwind-base {
  @tailwind base;
}

@layer tailwind-components {
  @tailwind components;
}

@tailwind utilities;
@tailwind variants;

这实际上给我们带来了什么?在很多时候,高级 CSS 选择器非常有用。让我们创建一个仅响应键盘焦点而不是鼠标点击的 :focus-within 版本,使用 :has 选择器(它在 Chrome 105 中可用)。当其任何子元素获得焦点时,这将为父元素设置样式。Tailwind 3.1 引入了 自定义变体——例如 <div class="[&:has(:focus-visible)]:outline-red-600">——但有时直接编写 CSS 更容易

@layer tailwind-base, my-custom-styles;
@layer tailwind-base {
  @tailwind base;
}

@tailwind utilities;

@layer my-custom-styles {
  .radio-container {
    padding: 4px 24px;
    border: solid 2px rgb(230, 230, 230);
  }
  .radio-container:has(:focus-visible) {
    outline: solid 2px blue;
  }
}

假设在一个实例中,我们希望将 outline-colorblue 覆盖为其他颜色。假设我们正在处理的元素同时具有 Tailwind 类 .outline-red-600 和我们自己的 .radio-container:has(:focus-visible)

<div class="outline-red-600 radio-container"> ... </div>

哪个 outline-color 会获胜?

通常,.radio-container:has(:focus-visible) 较高的特异性意味着 Tailwind 类无效——即使它在源顺序中较低。但是,与依赖于源顺序的 Tailwind @layer 指令不同,CSS 标准 @layer 覆盖了特异性。

因此,我们可以在自己的自定义样式中使用复杂的选择器,但仍然可以在需要时用 Tailwind 的实用程序类覆盖它们——而无需诉诸强硬的 !important 使用来获得我们想要的结果。