这是您关于 CSS 级联层的完整指南,这是一个 CSS 功能,允许我们定义明确包含的特异性层,以便我们完全控制哪些样式在项目中优先,而无需依赖特异性技巧或 !important
。本指南旨在帮助您全面了解级联层的用途、如何以及为何选择使用它们、当前的支持级别以及如何使用它们的语法。
目录
- 快速示例
- 简介:什么是级联层?
- 层在级联中处于什么位置?
- !important 的起源、上下文和层是反向的!
- 建立层级顺序
- 语法:使用级联层
- 用例:我什么时候想使用级联层?
- 测试你的知识:哪种样式会胜出?
- 在浏览器开发者工具中调试层冲突
- 浏览器支持和回退
- 更多资源
快速示例
/* establish a layer order up-front, from lowest to highest priority */
@layer reset, defaults, patterns, components, utilities, overrides;
/* import stylesheets into a layer (dot syntax represents nesting) */
@import url('framework.css') layer(components.framework);
/* add styles to layers */
@layer utilities {
/* high layer priority, despite low specificity */
[data-color='brand'] {
color: var(--brand, rebeccapurple);
}
}
@layer defaults {
/* higher specificity, but lower layer priority */
a:any-link { color: maroon; }
}
/* un-layered styles have the highest priority */
a {
color: mediumvioletred;
}
简介:什么是级联层?
CSS 级联层旨在解决 CSS 中的棘手问题。让我们来看看**主要问题**以及级联层如何解决它。
问题:特异性冲突升级
我们中的许多人都遇到过这样的情况:由于选择器冲突,我们希望覆盖代码中其他地方(或第三方工具)的样式。多年来,开发者们已经开发了许多“方法”和“最佳实践”来避免这些情况——例如“所有选择器只使用一个类”。这些规则通常更多的是关于避免级联,而不是利用它。
管理级联冲突和选择器特异性通常被认为是 CSS 中比较困难——或者至少是比较令人困惑——的方面之一。这可能部分是因为很少有其他语言依赖级联作为其核心功能,但也确实如此,原始级联严重依赖于*启发式*(内置于代码中的有根据的猜测或假设),而不是为 Web 作者提供直接和明确的控制。
例如,选择器特异性——我们与级联的主要交互方式——是基于这样的假设:更窄目标的样式(例如仅使用一次的 ID)可能比更通用和可重用样式(例如类和属性)更重要。也就是说:选择器有多*具体*。这是一个很好的猜测,但它不是一个完全可靠的规则,这会导致一些问题
- 它将*选择元素*的行为与*优先级规则集*的行为结合起来。
- “解决”特异性冲突的最简单方法是通过添加其他不必要的选择器来升级问题,或者(喘气)扔出
!important
手榴弹。
.overly#powerful .framework.widget {
color: maroon;
}
.my-single_class { /* add some IDs to this ??? */
color: rebeccapurple; /* add !important ??? */
}
解决方案:级联层提供控制
级联层 使 CSS 作者能够更直接地控制级联,因此我们可以构建更有意图的级联系统,而无需过多依赖与选择相关的启发式假设。
使用 @layer
at 规则和分层 @import
,我们可以建立我们自己的*级联层*——从低优先级样式(如重置和默认值)开始,到主题、框架和设计系统,再到最高优先级样式,如组件、实用程序和覆盖。特异性仍然适用于*每个层内*的冲突,但层之间的冲突始终通过使用更高优先级的层样式来解决。
@layer framework {
.overly#powerful .framework.widget {
color: maroon;
}
}
@layer site {
.my-single_class {
color: rebeccapurple;
}
}
这些层是有序和分组的,因此它们不会像特异性和重要性那样升级。级联层不像选择器那样累积。添加*更多层*不会使某些东西*更重要*。它们也不像重要性那样是二元的——突然跳到堆栈的顶部——或者像 z-index
那样是编号的,我们必须猜测一个很大的数字(z-index:
9999999
?)。事实上,默认情况下,分层样式比非分层样式*更不重要*。
@layer defaults {
a:any-link { color: maroon; }
}
/* un-layered styles have the highest priority */
a {
color: mediumvioletred;
}
层在级联中处于什么位置?
级联是一系列步骤(算法),用于解决样式之间的冲突。
html { --button: teal; }
button { background: rebeccapurple !important; }
.warning { background: maroon; }
<button class="warning" style="background: var(--button);">
what color background?
</button>
添加级联层后,这些步骤是
*选择器特异性*只是级联的一小部分,但它也是我们互动最多的步骤,并且通常用于更普遍地指代整体*级联优先级*。人们可能会说 !important
标记或 style
属性“增加了特异性”——这是一种表达样式在级联中变得更高优先级的快速方法。由于级联层已直接添加到特异性之上,因此以类似的方式考虑它们是合理的:比 ID 选择器更强大的一步。
但是,CSS 级联层也使我们必须完全理解 !important
在级联中的作用——不仅仅是作为“增加特异性”的工具,而是作为平衡关注点的系统。
!important
的起源、上下文和层是反向的!
作为 Web 作者,我们通常认为 !important
是一种增加特异性的方法,可以覆盖内联样式或高度特定的选择器。这在大多数情况下都适用(如果您对升级没问题),但它忽略了*重要性*作为整体级联中一项功能的主要目的。
重要性不仅仅是为了简单地增加权力——而是为了平衡各种竞争关注点之间的权力。
重要性起源
这一切都始于起源,即样式在 Web 生态系统中的来源。CSS 中有三个基本起源
- **浏览器**(或用户代理)
- **用户**(通常通过浏览器首选项)
- Web **作者**(那就是我们!)
浏览器为所有元素提供可读的默认值,然后用户设置他们的首选项,然后我们(作者)为我们的网页提供预期的设计。因此,默认情况下,浏览器具有最低优先级,用户首选项会覆盖浏览器默认值,并且我们能够覆盖所有人。
但是 CSS 的创建者非常清楚我们不应该拥有最终决定权
如果发生冲突,**用户应该拥有最终决定权**,但也应该允许作者附加样式提示。
—— Håkon Lie(强调补充)
因此,*重要性*为浏览器和用户提供了一种在最重要的时候重新获得控制权的方法。当 !important
标记添加到样式中时,会创建三个新层——并且顺序相反!
!important
浏览器样式(最强大)!important
用户首选项!important
作者样式- 普通作者样式
- 普通用户首选项
- 普通浏览器样式(最不强大)
对我们来说,添加 !important
并没有太大改变——我们的重要样式与我们的普通样式非常接近——但对于浏览器和用户来说,这是一个非常强大的重新获得控制权的工具。浏览器默认样式表包含许多我们无法覆盖的重要样式,例如
iframe:fullscreen {
/* iframes in full-screen mode don't show a border. */
border: none !important;
padding: unset !important;
}
虽然大多数流行的浏览器都难以上传实际的用户样式表,但它们都提供用户首选项:用于建立特定用户样式的图形界面。在该界面中,始终有一个复选框可供用户选择是否允许网站覆盖其首选项。这与在用户样式表中设置 !important
相同
重要的上下文
同样的基本逻辑也适用于层叠中的*上下文*。默认情况下,来自宿主文档(light DOM)的样式会覆盖来自嵌入上下文(shadow DOM)的样式。但是,添加 !important
会颠倒顺序
!important
shadow 上下文(最强)!important
host 上下文- 普通 host 上下文
- 普通 shadow 上下文(最弱)
来自 shadow 上下文内部的重要样式会覆盖宿主文档定义的重要样式。这是一个 odd-bird
自定义元素,其中一些样式写在元素模板(shadow DOM)中,一些样式写在宿主页面(light DOM)样式表中
两个 color
声明都具有正常的权重,因此宿主页面的 mediumvioletred
优先级更高。但是 font-family
声明被标记为 !important
,这使得 shadow 上下文(其中定义了 fantasy
)具有优势。
重要的层
层叠层的工作方式与来源和上下文相同,重要的层以相反的顺序排列。唯一的区别是层使这种行为更加明显。
一旦我们开始使用层叠层,我们就需要更加谨慎和有意地使用 !important
。它不再是快速跳到优先级顶部的方法,而是层叠分层的一个组成部分;一种让较低层*坚持*其某些样式必不可少的方法。
由于层叠层是可定制的,因此没有预定义的顺序。但是我们可以想象从三个层开始
- 实用程序(最强)
- 组件
- 默认值(最弱)
当这些层中的样式被标记为重要时,它们将生成三个新的、反向的重要层
!important
默认值(最强)!important
组件!important
实用程序- 普通实用程序
- 普通组件
- 普通默认值(最弱)
在这个例子中,颜色是由所有三个普通层定义的,并且 utilities
层赢得了冲突,应用了 maroon
颜色,因为 utilities
层在 @layers
中具有更高的优先级。但是请注意,text-decoration
属性在 defaults
和 components
层中都被标记为 !important
,其中重要的 defaults
优先,应用了 defaults
声明的下划线
建立层顺序
我们可以创建任意数量的层,并以各种方式命名或分组它们。但最重要的是要确保我们的层按正确的优先级顺序应用。
单个层可以在整个代码库中多次使用 - 层叠层按它们*首次出现*的顺序堆叠。遇到的第一个层位于底部(最弱),最后一个层位于顶部(最强)。但是,在此之上,**未分层样式具有最高优先级**
@layer layer-1 { a { color: red; } }
@layer layer-2 { a { color: orange; } }
@layer layer-3 { a { color: yellow; } }
/* un-layered */ a { color: green; }
- 未分层样式(最强)
- layer-3
- layer-2
- layer-1(最弱)
然后,如上所述,任何重要的样式都以相反的顺序应用
@layer layer-1 { a { color: red !important; } }
@layer layer-2 { a { color: orange !important; } }
@layer layer-3 { a { color: yellow !important; } }
/* un-layered */ a { color: green !important; }
!important
layer-1(最强)!important
layer-2!important
layer-3!important
未分层样式- 普通未分层样式
- 普通 layer-3
- 普通 layer-2
- 普通 layer-1(最弱)
层也可以分组,允许我们对顶级层和嵌套层进行更复杂的排序
@layer layer-1 { a { color: red; } }
@layer layer-2 { a { color: orange; } }
@layer layer-3 {
@layer sub-layer-1 { a { color: yellow; } }
@layer sub-layer-2 { a { color: green; } }
/* un-nested */ a { color: blue; }
}
/* un-layered */ a { color: indigo; }
- 未分层样式(最强)
- layer-3
- layer-3 未嵌套
- layer-3 sub-layer-2
- layer-3 sub-layer-1
- layer-2
- layer-1(最弱)
分组层始终保持在最终层顺序中(例如,layer-3 的子层将全部彼此相邻),但这与将列表“扁平化”相同 - 将其转换为单个六项列表。反转 !important
层顺序时,整个扁平化列表都会反转。
但是层不必在单个位置定义一次。我们给它们命名,以便可以在一个地方定义层(以建立层顺序),然后我们可以从任何地方向它们追加样式
/* describe the layer in one place */
@layer my-layer;
/* append styles to it from anywhere */
@layer my-layer { a { color: red; } }
我们甚至可以在单个声明中定义整个有序的层列表
@layer one, two, three, four, five, etc;
这使得网站的作者可以对层顺序拥有最终决定权。通过在导入任何第三方代码之前预先提供层顺序,可以在一个地方建立和重新排列顺序,而无需担心在任何第三方工具中如何使用层。
语法:使用层叠层
让我们看看语法!
@layer
语句
顺序设置 由于层按定义的顺序堆叠,因此我们有一个工具可以在一个地方建立该顺序非常重要!
我们可以使用 @layer
语句来做到这一点。语法是
@layer <layer-name>#;
该井号 (#
) 意味着我们可以在逗号分隔的列表中添加任意数量的层名称
@layer reset, defaults, framework, components, utilities;
这将建立层顺序
- 未分层样式(最强)
- utilities
- 组件
- framework
- defaults
- reset(最弱)
我们可以这样做任意次数,但请记住:重要的是每个名称*首次出现*的顺序。所以这将具有相同的结果
@layer reset, defaults, framework;
@layer components, defaults, framework, reset, utilities;
排序逻辑将忽略第二个 @layer
规则中 reset
、defaults
和 framework
的顺序,因为这些层已经建立。此 @layer
列表语法不会为层排序逻辑添加任何特殊魔法:层是根据*它们在代码中首次出现的顺序*堆叠的。在这种情况下,reset
首次出现在第一个 @layer
列表中。任何后面的 @layer
语句只能将层名称追加到列表中,*但不能移动已经存在的层*。这确保您始终可以从一个位置(样式的最开始)控制最终的整体层顺序。
这些层排序语句允许在样式表的顶部,在 @import
规则之前(但不能在导入之间)。我们强烈建议使用此功能预先在一个地方建立所有层,以便您始终知道在哪里查找或进行更改。
@layer
规则
块 @layer
规则的块版本仅采用单个层名称,但随后允许您向该层添加样式
@layer <layer-name> {
/* styles added to the layer */
}
您可以将大多数内容放在 @layer
块中——媒体查询、选择器和样式、支持查询等。您不能放在层块中的唯一内容是字符集、导入和命名空间之类的内容。但别担心,有一种将样式导入到层的语法。
如果之前尚未建立层名称,则此层规则会将其添加到层顺序中。但是,如果已建立名称,则允许您从文档中的任何位置向现有层添加样式,而无需更改每个层的优先级。
如果我们已经使用 layer 语句规则预先建立了层顺序,我们不再需要担心这些层块的顺序
/* establish the order up-front */
@layer defaults, components, utilities;
/* add styles to layers in any order */
@layer utilities {
[hidden] { display: none; }
}
/* utilities will override defaults, based on established order */
@layer defaults {
* { box-sizing: border-box; }
img { display: block; }
}
分组(嵌套)层
可以通过嵌套层规则对层进行分组
@layer one {
/* sorting the sub-layers */
@layer two, three;
/* styles ... */
@layer three { /* styles ... */ }
@layer two { /* styles ... */ }
}
这会生成可以通过将父名称和子名称与句点连接来表示的分组层。这意味着也可以从组外部直接访问生成的子层
/* sorting nested layers directly */
@layer one.two, one.three;
/* adding to nested layers directly */
@layer one.three { /* ... */ }
@layer one.two { /* ... */ }
层排序规则适用于每一级嵌套。任何未进一步嵌套的样式在该上下文中被视为“未分层”,并且优先于进一步嵌套的样式
@layer defaults {
/* un-layered defaults (higher priority) */
:any-link { color: rebeccapurple; }
/* layered defaults (lower priority) */
@layer reset {
a[href] { color: blue; }
}
}
分组层也包含在其父层中,因此层顺序不会跨组混合。在此示例中,首先对顶级层进行排序,然后在每个组内对层进行排序
@layer reset.type, default.type, reset.media, default.media;
导致层顺序为
- *未分层*(最强)
- default 组
- default *未分层*
- default.media
- default.type
- reset 组
- reset *未分层*
- reset.media
- reset.type
请注意,层名称也是作用域化的,因此它们不会与其嵌套上下文之外的同名层交互或冲突。两个组都可以具有不同的 media
子层。
当使用 @import
或 <link>
来分层整个样式表时,这种分组变得尤为重要。像 Bootstrap 这样的第三方工具,可以在内部使用层级——但我们可以在导入时将这些层级嵌套到一个共享的 bootstrap
层组中,以避免潜在的层级命名冲突。
@import
或 <link>
对整个样式表进行分层
使用 可以使用带有 @import
规则的新 layer()
函数语法将整个样式表添加到层中
/* styles imported into to the <layer-name> layer */
@import url('example.css') layer(<layer-name>);
还有一个提案建议在 HTML <link>
元素中添加 layer
属性——尽管这仍在开发中,并且 尚无任何地方支持。这可以用于导入第三方工具或组件库,同时将所有内部层级分组到单个层级名称下——或者作为将层级组织到不同文件的一种方式。
匿名(未命名)层
层名称 很有用,因为它们允许我们从多个位置访问同一个层进行排序或组合层块——但它们不是必需的。
可以使用块层规则创建_匿名_(未命名)层
@layer { /* ... */ }
@layer { /* ... */ }
或者使用导入语法,用 layer
关键字代替 layer()
函数
/* styles imported into to a new anonymous layer */
@import url('../example.css') layer;
每个匿名层都是唯一的,并添加到遇到它的层顺序中。匿名层不能从其他层规则中引用以进行排序或追加更多样式。
这些应该谨慎使用,但可能有一些用例
- 项目可以确保给定层的所有样式都必须位于单个位置。
- 第三方工具可以将它们的内部层级“隐藏”在匿名层中,这样它们就不会成为该工具公共 API 的一部分。
将值还原到上一层
我们可以使用几种方法将级联中的样式“还原”到由较低优先级来源或层定义的先前值。这包括许多现有的全局 CSS 值,以及一个新的 revert-layer
关键字,该关键字也将是全局的(适用于任何属性)。
上下文:现有的全局级联关键字*
CSS 有几个 全局关键字,可以用于任何属性,以帮助以各种方式回滚级联。
initial
将属性设置为应用任何样式(包括浏览器默认值)之前的_指定_值。这可能令人惊讶,因为我们通常将浏览器样式视为初始值——但是,例如,display
的初始值是inline
,无论我们将其用于哪个元素。inherit
将属性设置为应用其父元素的值。这是继承属性的默认值,但仍然可以用来删除以前的值。unset
的作用就像简单地删除所有先前的值一样——这样继承的属性再次inherit
,而非继承的属性返回到它们的initial
值。revert
只删除我们在作者来源(即站点样式)中应用的值。这在大多数情况下是我们想要的,因为它允许浏览器和用户样式保持不变。
revert-layer
关键字
新增:级联层添加了一个新的全局 revert-layer
关键字。它的工作原理与 revert
相同,但只删除我们在当前级联层中应用的值。我们可以使用它来回滚级联,并使用在先前层中定义的任何值。
在这个例子中,no-theme
类删除了在 theme
层中设置的任何值。
@layer default {
a { color: maroon; }
}
@layer theme {
a { color: var(--brand-primary, purple); }
.no-theme {
color: revert-layer;
}
}
因此,带有 .no-theme
类的链接标签将回滚到使用在 default
层中设置的值。当 revert-layer
用于非分层样式时,它的行为与 revert
相同——回滚到先前的来源。
还原重要层
如果我们将 !important
添加到 revert-layer
关键字,事情就会变得有趣。因为每个层都有两个不同的“正常”和“重要” 级联位置,这不仅仅是改变声明的优先级——它改变了哪些层被还原。
假设我们定义了三个层,它们在层堆栈中如下所示
- 实用程序(最强)
- 组件
- 默认值(最弱)
我们可以将其充实,不仅包括每个层的正常和重要位置,还包括非分层样式和动画
!important
默认值(最强)!important
组件!important
实用程序!important
未分层样式- CSS 动画
- 普通未分层样式
- 普通实用程序
- 普通组件
- 普通默认值(最弱)
现在,当我们在正常层中使用 revert-layer
时(让我们使用 utilities
),结果相当直接。我们_只还原该层_,而其他所有内容都正常应用
- ✅
!important
默认值(最强大) - ✅
!important
组件 - ✅
!important
实用程序 - ✅
!important
非分层样式 - ✅ CSS 动画
- ✅ 正常非分层样式
- ❌ 正常实用程序
- ✅ 正常组件
- ✅ 正常默认值(最不强大)
但是当我们将 revert-layer
移到重要位置时,我们会还原正常和重要版本_以及介于两者之间的所有内容_
- ✅
!important
默认值(最强大) - ✅
!important
组件 - ❌
!important
实用程序 - ❌
!important
非分层样式 - ❌ CSS 动画
- ❌ 正常非分层样式
- ❌ 正常实用程序
- ✅ 正常组件
- ✅ 正常默认值(最不强大)
用例:我什么时候想要使用级联层?
那么,我们可能会在什么样的情况下使用级联层呢?这里有几个例子,说明级联层在什么时候很有意义,以及在什么时候_没有_什么意义。
侵入性较低的重置和默认值
最初最明显的用例之一是使低优先级默认值易于覆盖。
一些重置已经通过在每个选择器周围应用 :where()
伪类来做到这一点。:where()
从应用它的选择器中删除所有特异性,这具有所需的基本影响,但也有一些缺点
- 它必须单独应用于每个选择器
- 重置内部的冲突必须在没有特异性的情况下解决
层级允许我们更简单地包装整个重置样式表,可以使用块 @layer
规则
/* reset.css */
@layer reset {
/* all reset styles in here */
}
或者当你导入重置时
/* reset.css */
@import url(reset.css) layer(reset);
或者两者兼而有之!层级可以嵌套而不改变其优先级。这样,你可以使用第三方重置,并确保它被添加到你想要的层级中,无论重置样式表本身是否在内部使用层级编写。
由于分层样式的优先级低于默认的“非分层”样式,因此这是在不重写整个 CSS 代码库的情况下开始使用级联层的好方法。
重置选择器仍然具有特异性信息来帮助解决内部冲突,而无需包装每个单独的选择器——但你也可以获得易于覆盖的重置样式表的预期结果。
管理复杂的 CSS 架构
随着项目变得越来越大、越来越复杂,为 CSS 代码的命名和组织定义更清晰的界限可能很有用。但是,我们拥有的 CSS 越多,发生冲突的可能性就越大——尤其是来自系统不同部分的冲突,比如“主题”、“组件库”或一组“实用程序类”。
我们不仅希望这些按功能组织,而且根据系统哪些部分在发生冲突时具有优先级来组织它们也很有用。Harry Roberts 的倒三角 CSS 很好地形象化了这些层可能包含的内容。
事实上,将层添加到 CSS 级联的最初提议使用 ITCSS 方法作为主要示例,以及开发该功能的指南。
这不需要特定的技术,但将项目限制为一组预定义的顶级层,然后根据需要使用嵌套层扩展该集合可能会有所帮助。
例如
- 低级重置和规范化样式
- 元素默认值,用于基本排版和易读性
- 主题,例如亮色和暗色模式
- 可能出现在多个组件中的可重用模式
- 布局和更大的页面结构
- 单个组件
- 覆盖和实用程序
我们可以在 CSS 的最开始创建顶层堆栈,只需一个层声明
@layer
reset,
default,
themes,
patterns,
layouts,
components,
utilities;
所需的具体层以及如何命名这些层,可能会因项目而异。
从那里,我们创建更详细的层细分。也许我们的组件本身内部就有默认值、结构、主题和实用程序。
@layer components {
@layer defaults, structures, themes, utilities;
}
在不改变顶层结构的情况下,我们现在有了一种方法可以进一步对每个组件内的样式进行分层。
使用第三方工具和框架
将第三方 CSS 集成到项目中是最容易遇到层叠问题的场景之一。无论我们使用的是像 Normalizer 或 CSS Remedy 这样的共享重置,还是像 Material Design 这样的通用设计系统,像 Bootstrap 这样的框架,或者像 Tailwind 这样的实用程序工具包,我们都无法始终控制网站上所有使用的 CSS 的选择器特异性和重要性。有时,这甚至扩展到组织中其他地方管理的内部库、设计系统和工具。
因此,我们经常不得不围绕第三方代码构建我们的内部 CSS,或者在冲突出现时使用人为提高的特异性或 !important
标记来升级冲突。然后我们必须随着时间的推移维护这些 hack,以适应上游的变化。
层叠层为我们提供了一种方法,可以将第三方代码插入到任何项目的层叠中的确切位置,无论内部如何编写选择器。根据我们使用的库类型,我们可以通过各种方式来做到这一点。让我们从一个基本的层堆栈开始,从重置到实用程序逐步向上。
@layer reset, type, theme, components, utilities;
然后我们可以整合一些工具…
使用重置
如果我们使用像 CSS Remedy 这样的工具,我们可能还有一些我们想要包含的自己的重置样式。让我们将 CSS Remedy 导入 reset
的子层中
@import url('remedy.css') layer(reset.remedy);
现在我们可以将我们自己的重置样式添加到 reset
层中,而无需任何进一步的嵌套(除非我们想要它)。由于直接在 reset
中的样式将覆盖任何进一步嵌套的样式,因此如果发生冲突,我们可以确保我们的样式始终优先于 CSS Remedy,无论新版本中发生什么变化。
@import url('remedy.css') layer(reset.remedy);
@layer reset {
:is(ol, ul)[role='list'] {
list-style: none;
padding-inline-start: 0;
}
}
由于 reset
层位于堆栈的底部,因此我们系统中的其余 CSS 将覆盖 Remedy 和我们自己的本地重置添加。
使用实用程序类
在我们堆栈的另一端,CSS 中的“实用程序类”可以是一种以广泛适用的方式复制常见模式(例如为屏幕阅读器添加上下文)的有用方法。实用程序倾向于打破特异性启发式方法,因为我们希望它们被广泛定义(导致低特异性),但我们通常也希望它们“赢得”冲突。
通过在层堆栈的顶部放置一个 utilities
层,我们可以实现这一点。我们可以以类似于重置示例的方式使用它,将外部实用程序加载到子层中,并提供我们自己的实用程序。
@import url('tailwind.css') layer(utilities.tailwind);
@layer utilities {
/* from https://kittygiraudel.com/snippets/sr-only-class/ */
/* but with !important removed from the properties */
.sr-only {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
-webkit-clip-path: inset(50%);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
margin: -1px;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}
}
使用设计系统和组件库
有很多 CSS 工具位于我们层堆栈的中间 - 结合了排版默认值、主题、组件和系统的其他方面。
根据特定的工具,我们可能会做类似于重置和实用程序示例的操作 - 但还有一些其他选项。一个高度集成的工具可能需要一个顶层。
@layer reset, bootstrap, utilities;
@import url('bootstrap.css') layer(bootstrap);
如果这些工具开始将层作为其公共 API 的一部分提供,我们还可以将其分解成多个部分 - 允许我们将我们的代码与库穿插使用。
@import url('bootstrap/reset.css') layer(reset.bootstrap);
@import url('bootstrap/theme.css') layer(theme.bootstrap);
@import url('bootstrap/components.css') layer(components.bootstrap);
@layer theme.local {
/* styles here will override theme.bootstrap */
/* but not interfere with styles from components.bootstrap */
}
将层与现有的(未分层、充满 !important 的)框架一起使用
与任何主要的语言变化一样,当 CSS 层叠层被广泛采用时,将会有一个调整期。如果你的团队准备在下个月开始使用层,但你最喜欢的框架决定再等三年才切换到分层样式,会发生什么?许多框架可能仍然会比我们希望的更频繁地使用 !important
!随着 !important
层的颠倒,这并不理想。
尽管如此,层仍然可以帮助我们解决问题。我们只需要聪明地利用它。我们决定我们想要为我们的项目设置哪些层,这意味着我们可以在我们创建的框架层之上和之下添加层。
但是现在,我们可以使用较低的层来覆盖框架中的 !important
样式,并使用较高的层来覆盖普通样式。像这样
@layer framework.important, framework.bootstrap, framework.local;
@import url('bootstrap.css') layer(framework.bootstrap);
@layer framework.local {
/* most of our normal framework overrides can live here */
}
@layer framework.important {
/* add !important styles in a lower layer */
/* to override any !important framework styles */
}
它仍然感觉有点像 hack,但它有助于我们朝着正确的方向前进 - 朝着更结构化的层叠前进。希望这是一个临时修复。
设计 CSS 工具或框架
对于维护 CSS 库的任何人来说,层叠层可以帮助进行内部组织,甚至可以成为开发者 API 的一部分。通过命名库的内部层,我们可以允许我们框架的用户在自定义或覆盖我们提供的样式时挂钩到这些层。
例如,Bootstrap 可以为他们的“reboot”、“grid”和“utilities”公开层 - 可能是按此顺序堆叠的。现在用户可以决定是否要将这些 Bootstrap 层加载到不同的本地层中。
@import url(bootstrap/reboot.css) layer(reset); /* reboot » reset.reboot */
@import url(bootstrap/grid.css) layer(layout); /* grid » layout.grid */
@import url(bootstrap/utils.css) layer(override); /* utils » override.utils */
或者用户可以将它们加载到 Bootstrap 层中,并穿插本地层。
@layer bs.reboot, bs.grid, bs.grid-overrides, bs.utils, bs.util-overrides;
@import url('bootstrap-all.css') layer(bs);
如果需要,还可以通过将任何私有/内部层分组在一个匿名(未命名)层中来向用户隐藏内部分层。匿名层将被添加到遇到它们的层顺序中,但不会暴露给重新排列或追加样式的用户。
!important
我只希望这一个属性更 与一些预期相反,层并不能轻松地快速升级特定样式,使其覆盖另一个样式。
如果我们的大多数样式未分层,那么任何新层相对于默认层都将被 _降级_。我们可以对单个样式块执行此操作,但很快就会变得难以跟踪。
层旨在更基础,而不是逐个样式,而是在整个项目中建立一致的模式。理想情况下,如果我们正确设置了它,我们可以通过将我们的样式移动到适当的(和预定义的)层来获得正确的结果。
如果我们的大多数样式已经属于定义明确的层,我们总是可以考虑在给定堆栈的顶部添加一个新的最高功率层,或者使用未分层的样式来覆盖这些层。我们甚至可以考虑在堆栈顶部放置一个 debug
层,以便在生产环境之外进行探索性工作。
但是,动态添加新层可能会破坏此功能的组织实用性,因此应谨慎使用。最好问:_为什么这种样式应该覆盖另一种样式?_
如果答案与一种 _样式类型_ 始终覆盖另一种样式类型有关,那么层可能是正确的解决方案。这可能是因为我们正在覆盖来自我们无法控制的地方的样式,或者因为我们正在编写一个实用程序,并且它应该移动到我们的 utilities
层中。如果答案与更有针对性的样式覆盖针对性较低的样式有关,我们可能会考虑使选择器反映这种特异性。
或者,在极少数情况下,我们甚至可能拥有真正重要的样式——如果您覆盖此特定样式,该功能根本无法正常工作。我们可能会说,将 display: none
添加到 [hidden]
属性属于我们最低优先级的重置,但仍然应该难以覆盖。在这种情况下,!important
确实是完成这项工作的正确工具。
@layer reset {
[hidden] { display: none !important; }
}
作用域和命名空间样式?不!
层叠层显然是一种组织工具,它可以“捕获”选择器的影响,尤其是在它们冲突时。因此,乍一看,很容易将它们视为管理作用域或命名空间的解决方案。
一个常见的本能反应是为项目中的每个组件创建一个层——希望这将确保(例如).post-title
仅应用于 .post
内部。
但是层叠冲突与命名冲突不同,层也不太适合这种类型的范围组织。层叠层不会限制选择器如何匹配或应用于 HTML,只会限制它们如何级联在一起。因此,除非我们能够确保组件 X 始终 覆盖组件 Y,否则单个组件层不会有太大帮助。相反,我们需要关注正在开发的提议的 @scope
规范。
将层和组件作用域视为重叠的关注点可能会有所帮助。
作用域描述了我们正在设置样式的内容,而层描述了我们设置样式的原因。我们还可以将层视为表示样式的来源,而作用域表示样式将附加到的内容。
测试你的知识:哪种样式会赢?
对于每种情况,假设此段落
<p id="intro">Hello, World!</p>
问题 1
@layer ultra-high-priority {
#intro {
color: red;
}
}
p {
color: green;
}
段落是什么颜色?
尽管该层的名称听起来非常重要,但未分层的样式在级联中具有更高的优先级。所以段落将是 green
(绿色)。
问题 2
@layer ren, stimpy;
@layer ren {
p { color: red !important; }
}
p { color: green; }
@layer stimpy {
p { color: blue !important; }
}
段落是什么颜色?
我们的正常层顺序在开始时就建立了——ren
在底部,然后是 stimpy
,然后(一如既往)未分层的样式在顶部。但这些样式并非都是 normal
(正常的),其中一些是重要的。我们马上就可以过滤掉只有 !important
的样式,而忽略不重要的 green
(绿色)。请记住,“来源和重要性”是级联的第一步,甚至在我们考虑分层之前。
这给我们留下了两种重要的样式,都在层中。由于我们重要的层被颠倒了,ren
移到顶部,stimpy
移到底部。段落将是 red
(红色)。
问题 3
@layer Montagues, Capulets, Verona;
@layer Montagues.Romeo { #intro { color: red; } }
@layer Montagues.Benvolio { p { color: orange; } }
@layer Capulets.Juliet { p { color: yellow; } }
@layer Verona { * { color: blue; } }
@layer Capulets.Tybalt { #intro { color: green; } }
段落是什么颜色?
我们所有的样式都在相同的来源和上下文中,没有一个被标记为重要,也没有一个是内联样式。我们这里确实有各种各样的选择器,从高度特定的 ID #intro
到零特异性的通用 *
选择器。但是,在我们考虑特异性之前,会先解析层,因此我们现在可以忽略选择器。
主层顺序预先建立,然后在内部添加子层。但是子层与其父层一起排序——这意味着所有 Montagues
将具有最低优先级,其次是所有 Capulets
,然后 Verona
在层顺序中拥有最终决定权。因此,我们可以立即过滤掉只有 Verona
样式,它们优先。即使 *
选择器的特异性为零,它也会赢。
小心将通用选择器放在强大的层中!
在浏览器开发者工具中调试层冲突
Chrome、Safari、Firefox 和 Edge 浏览器都有开发者工具,允许您检查应用于页面上给定元素的样式。此元素检查器的样式面板将显示应用的选择器,按其级联优先级排序(最高优先级在顶部),然后是下面的继承样式。由于任何原因未应用的样式通常会变灰,甚至会被划掉——有时还会提供有关样式未应用原因的附加信息。这是调试级联任何方面(包括层冲突)时首先要查看的地方。
Safari Technology Preview 和 Firefox Nightly 已经在此面板中显示(并排序)级联层。预计此工具将在与级联层同时在稳定版本中推出。每个选择器的层都列在选择器本身的正上方。
Chrome/Edge 正在开发类似的工具,并希望在级联层进入稳定版本时在 Canary(夜间)版本中提供它们。随着这些工具的更改和改进,我们将在此处进行更新。
浏览器支持和回退
此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器支持该版本及更高版本的功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
99 | 97 | 不支持 | 99 | 15.4 |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 127 | 15.4 |
由于层旨在作为整个 CSS 架构的基础构建块,因此很难想象像其他 CSS 功能那样构建手动回退。回退可能涉及复制大量的代码,使用不同的选择器来管理层叠层——或者提供一个更简单的回退样式表。
@supports
查询功能支持
使用 CSS 中有一个 @supports
功能,允许作者测试对 @layer
和其他 at 规则的支持。
@supports at-rule(@layer) {
/* code applied for browsers with layer support */
}
@supports not at-rule(@layer) {
/* fallback applied for browsers without layer support */
}
但是,目前还不清楚何时会在浏览器中支持此查询本身。
<link>
标签在 HTML 中分配层
使用 目前还没有关于从 html <link>
标签对整个样式表进行分层的语法的官方规范,但有一个 正在开发的提案。该提案包含一个新的 layer
属性,可用于将样式分配给命名或匿名层。
<!-- styles imported into to the <layer-name> layer -->
<link rel="stylesheet" href="example.css" layer="<layer-name>">
<!-- styles imported into to a new anonymous layer -->
<link rel="stylesheet" href="example.css" layer>
但是,不支持 layer
属性的旧浏览器将完全忽略它,并继续加载样式表而不进行任何分层。结果可能非常出乎意料。因此,该提案还扩展了现有的 media
属性,使其允许在 support()
函数中进行功能支持查询。
这将允许我们根据对分层的支持使分层链接成为条件。
<link rel="stylesheet" layer="bootstrap" media="supports(at-rule(@layer))" href="bootstrap.css">
潜在的 polyfill 和解决方法
主要浏览器都已转向 “常青”模型,更新会以相当短的发布周期推送给用户。即使是 Safari 也会在其较为罕见的主要版本之间的“补丁”更新中定期发布新功能。
这意味着我们可以预期这些功能的浏览器支持会非常快地增加。对于我们中的许多人来说,在短短几个月内就开始使用层可能是合理的,而无需过多担心旧浏览器。
对于其他人来说,可能需要更长的时间才能适应原生浏览器支持。还有很多其他方法可以管理层叠,使用选择器、自定义属性和其他工具。理论上也可以模拟(或 polyfill)基本行为。有人正在开发该 polyfill,但也不清楚何时才能准备好。
更多资源
CSS Cascade Layers 仍在发展中,但已经有许多资源,包括文档、文章、视频和演示,可以帮助您更加熟悉层及其工作原理。
参考
文章
- CSS 的未来:层叠层 (CSS
@layer
) 作者:Bramus Van Damme - CSS 层叠层入门 作者:Stephanie Eckles,Smashing Magazine
- 层叠层即将登陆您的浏览器 作者:Una Kravets,Chrome Developers
视频
- CSS !important 实际上是如何工作的? 作者:Una Kravets
- 新的 @layer 和 layer() CSS 原语概述 作者:Una Kravets
- CSS Revert 和 Revert-Layer 关键字 作者:Una Kravets
在使用 !important 时,这种层优先级的反转真的令人震惊,并且有可能造成很多混乱。我觉得这种行为应该在官方文档中更加突出 - 在本文中了解到它之后,我不得不一遍又一遍地搜索规范,直到找到说明它的段落。在 MDN 文档中,我找不到任何关于它的内容。
当将一些现有的带有 !important 的“坏”CSS 导入到一个层中时,这将尤其糟糕和令人困惑 - 如果你将第三方 CSS 加载到层中,这绝对不是你所期望的。
我觉得他们在这里犯了一个错误?层应该包含和控制 CSS 效果,但由于某种原因,!important 似乎比以前拥有更具破坏性的力量。将带有 !important 的导入规则的影响包含在最初造成严重破坏的文件中似乎更有用,通过分层给我们更多地从外部控制它们的能力。
如果有人可以提供解释,请这样做?
(鉴于 !important 现在比以前更危险,而且可能已经来不及更改它了,也许是时候完全弃用它了……)
同意。从表面上看,这似乎像是代码的孤岛,其中一组代码总是比另一组代码具有更高的特异性,这听起来非常有序且有用。使用
!important
会打破这种局面。当我们考虑revert-layer
功能时,这会变得非常糟糕。我觉得,虽然!important
今天不被认为是特别好的做法,但这种看法在未来会被大大放大。虽然确实有解决方法,但它看起来确实很hacky,其中一些覆盖必须在之前,一些在之后。这将成为采用道路上的一个重大障碍。更令人困惑的是,在
!important
上下文中并非所有内容都被反转。如果早期和晚期规则都被声明为重要,则晚期规则仍然会胜过类似的早期规则,更高的特异性会胜过较低的特异性,并且重要的内联规则会覆盖所有层。没有
!important
1. 内联
2. 非分层
2.1 非分层内的特异性
2.2 非分层内的顺序
3. 晚期层
3.1 晚期层内的特异性
3.2 晚期层内的顺序
4. 早期层
4.1 早期层内的特异性
4.2 早期层内的顺序
然后……
使用
!important
1. 内联
4. 早期层
4.1 早期层内的特异性
4.2 早期层内的顺序
3. 晚期层
3.1 晚期层内的特异性
3.2 晚期层内的顺序
2. 非分层
2.1 非分层内的特异性
2.2 非分层内的顺序
大家好,我认为你们有必要强调重要性反转是作者可能混淆的一点,我也希望 MDN 能够更多地关注它。我希望他们的页面现在会进行一些更新,因为该功能正在浏览器中推出,并且不再是理论上的。但是,一如既往,这将需要一些时间和精力(文档不是神奇的,但他们确实接受问题/请求和 PR)。
部分困惑在于,Web 作者主要将
!important
用作覆盖特异性的 hack,而它在整个层叠中起着不同的作用。因此,在某些方面,您可以将此更改视为“弃用”作者一直在使用的重要性 hack,并将其替换为显式分层功能。在过渡期间会有一些复杂情况需要管理,但除此之外,我希望使用层的作者希望开始删除以这种方式使用的重要标志——作为特异性覆盖。如果您仅将重要性视为简单的特异性覆盖 hack,那么层反转行为看起来非常令人困惑。这对该用例没有任何意义!但是一旦我们认为该用例已弃用……我们仍然保留了重要性功能的原始目的。作者不是唯一使用 CSS 的人,
!important
作为起源之间的平衡机制仍然有用且必要——一个与层相关的用例。不是简单的优先级提升(您可以为此使用新层),而是一种标记某些样式实际上是选择器模式按预期工作所*必需*的方式。该用例仍然存在,并且非常有用——我认为层反转行为与该用例非常匹配。我的观点是:相同的特性对于一个用例来说可能令人困惑,而对另一个用例来说可能会有所帮助。对于一种情况,它可以(非官方地)“弃用”,而在其他情况下它仍然是必要的和有用的。所以,是的,我同意这对于习惯了旧(以作者为中心)模式的人来说将是一个令人困惑的调整。但我认为这提供了一个非常强大的新模式,人们可以学习在他们的设计系统中使用它来获得很好的效果。
我希望作者花时间去学习重要性是如何使用的用例和心智模型,而不是简单地因为旧的心智模型不再适用就将其抛弃。
很棒的东西!目前正在将层叠层应用于一个新项目。关于
!important
:我发现使用层叠层几乎消除了对它的需求,甚至是主动使用,例如覆盖。值得注意的是,将
!important
添加到层会*反转*层顺序。假设您有这样的内容:-并且您导入了一些第三方代码并为其分配了层实用程序:-
现在,如果
blabla.css
中有一些!important
,我们将很难覆盖它。非分层样式无法覆盖它,因此唯一的解决方案是在层重置和默认中抛出!important
。我认为是时候弃用
!important
并让位给层叠层了,就像clip
被弃用而clip-path
被优先使用而不是它一样。此外,我认为开发人员很可能希望在 HTML 文件中使用
layer
属性,而不是希望在导入语句中使用它,因为通过外部 CSS 文件处理额外的请求肯定会导致性能问题。如果我理解这篇文章没有错,这可能是处理这个问题的方法
现在如果我再次使用你的例子
如果
blabla.css
包含一些带有!important
的样式,我们应该能够在重要层中使用另一个重要样式覆盖它吗?我花了很多时间在 Polypane 浏览器中实现了层支持,并于本月早些时候发布了它,领先于许多其他浏览器(Safari TP 更早)。
因此,如果您正在寻找专为开发人员构建的基于 Chromium 的浏览器,并且支持 @layer 并在其开发者工具中显示它们,请查看 Polypane。
这是一个快速屏幕截图,显示它在运行中,支持命名和未命名的层以及嵌套层
解决层叠复杂性的方案是……增加更多的复杂性?这就是为什么我使用 CSS Modules 的单类名。它完全打败了层叠。
它并没有打败层叠。层叠仍然发生。在整个层叠中,你只是扁平化了“特异性”,并且只在作者的普通和重要来源中。为了做到这一点,你必须放弃许多强大的选择器组合,添加许多唯一的类(使用额外的依赖项来确保唯一性),并在内部层叠冲突仍然可能的地方保持严格的“出现顺序”。
你可以这样做,但它只是在你不打算利用 CSS 提供的任何其他更明确的层叠管理工具的情况下才更简单。就像将工具包简化为只有锤子,因为螺丝刀增加了复杂性。但是,你用构建工具(如 CSS Modules)和唯一类约定的额外复杂性(功能)替换了该语言“复杂性”(功能)。因为你理解额外的语言/工具复杂性可以用来使我们的实际代码更清晰、明确和有意图。
当然,欢迎你使用你最喜欢的工具来管理层叠。但只要你在以任何方式使用 CSS,层叠就仍然是不可避免的,并且无法被打败。
我在这里遇到了困难。我看不到这些 CSS 层的用例。
我已经好几年没有遇到过特异性之争了。这要归功于命名空间。尤其是与我使用的重置或框架无关。
如果你看看这些用例(https://noti.st/jensimmons/QOEOYT/three-topics#syyV24S),你会发现 CSS 层并没有解决其中任何一个问题。我认为他们认为会有不同的来源,并且这些来源之间没有层叠。现在我们有了层,以及层叠。
所以你不能真正重构旧的样式。旧的样式总是会层叠到新的样式中,所以你总是需要再次覆盖它们。是的,你可以用较低的特异性来做到这一点,但除此之外,没有什么可以获得的,这是源代码顺序无法做到的。最重要的是,你不能并行运行旧样式和新样式,因为新样式的一部分(具有较低的特异性)会在旧样式中造成严重破坏。
第三方的东西也是一样,如果他们写的是糟糕的 CSS,那么可能会有一个
!important
在某个地方。为了覆盖某些样式,你在第三方层下面有一个层,但是对于!important
覆盖,你需要在第三方层上面再加一个层。我认为这增加了复杂性和可维护性。特别是如果解决方案是复制选择器并将其放在源代码顺序的后面。就这么简单。这表明真正的用例是有限的。在过去几周里,我读过的所有关于 CSS 层的博客文章,都有这些构建的理论示例。它们展示了层是如何工作的,但它们并没有展示一个真实的用例。
缺点
– 增加复杂性
– 更难维护
– 过于通用的选择器的副作用(特异性是有原因的)
– 打乱源代码顺序(意大利面条式 CSS 代码)
– CSS 解析速度变慢
优点
– 即使特异性较低,也很容易覆盖现有样式。
CSS 层在理论上听起来非常好,但在现实世界中,它们的用途有限。目前,它们没有解决我们无法用现有工具解决的任何问题。相反,它们创造了新的陷阱,并造成了对层叠、特异性和重要性的混淆。它们使 CSS 更难学习。它们增加了一层混乱(抱歉)。
现在,每次解析 CSS 文件时,都会浪费时间和精力,而收获却很少。感觉像是臃肿和功能过度。
我之前也不太喜欢这个功能(参见我上面的评论),但你的评论让我思考,我现在意识到层应该服务于什么目标。
乍一看,层似乎提供了一种方法来“封装”写得不好或半兼容的 CSS 模块,这样其中的所有内容都具有低优先级,而你在其上编写的所有内容都将覆盖它。我担心所涉及的复杂性,但我明白了这一点。你的(非常好的)观点让我意识到这根本不是层的用途。
相反,层可以服务于那些在编写时就考虑到了层的 CSS 模块。它们不服务于模块客户端,而是服务于模块作者!现在,模块作者可以使用在其自身模块逻辑中有效的任何选择器序列,而不必担心模块客户端稍后必须处理他们的选择器,因为分层系统会处理这个问题。
考虑到这一点,我明白了带有层反转机制的
!important
的意义。正如 Miriam 指出的那样,!important
现在可以被视为一种已弃用的工具,用于覆盖以前的 CSS 规则。在分层模块中以这种方式使用它会导致绝对的混乱。相反,
!important
传达了“此声明对于模块的正常工作至关重要。它必须能够抵抗任何未来未知的声明,并且你不得在后续模块中覆盖它”。现在,该机制积极阻止后续模块覆盖重要声明是有道理的。模块客户端可以决定无论如何都要覆盖它,但这只能通过专用的“覆盖”层来实现,而不是通过一些意外的代码。与旧版浏览器的向后兼容性如何?需要两个样式表 - 有层和无层。这不是 CSS 应该工作的方式。不应该有破坏性的更改。
所有新功能在旧浏览器中都会失效,这是无法避免的。向后兼容性要求是新功能不应该在任何浏览器中破坏旧的 **CSS**。
在这种情况下,与媒体查询一样,
@layer
内的任何代码都将对旧浏览器隐藏。并且正在开发一个 polyfill,它能够自动生成和链接旧版样式表。这不是一种理想的情况,但它是一种临时情况。有没有办法可以将层分配给不公开样式表的第三方组件,以便我可以将第三方组件的样式保持在我自己的层下面一层。
嗨,马克,理论上答案是“是的” - 因为来自任何地方的任何样式都可以添加到一个层中。但实际答案可能很大程度上取决于用于发布组件的确切工具链,以及用于应用它们的工具链。如果没有更多细节,我无法对此发表评论,即使那样,我也有经验的工具集也很有限。我会向发布组件的人员提交一个问题,看看他们是否有建议。
嘿!很棒的文章 :)
我想问一个问题。
有没有办法恢复整个层?就像单个 CSS 规则一样:
color: rever-layer
。我的意思是,可以根据媒体查询或类名动态更改层顺序吗?
你可以使用
all: revert-layer
回滚所有层样式,类似于使用all: revert
回滚作者样式。媒体查询绝对可以更改层顺序。
@layer
规则允许在媒体查询内部使用,并且仅在应用媒体查询时才会影响层的顺序。你可以这样做嘿 :)
正在为一个新项目试用层叠层,当我像这样为 Bootstrap 提供自己的层时,似乎遇到了“未定义的 mixin”错误
`// 层顺序
@layer bootstrap, theme, utilities, third-party;
// 变量
@import “1-variables/app”;
// 主题 mixin
@import “2-mixins/badge”;
@import “2-mixins/button”;
@import “2-mixins/modal”;
@import “2-mixins/switch”;
@import “2-mixins/tabs”;
@import “2-mixins/theme”;
// Bootstrap
@layer bootstrap {
@import “~bootstrap/scss/bootstrap”;
}
// 主题组件
@layer theme {
@import “3-components/accordion”;
@import “3-components/alert”;
@import “3-components/avatar”;
}`
或者甚至
@import '~bootstrap/scss/bootstrap' layer(bootstrap);
错误
ERROR in ./resources/sass/phoenix/phoenix.scss
Module build failed (from ./node_modules/laravel-mix/node_modules/mini-css-extract-plugin/dist/loader.js)
ModuleBuildError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js)
SassError: 未定义的 mixin。
╷
26 │ @include border-end-radius($alert-border-radius);
如果我 kemudian 导入 Bootstrap 到层外,项目可以正常构建。
如你所见,我的层的顺序表明 Bootstrap 是第一个,所以这对我来说很奇怪。
还有其他人遇到过这个问题吗?
我不知道你使用的是哪个版本的 Sass,但 Sass 并没有对层进行任何特殊处理。因此,它很可能将你的导入视为 CSS 导入(而不是 Sass 导入),因为它具有非 Sass 语法。
如果
~bootstrap/scss/bootstrap
同时提供 Sass 功能(mixin、变量等)并且还有直接的 CSS 输出,则需要对它们进行不同的处理。你希望将 CSS 输出放入一个层中,但保持 Sass 功能在任何地方都可用。这应该可以使用 Sass 模块 和更具体的 Bootstrap 路径来实现。类似于……从一个文件转发所有 Sass 功能
然后在你的主文件中,你可以
@use
你转发过的 Sass 功能,并单独加载来自 Bootstrap 的 CSS抱歉,那里有一个小错误。对于
load-css()
,你需要来自 Sass 的内置“meta”模块除了在典型网页中,它们的 !important 覆盖所有其他非层样式中的 !important 的情况外,我看不到使用层的任何价值。就是这样。即使这样也很令人困惑,因为它的第一个层覆盖了后面的层。超级愚蠢的设计。
事实是,一旦你在你的作者样式表中修改任何元素,比如 div、p、span 等,你就永远无法使用层使用任何层叠、特异性等来覆盖它们。而且大多数现代 Web 开发人员都会创建“重置”样式表来更改他们的元素设计。所以层就失效了。
可悲的现实是,预处理器、变量、层和其他新的 Web 技术是由那些从未理解层叠样式表如何工作以及基于文本的属性如何继承的人设计的。如果他们理解了,他们就会发现他们根本不需要这些工具。你不需要改变从典型网页的 body 元素继承下来的东西。当你需要改变时,你已经拥有了一个使用 ID 和 Class 的加权和类似起源、类似层的系统,它的工作原理与表单起源和层起源优先级完全相同,只需要一小部分 CSS 代码!
例如,任何通过 ID 修改的 HTML 都永远不会被一组权重更高的类覆盖。它们本质上仍然在它们自己的层中。
这就是为什么 CSS 20 多年来一直完美运行,并且很少需要更改的原因。
只是快速说明一下,现在可以使用 Postcss polyfill: https://npmjs.net.cn/package/@csstools/postcss-cascade-layers
它也包含在
postcss-preset-env
中: https://preset-env.cssdb.org/features/#cascade-layers我喜欢彩色的级联图像(https://css-tricks.org.cn/wp-content/uploads/2022/02/layers-tall-outlines2.svg),但是类不应该比元素更重要吗?这张图片目前看起来像是说 * -> .class -> element -> #ID。
好眼力!已经全部清理干净了。 ✨
如何通过导入外部css改变优先级?
@layer component-1,component,importLayer;
@import url('example.css') layer(importLayer)
componet 层不工作 importLayer?可以举个例子吗?