保持 CSS 特定性低的策略

Avatar of Chris Coyier
Chris Coyier 发布

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

在整个项目中,保持 CSS 特定性 较低是一个值得的目标。它通常表明各个元素之间处于相对和谐的状态。您不会与自己作斗争,而且在需要时有足够的余地覆盖样式。选择器上的特定性往往会随着时间的推移而增加,而且存在一个硬性上限。我相信我们都经历过 !important 标签和内联样式的痛苦。

那么,我们如何才能随着时间的推移保持特定性较低呢?

给自己需要的类

也许您正在编写一个特定性很高的选择器,因为您正在覆盖一个已经存在的选择器。也许您要选择的元素包含在多个页面上,因此您从一个较高层的类中选择它

.section-header {
  /* normal styles */
}

body.about-page .section-header {
  /* override with higher specificity */
}

无论您对此有何感受,都要认识到特定性确实在这里逐渐增加。为了防止这种情况,您是否可以更改要直接设置样式的元素的类名?有时,创建一些服务器端辅助函数或变量来发出类可以很有帮助,以避免视图中的逻辑。

<header class="<%= header_class %>">
  Which could output one class, or both, as desired.
</header>
.section-header {
  /* normal styles */
}

.about-section-header {
  /* override with same specificity */
  /* possibly extend the standard class */
}

考虑样式表的源代码顺序

在尝试覆盖某些内容的相同情况下,也许您正在添加一个额外的类名来处理样式覆盖。

<header class="section-header section-header-about">
  
</header>

但是,您使用 .section-header-about 编写覆盖样式的位置实际上正在被现有类覆盖。这可能是因为样式表中的选择器顺序。这两个选择器具有完全相同的特定性,因此最后声明的规则获胜。

解决此问题意味着只要确保编写覆盖类的任何位置都位于后面。也许以某种方式组织您的样式表,例如

@import "third-party-and-libs-and-stuff";

@import "global-stuff";

@import "section-stuff";

@import "specific-things-and-potential-overrides";

降低要覆盖的元素的特定性

您知道关于将事物做得比您找到时更好的说法。您应该这样做。

如果某个元素上有一个 ID,并且这是它的样式来源,您可以将其删除吗?在执行此操作之前,一定要在整个项目中搜索 #that。它可能用作 JS 挂钩(完全没问题),在这种情况下,您应该保留它,并为 CSS 添加一个类或使用该元素上已有的类。

一开始不要避免使用类

我之前一直使用类似这样的选择器

.module > h2 {

}

这将正常工作,直到您想为该 h2 元素设置特殊样式的那一天。您可能会在标记中进行操作,并说

<div class="module">
  <h2 class="unique">
    Special Header
  </h2>
</div>

但不幸的是

.module > h2 {
  /* normal styles */
}
.unique {
  /* I'm going to lose this specificity battle */
}
.module .unique {
  /* This will work, but specificity creep! */
}

特定性逐渐增加,因为最初的选择器正在困扰着我们。这就是几乎所有 CSS 方法都建议使用类似以下内容的扁平结构的原因

<div class="module">
  <h2 class="module-header">
  </h2>
  <div class="module-content">
  </div>
</div>

从一开始就感觉像是更多繁琐的工作,因为您可能并不立即需要这些类。但是,通过不回避这项工作(不要偷懒!),您以后拥有的挂钩可以避免您的痛苦。

使用层叠

随着项目的发展,使用特定性*低*的选择器来修改选择器变得越来越危险,因为它们可能影响更多内容并产生意想不到的后果。

#im #just .gonna[do] .this .to .be #safe {
  /* cries (ಥ_ʖಥ) */
}

但是,影响更多内容是 CSS 的强大功能。拥有一个坚实的构建基础意味着可能需要更少的覆盖。实现这一点的策略可以包括以下内容……

使用模式库和/或样式指南和/或原子设计

模式库(类似 这样)意味着您在需要时就可以找到所需的内容。需要一些选项卡吗?来吧,这是已建立的方法。而且它很可能以特定性很低的方式构建,因此覆盖不会太难。

原子设计书籍)可以指导您构建网站(或模式库),因此,即使您没有完整的模式来满足您的需求,您也有其下方的构建模块。

一个 样式指南可能会帮助您,因为它可能会强制执行有关特定性的特定规则,以防止您自食其果。

考虑选择性排版

在尝试使用层叠并为大量元素设置智能默认值的同时,您可能希望有时对一些智能默认值进行范围限定。例如,列表元素通常用于导航和内容中。它们样式将大不相同。那么为什么不从空白状态开始,并在需要时才应用文本样式

/* reset styles globally */
ul, ol, li {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* scope text styles to text content */
.text-content {
  h1, h2, h3 {
  }
  p {
  }
  ul {
  }
  ol {
  }
  /* etc */
}

这确实会提高这些文本选择器的特定性,但这意味着专门针对文本的规则不会影响比需要的影响更多的内容,并且减少了覆盖的需求。

超出控制范围的问题

也许某些第三方代码期望或将某些 HTML 注入您的页面。您必须处理这些问题。您仍然可以尝试使用特定性尽可能低的

仅稍微提高特定性并进行记录

如果您需要进行特定性争斗,而不是使用像 ID 或 !important 这样的重型武器,请尝试更轻的东西。

标签限定符是特定性升级的最小可能性。

ul.override {
  /* I win */  
}
.normal {
}

但是,将选择器限制为特定标签是一个奇怪的限制因素。最好添加一个额外的类。如果另一个名称没有意义,您甚至可以根据需要使用相同的类。

.nav.override {
}
.override.override {
}
.nav {
}

仅仅因为嵌套很好,并不意味着特定性必须提高

预处理器中的嵌套有时不被鼓励,因为它使编写过于复杂的选择器变得非常容易。但嵌套有时在样式表中只是视觉上的好处,因为它将事物分组在一起,使其更易于阅读和理解。

Bohdan Kokotko 最近提醒我,Sass 中的 & 可以用来做本质上的命名空间,而不是复合选择器。

.what {
    color: red;
   &-so {
      color: green;
      &-ever {
        color: blue;
      }
   }
}
.what {
  color: red;
}
.what-so {
  color: green;
}
.what-so-ever {
  color: blue;
}

单类包装器

处理覆盖的一种比较强硬的方式是在需要应用样式覆盖的区域使用现有的包装元素,或者添加一个新的包装元素。

<div class="override">

   ... existing stuff ...

</div>

现在您可以通过在选择器中添加一个类来覆盖任何现有的样式。这是特异性蠕变,但试图保持特异性较低。

.override .existing {
}

只此一次

如果要覆盖一个覆盖,那应该是一个停止并重新考虑的时机。

您可以给自己一个可以使用的一次性类吗?一次性覆盖实际上可以很高效,就像是对模式的变体,而不是创建新的模式。覆盖一个覆盖的做法会带来混乱和进一步的冲突。

下次做得更好

如果您遇到特异性问题,即使现在无法解决,至少您现在知道它有問題,并且下次可以采用不同的方法。