嵌套 CSS 选择器而不增加特异性

Nesting CSS selectors without increasing specificity

让我们采用这三个选择器,从最高特异性到最低特异性排序:

.special-section p { }
.weird-font        { }
p                  { }

许多 CSS 专家建议不要像第一个选择器 .special-section p 那样嵌套,因为它的特异性足够高,你不能用简单的 class 覆盖它,比如 .weird-font。我想找到一种方法仍然可以像 .special-section p 那样实现嵌套,但又不会增加特异性。像这样:

 .weird-font { }
 .special-section p /* with hack to decrease specificity */ { }
 p { }

用例:

使用像 p 这样的简单选择器为排版和此类文档范围应用默认值是非常安全的。但是,我想更改特定部分的默认值,类似于 .special-section p,而不必使用 hack 来增加选择器的特异性,例如 .weird-font。我宁愿使用 hack 来降低 .special-section p 的特异性,也不愿使用 hack 来增加 .weird-font 的特异性。有办法吗?

您不能降低特异性,但可以为异常添加一个更具体的选择器。

.weird-font, /* Normal weird font */
.special-section p.weird-font /* Override for special section */ 
  { }

但如您所见,这是一个浮动比例。所以那些大师可能是对的。如果您要删除 .special-section p,而是为这些 P 提供它们自己的选择器 .special-section-para 或其他东西,那么您就不会有这个问题。

但就个人而言,我不介意时不时地添加一个像上面那样的例外。我认为整个特性就是为了这个目的,如果你需要一个更具体的选择器来设计一些东西,对我来说这似乎是正确的做法。

一个普遍听到的解决方案是使用!important。 !important 的问题在于只有一层覆盖。如果你制作了一个更具体的选择器,你可以用一个更具体的选择器再次覆盖它。使用 !important 之后,您就别无选择了。更重要的是,使用 !important 可能会干扰用户为提高可读性而可能拥有的特殊样式表。出于这个原因,我从不在这种情况下使用 !important

但话又说回来,我不认为自己是 CSS 大师。 ;)

作为一名 CSS 大师,我对抛弃选择器必须提供的一切以避免特异性问题的想法感到遗憾。这并不是说我不相信特异性机制有缺陷,但肯定没有那么引人注目的解决方法。

首先:不,您不能降低选择器的特异性。选择器不提供任何具有负面特异性级别的特征,这些特征会以这种方式降低特异性。您可以选择的最低值是 *,它具有 特异性(即它不会使复杂的选择器更加具体或不那么具体)。

因此您在选择器级别上的唯一办法是增加它。您是否可以在不使用黑客的情况下做到这一点取决于您对 "hack".

的定义

以下是我认为的 hack,因为它使用语法上合法但语义上无意义的选择器,如 ,没有明显的目的,只是将类型选择器的特异性价值添加到复杂的选择器(这对外行来说并不明显):

.special-section p { }
.weird-font, :not(_).weird-font { }

以下是不是我认为是 hack 的内容,因为这是您通常会做的事情。几乎唯一的 "issue" 是它明显重复了单独的 class 选择器:

.special-section p { }
.weird-font, .special-section p.weird-font { }

如果您认为任何类型的外来选择器都是为了增加特异性而被黑客攻击——这是一个完全合理的 POV,请不要搞错——那么下一个最好的选择 不是 黑客是 !important.

就我个人而言,我会选择特异性 hack。 !important 有,咳咳, 重要 的影响,这些影响并没有伴随着特异性 hack — 请记住 !important and specificity have different semantics。例如,您不能使用内联样式或 JavaScript 覆盖 !important 声明,除非它们也被标记为重要。1


1 事实上,这是我对 Lea Verou 的回应,当时她 discussion on Twitter 一段时间前关于特异性黑客与 !important.

我希望尽可能具体当前需要。我确实喜欢为将来 CSS 的变化留出空间,所以不要为了它而尽可能具体,例如:

.great-grandfather .grandfather .father .child { }

如果必须的话,我当然会。但是以上面的例子为例,如果我想为使用这个 class 的特定元素覆盖 .child,它的样式可能是这样的:

.child {
   color: black;
}

如果可能的话,我会去上面的一位家长覆盖它:

.father .child {
   color: white;
}

再往下看,如果特定页面上的某个元素使用 .child class,在这种情况下,我需要覆盖两个 .father .child,我会选择一个更高级别的特异性:

.grandfather .father .child {
   color: red;
}

这样做可以确保您不需要使用 !important... 我尽可能避免使用它!

这些天,在 2018 年,这已接近可能。

首先,CSS4 将有一种方法可以让您在不增加特异性的情况下创建更具体的选择器:

:where(.special-section) p {
    color: red;
}

这会将 .special-section 内的段落颜色设置为红色,但特异性为 001(即与普通 p 选择器具有的特异性相同)。

spec still calls this special pseudo-class :something(), but chances are it's going to be called :where(). (Side note: I really want这个要被称为"honey badger selector").

但这还在未来。

但是,实际上 今天,如果您不再需要支持 IE(或者是对不完美的回退感到满意),那就是使用 custom properties a.k.a。 CSS 个变量。

所以你想要这个:

.special-section p { color: red; }
.weird-font        { color: magenta; }
p                  { color: green; }

但第一部分的特异性低于任何带有 class 的选择器。你可以这样做:

.special-section p { --low-specificity-color: red; }
.weird-font        { color: magenta; }
p                  { color: var(--low-specificity-color, green); }

如果你在现代浏览器中 运行 下面的代码片段,你应该注意到第二段是红色的,因为它在一个特殊的部分,但第三段是洋红色的,因为它是 .weird-font -- 尽管 .weird-font 具有 010 特异性并且 .special-section p 具有 011.

.special-section p { --low-specificity-color: red; }
.weird-font        { color: magenta; }
p                  { color: var(--low-specificity-color, green); }
<p>This is a paragraph.</p>

<section class="special-section">
   <p>This is a paragraph inside a special section.</p>
   <p class="weird-font">This is a paragraph with a weird font inside a special section.</p>
</section>

   <p class="weird-font">This is a paragraph with a weird font.</p>

   <div class="weird-font">This is a div with a weird font.</div>

这是有效的,因为虽然 --low-specificity-color 被更改为 011 特异性,但它仅 应用 具有 001 特异性。