层叠样式表(CSS)是一种样式表语言,用于描述以 超文本标记语言 等标记语言编写的文档在屏幕、印刷品及其他媒介上的呈现方式。其核心思想是结构与表现的分离:标记说明一段内容是什么,样式规则则说明它应当如何呈现。由于万维网建立在这种分离之上,CSS与HTML、JavaScript一道,成为网络的核心技术之一。
"层叠"一词指的是层叠机制,即当来自多个样式来源的声明发生冲突时,将其合并并确定最终生效者的过程。这些样式来源包括浏览器的默认样式、作者编写的样式表以及用户的偏好设置。CSS的开发始于二十世纪九十年代中期的欧洲核子研究组织,此后由W3C的CSS工作组维护。它逐渐发展为一组不断扩充的模块,每个模块各自按照自身节奏走向成熟。因此,CSS已从最初少量的文本和颜色属性,发展成为能够处理布局、响应式适配、动画和色彩管理的完整体系。
本词条首先介绍CSS的起源与标准化,随后讲述其语法、选择器以及层叠与继承模型。接着论述值的计算、布局、响应式设计、视觉效果与动画,之后是打印与无障碍访问、工具链、浏览器实现,以及CSS与其他网络技术的关系。最后讨论批评意见、未解决的问题与未来发展方向。
定义与范围
层叠样式表(CSS)是一种用于描述结构化文档呈现方式的声明式语言。它的主要作用对象是 HTML、XML 和 SVG 等标记语言,其语法也用于其他文档格式。样式表不会改变文档的内容,它规定元素应当如何显示以及出现在什么位置,包括字体、颜色、间距和布局等。由于 CSS 是声明式的,作者只需列出期望的结果,由用户代理(通常是浏览器)决定如何实现。这与过程式程序不同,后者会向计算机给出逐步的指令。CSS 通常与 JavaScript 配合实现交互行为,但 CSS 本身并不执行此类计算。
CSS 的核心原则是内容与表现的分离。标记应描述结构和含义:这是标题,这是列表,这是引文。视觉呈现则保存在一个或多个独立的样式表中。这种分离带来了实际的好处。一份样式表可以统管成千上万个页面,因此全站改版只需修改一个文件,而无需逐个修改文档。作者可以专注于写作与结构,设计人员则可以在不改动文字的情况下调整外观。同一套标记还可以在不同场景下复用,例如手机屏幕、纸质印刷页面或语音输出,每种场景各配一套规则。早期网页通过 font 等表现性元素和属性将含义与外观混杂在一起,后来的 HTML 版本则废弃了这些特性,转而由 CSS 承担其功能。
CSS 使用一小套含义精确的术语。样式表由规则组成,也可以包含 at-规则,即以 @ 符号开头、用于控制其他行为的语句,例如条件块或导入。样式规则有两个部分:选择器,用于指明规则适用的元素;以及用花括号括起来的声明块。在声明块内,每条声明由属性和值组成,属性是表现方面的一个具名特征,如 color 或 font-size。"声明"一词指的是整个属性与值的组合,而不仅仅是属性名。在下面的例子中,h1 是选择器,color 和 font-size 是属性,花括号内的两行都是声明。
h1 {
color: navy;
font-size: 2rem;
}
CSS 的适用范围不限于屏幕上的视觉显示。该语言定义了媒体类型,即输出介质的类别,使样式表可以有选择地应用于不同场合。screen 类型涵盖电脑和移动设备屏幕。print 类型涵盖分页输出,如纸张打印,与其他条目中讨论的印刷术及页面排版规则密切相关。speech 类型涵盖音频输出,如由语音合成器朗读的文本;早期规范称之为 aural 类型。媒体类型使同一份文档无需修改标记即可以多种形式呈现。对于依赖辅助技术的用户而言,支持这些形式十分重要,这一话题与残疾及无障碍访问相关。
在此范围内,CSS 只关注呈现。它不定义文档的结构,那是标记语言的职责;也不定义页面的脚本行为。这些边界是内容与样式分离在实践中得以成立的重要原因。
起源与早期发展
在 CERN 提出构想
CSS 源于早期万维网中的一个实际缺口。Håkon Wium Lie 在 CERN 工作期间发现,万维网开始被用于电子出版,却没有办法为文档设置样式。例如,网页无法表达报纸式的分栏排版。1994 年,Lie 仍在 CERN 工作时便提出了 CSS 的构想。他的背景也影响了这一构想:他此前在麻省理工学院媒体实验室从事个性化报纸呈现方面的工作,由此得出结论,万维网需要一种独立的样式表语言。
这一构想并非当时唯一的方案。大约在同一时期,还出现了其他样式方案,例如 Style Sheets for HTML 和 JSSS,但 CSS 最终胜出。Lie 的方案与众不同之处在于它对层叠机制的处理:作者的意愿、读者的意愿,以及显示设备和浏览器的能力,必须以一种明确定义的方式结合起来。有些史料将他首次公开提出该方案的时间定在 1994 年 10 月 10 日,但各种资料在这些细节上说法不一。
非正式草案与早期合作者
此后,Lie 的设计从 1995 年开始以一系列非正式草案的形式流传开来。在回应第一份草案的人中,有正在开发 Argo 的 Bert Bos。Argo 是一款可高度定制的浏览器,支持样式表。Bos 决定与 Lie 联手。两人的合作产出了初始设计,后来的规范便是在此基础上构建的。
W3C 的采纳(1994–1995)
万维网联盟成立于 1994 年,旨在协调网络标准。1995 年底,它设立了 HTML 编辑评审委员会,负责审定未来的 HTML 规范。由于样式表属于新组织成员的关注范围,CSS 规范被列为一项工作事项,目标是使其成为 W3C 推荐标准。行业压力也起到了作用。微软宣布其浏览器支持 CSS 之后,W3C 力邀网景公司参与,因为支持不同规范的浏览器可能会把万维网引向不同的方向。1996 年 8 月发布的 Internet Explorer 3 成为首个支持 CSS 的商用浏览器,而此时 CSS1 规范尚未成为 W3C 推荐标准。
CSS 第 1 级与第 2 级
第一版规范于 1996 年 11 月 12 日进入提议推荐(Proposed Recommendation)状态,并于 1996 年 12 月 17 日成为 W3C 推荐标准。其范围有意保持克制:它涵盖应用于 HTML 文档的字体、颜色和间距等属性,并采用桌面出版领域的常用术语来描述样式。
CSS2 由一个于 1997 年 2 月成立的专门工作组开发,旨在补充 CSS1 中未涵盖的功能。它于 1998 年 3 月 24 日以提议推荐的形式发布,并于 1998 年 5 月 12 日成为 W3C 推荐标准。CSS2 建立在 CSS1 之上,增加了许多新功能,并保持完全向后兼容。其新增内容包括更完善的打印排版、定位与分层元素,以及可下载字体,还包括用于语音输出的听觉属性。它还引入了针对特定媒体的样式表,分别适用于视觉浏览器、打印机、盲文设备和手持设备。
标准化与模块化模型
CSS 由 W3C 的 CSS 工作组开发,每份规范都要经过一套固定的成熟度阶段。阶段标签按由不成熟到定型的顺序包括:First Public Working Draft(首份公开工作草案)、Working Draft(工作草案)、Candidate Recommendation(候选推荐标准)、Candidate Recommendation Draft(候选推荐标准草案)、Proposed Recommendation(拟推荐标准)和 Recommendation(推荐标准)。工作组的状态页面按此顺序列出这些代码。
W3C 用状态代码表示规范的成熟度。CSS 工作组使用的代码按稳定性从低到高依次为:工作草案是一种早期发布的文档,旨在征集外部评审意见,它并不表示已经达成共识。工作草案通常代表进行中的工作,以及 W3C 在某一领域推进工作的承诺。“工作草案”这一标签并不意味着 W3C 内部就该文档达成了共识。
从候选推荐标准到推荐标准
候选推荐标准阶段是测试阶段。此阶段的工作重点是检验规范能否被一致地实现,而不是给浏览器打分。退出 CR 阶段需要证明每项功能都有两个正确且相互独立的实现,因此在这一阶段,工作组要编写测试套件并生成实现报告。CSS 章程对每个模块重复了同样的关卡,要求每项功能在模块推进之前至少要有两个相互独立的实现。通过这一关的规范成为拟推荐标准,并提交给咨询委员会评审。拟推荐标准是指已借助实现经验,并已提交咨询委员会评审的候选推荐标准。正式批准后,文档才获得推荐标准的称号。该称号表明,文档预计不再需要太多改动,此时理应不再需要任何修改。
从整体性的级别到独立模块
CSS 最初按编号的级别组织,这种做法带来了一些摩擦。CSS2 在候选推荐标准阶段出现之前就已成为推荐标准。随着实现者不断报告问题,工作组决定不再继续扩充臃肿的勘误清单。工作组发布了修订版 CSS2.1,在两份文档存在冲突之处,以 CSS2.1 为准。尽管从技术上讲 CSS2 规范是 W3C 推荐标准,但它是在 W3C 定义候选推荐标准阶段之前进入推荐标准阶段的。随着时间推移,实现经验和进一步的评审揭示了 CSS2 规范中的许多问题,因此 CSS 工作组没有继续扩充已经十分臃肿的勘误清单,而是选择定义 CSS Level 2 Revision 1(CSS2.1)。
此后的工作被拆分为若干模块,每个模块都有各自的成熟度和时间表。模块可以取代早期级别的特定部分。例如,Selectors Level 3 取代了 CSS2§5 和 CSS2§6.4.3,并定义了扩展后的选择器范围。模块也可以在前代规范仍在使用时对其进行扩展。Selectors Level 4 在 Selectors Level 3 的基础上引入了新的伪类、伪元素和组合器。由于各模块的推进速度不同,因此不存在一份完整定义“CSS Level 3”或更高级别的单一文档。工作组解释说,由于不同的 CSS 模块处于不同的稳定性级别,CSS 工作组选择发布这一概况文档,用以界定在某一特定日期层叠样式表的当前范围和状态。
年度快照
每年发布的 CSS 快照是工作组为该语言确定参照点的方式。每份快照都是 W3C 工作组说明(Group Note),而非推荐标准,它汇集了在某一特定日期共同描述 CSS 的各个模块。2020 年的快照标准较为严格:此概况仅包含我们认为稳定、且有足够实现经验以确信其稳定性的规范。后来的快照放宽了这一规则,并按成熟度将模块划分为不同层级。例如,2023 年版列出了一类“相当稳定的模块”,其实现经验有限。2026 年版于 2026 年 6 月 22 日以工作组说明的形式发布,文中称其主要读者是实现者而非作者,因为它是按规范的稳定性而非网页浏览器的采用率来收录模块的。
工作组与关卡机制
CSS 工作组与浏览器厂商及受邀专家共同开发规范。因此,浏览器实现是这一流程的核心:模块不会仅因其文本完成就推进。在成为推荐标准之前,它必须被证明能够在各个引擎之间实现互操作。其结果是,快照可能将某个模块列为稳定,而该模块的某些功能在各浏览器中的支持仍参差不齐。阶段模型的设计目标是缩小这一差距,但无法将其完全消除。
语法与解析
规则、选择器与声明
样式表是由一系列规则组成的列表,解析器将每条规则视为 at-规则或限定规则之一。限定规则以前缀(prelude)开始,后接一个由 {} 包围的块,块中包含一系列声明。在样式规则中,前缀即选择器,用于决定该块作用于哪些元素。块内每条声明由属性标识符、冒号和值组成,分号用于将一条声明与下一条分隔开。块中最后一条声明可以省略分号,不过作者通常仍会保留,这样之后添加新行时更不容易出错。声明前后的空白没有意义,空的块也是合法的。
at-规则
at-规则以 at 符号 @(U+0040 COMMERCIAL AT)开头,后接一个标识符。其语法由核心语法模块定义,而每条 at-规则的含义则由引入它的模块定义。at-规则分为两种形式。语句型 at-规则(如 @import,以及普通形式的 @layer)以分号结尾。块型 at-规则(如 @media、@supports 和 @keyframes)带有一个 {} 块。块型 at-规则中可以包含嵌套的限定规则、at-规则或声明。嵌套的 at-规则和样式规则是该语言中相对较新的加入,而且 at-规则的含义取决于它出现的位置。例如,@import 必须位于普通规则之前,因此位置靠后的 @import 会被忽略,且它不能放在条件组中。相比之下,@media 和 @supports 作为条件组规则,用于包裹普通样式规则,并且可以嵌套使用。
词法分析与错误恢复
解析首先将文本转换为一串词法单元(token),例如标识符、字符串、数字、at-关键字和花括号。注释以 /* … */ 的形式书写,在此阶段即被丢弃,因此对层叠没有任何影响。格式错误的文本不会导致整个文件失效。CSS 中出现错误时,解析器会尝试优雅地恢复:只丢弃最少量的内容,然后回到正常解析。这一设计有其实际考虑:新语法在旧解析器看来就是错误,而恢复规则使作者可以添加新特性,而不会让样式表在旧引擎中完全失效。实际上,如果属性或值之一无效,则该属性-值对会被忽略并丢弃。未知的 at-规则会被跳过,直到其结束分号或块为止;未闭合的块则会在文件结束时被自动闭合。解析与验证是两个独立的步骤:每个结构被读取之后,用户代理会依据该结构的语法进行检查,凡未通过检查的内容都会被视为从未写过。一致性检查工具在存在错误时必须至少报告一个解析错误,但浏览器若选择不应用恢复规则,则可以在遇到第一个错误时停止。
嵌入样式与处理顺序
CSS 主要有三种方式作用于文档:通过 link 元素加载的外部样式表、文档内部的 style 元素,以及作用于单个元素的 style 属性。style 属性中只包含声明块的主体部分,不含选择器和花括号。在作者来源(author origin)内,这些来源的出现顺序很重要。来自 link 和 style 元素的规则按文档顺序收集,因此当两条权重相同的规则发生冲突时,位于后面的那条获胜。行内 style 属性的特异性高于任何选择器,因此会覆盖基于普通选择器的规则,除非某个样式表声明被标记为 !important。样式表中放置的 @import 会作为该样式表顺序的一部分被解析,这也是其在文件中位置很重要的原因。由于浏览器通常会等待样式表加载完成后才开始渲染,因此在外部文件与内联代码块之间的选择也会影响加载性能,这一话题本文其他部分会再作讨论。
选择器与特异性
CSS 规则由选择器和声明块组成。选择器说明规则作用于哪些元素。选择器由若干简单部分组合而成:类型选择器匹配元素名,例如 p;类选择器(.note)匹配 class 属性;ID 选择器(#header)匹配 id 属性;属性选择器([href])检测某个属性是否存在或测试其值;通配选择器(*)匹配任意元素。伪类(如 :hover 或 :focus-visible)匹配文档树中未明确写出的元素状态,伪元素(如 ::before 或 ::first-line)则指向元素的某个部分或生成的内容。由于这些构件可以组合使用,一个选择器就能精确描述文档中的某个位置,这也是选择器设计与 HTML 文档结构密切相关的原因。
组合器与结构匹配
组合器用于把复合选择器连接成链。后代组合器(空格)匹配任意深度的嵌套元素;子代组合器(>)只匹配直接子元素;通用兄弟组合器(~)匹配其后的所有同级元素;相邻兄弟组合器(+)只匹配紧随其后的同级元素。因此,ul > li + li 这样的选择器能够表达原本需要人工在标记中添加类名才能实现的关系。
若干函数式伪类进一步扩展了这种结构匹配。:is() 接受一个选择器列表,匹配符合其中任一参数的元素,从而缩短冗长且重复的选择器。:where() 接受同样形式的列表,匹配行为相同,但自身不贡献任何特异性。:has() 根据元素所包含的内容或其后续的元素进行匹配。例如,使用 :has() 的选择器可以只选中包含图片的 figure 元素。:has() 的参数是相对选择器列表,因此可以以组合器开头,例如"后面紧跟段落的标题"。这些函数式写法在浏览器中的支持晚于基础选择器,其普及程度还取决于各引擎如何实现选择器列表的容错解析。
特异性如何给选择器排序
当多个声明作用于同一元素的同一属性时,浏览器需要一种方式在它们之间做出选择。特异性算法会为每个选择器分配一个权重,并对权重进行比较。在常见的表述中,权重是一个元组 :其中 统计内联样式属性的数量, 统计 ID 选择器的数量, 统计类选择器、属性选择器和伪类的数量, 统计类型选择器和伪元素的数量。通配选择器和组合器不计入。比较时从左到右逐位进行,因此一个 ID 的权重超过任意数量的类,而且该元组不会被加总成单个数字。为方便起见,若写成数字形式, 就胜过 。例如,选择器 #nav li.active a:hover 的权重为 :包含一个 ID、两个类或伪类(.active 和 :hover),以及两个类型选择器(li 和 a)。
有两个细节在实践中很重要。第一,函数式伪类的匹配遵循其自身规则。:is() 采用其中特异性最高的参数的权重,因此 :is(.card, #featured) p 的权重与其中的 ID 参数相同。:where() 始终贡献零特异性,因此 :where(.card, .panel) p 的权重仅相当于 p。:has() 同样采用其参数中特异性最高者的权重,因此使用它的选择器的特异性取决于其参数列表的内容。第二,!important 标志和层叠来源会在特异性之前被评估,而本文其他部分介绍的层叠层也可以覆盖特异性的结果。特异性只在这些较早的步骤之后仍然相持不下的声明之间起决定作用。
作为决胜依据的源顺序
如果两个声明的来源、重要性和层叠层相同,且其选择器的特异性也相同,那么在层叠中位于后面的声明胜出。因此,源顺序只在最后一步才起作用,即由样式表中的位置,或样式表被链接的先后顺序决定结果。这种设计意味着,一条位置靠后但选择器较弱的规则,仍可能输给位置靠前但选择器较强的规则。当样式分散在许多文件中时,开发者经常会遇到这种情况。本文后面各节所述的许多维护难题,都源于特异性与顺序之间的这种相互作用,这也促使人们采用 :where() 这类低特异性的选择器,并最终引入了将层叠明确划分为层的机制。
层叠、继承与层叠层
来源与重要性
每条声明都带有一个来源(origin),层叠机制据此为其排定优先级。作者来源指网站开发者编写的样式,用户来源指在浏览器设置中指定的样式,用户代理来源指浏览器自带的默认样式。操作系统设置和浏览器扩展也属于用户来源的样式。它们可能来自操作系统层面,例如基础字体大小或“减少动态效果”的偏好设置,也可能来自浏览器扩展。
来源与重要性共同决定优先级。浏览器会根据来源和重要性,把每条声明归入六个类别之一,共有八个优先级层次:六个来源类别、正在过渡的属性,以及正在动画的属性。在普通声明中,用户代理样式的优先级最低,其次是用户样式,作者样式最高。带有 !important 的声明则顺序相反,因此用户代理的 !important 规则位于来源层级的最顶端,而过渡效果的优先级高于其他所有类别。相关性是第一步:层叠机制先找出选择器与每个元素相匹配的所有声明块。
!important 标记的设计初衷,是为用户提供一种制衡作者的手段。CSS 试图在作者样式表和用户样式表之间建立权力平衡。默认情况下,作者样式表中的规则会覆盖用户样式表中的规则。在 1998 年发布的 CSS2 规范中,用户的 !important 规则会覆盖作者的 !important 规则。这一规定相对 CSS1 是语义上的改动,因为在 CSS1 中,作者的 !important 规则优先于用户的 !important 规则。实践中,!important 常被当作快速修补手段滥用。它看似简单有效,但往往只是把特异性之争从普通声明转移到了重要声明上。
继承与默认关键字
并非所有属性的层叠方式都相同。可继承属性(如颜色和大多数字体属性)在没有声明生效时,会从父元素获取其值;而大多数盒模型属性(如 margin、padding 和 border)则不会继承。由于颜色可以继承,嵌套在另一元素内的链接会沿用继承来的颜色,除非某条规则对其进行了覆盖。
四个全局关键字允许作者显式控制这一行为。inherit 复制父元素的计算值;initial 则恢复属性的规定默认值。unset 对可继承属性的作用与 inherit 相同,对其他属性的作用与 initial 相同。revert 是四者中作用范围最具体的一个:它会把层叠后的值回退到更早的来源。对于用户来源而言,它会把层叠值回退到用户代理层级,使该元素上这一属性的规定值,如同没有任何作者来源或用户来源的规则作用于它那样进行计算。
层叠层
通过 @layer 规则创建的层叠层(cascade layers),在层叠机制中增加了一个先于特异性进行评估的步骤。该功能最初由 CSS 规范编写者 Miriam Suzanne 于 2019 年提出。在每个来源与重要性类别内,层叠机制会先按层进行排序。普通声明的层顺序从最先创建的层到最后创建的层排列,未归入任何层的普通样式排在最后。对于重要声明,这一顺序则反转过来,未分层的重要样式优先级最低。
其实际结果是:层的优先级总是高于选择器的特异性,而败给层的选择器,其特异性无论多高都不起作用。在同一层内部,冲突仍由特异性和源码顺序决定。如果没有显式声明层的顺序,层会按首次出现的先后排序。一旦 CSS 被拆分到多个文件中、经过打包,或以不可预测的顺序导入,这种隐式排序就很难理解。层还可以相互嵌套。
使用 @scope 进行作用域限定
@scope 规则用于限制一组规则可以匹配的范围。它设定一个作用域根(scoping root),确定所针对子树的上边界;设定根之后,其中的样式规则只能选中该受限子树中的元素。可选的作用域边界(scoping limit)则设定下边界,从而形成一个“甜甜圈”式的作用域,排除其中嵌套的子组件。在代码块内部,:scope 选择器指向作用域根本身。
@scope 还引入了一个层叠判定标准,称为作用域邻近度(scoping proximity)。当两个作用域的样式发生冲突时,应用的是沿 DOM 树向上到达其作用域根所需“跳数”最少的那条样式。这一步位于特异性之后、源码顺序之前。因此,邻近度可以解决原本会由源码顺序决定的主题冲突;但重要性、层和特异性仍然优先于它。作用域限定只是限制选择器的影响范围,而非完全隔离某个组件,因此它不能替代 Shadow DOM。
值的计算与单位
从声明值到实际值
CSS 属性写下之后并不会立即生效。对于每个元素上的每个属性,浏览器都会执行一个简短的处理流程。层叠首先决定哪些声明生效,其结果即为指定值(specified value)。如果没有任何声明生效,继承属性取其父元素的计算值,其他属性则取其初始值,初始值由该属性的规范定义。计算值是在不进行页面布局的前提下,尽可能解析相对值后得到的结果。例如,em 长度在此阶段会变为绝对的像素长度,因此对于字体大小为 16px 的元素,设置 padding-top: 2em 的计算值为 32px。计算值正是子元素所继承的值,这也是为什么向下传递的是父元素解析后的长度,而不是原始的相对表达式。
使用值(used value)在之后确定,此时包含块尺寸等布局信息已可获得。百分比在此处被解析为像素,而 width 等尺寸的使用值以像素表示。实际值(actual value)是最后一步:它是用户代理在使用值的基础上,根据其运行环境所做的必要近似处理之后得到的值。例如,某个只能绘制整像素边框的浏览器,会把小数的边框宽度取整为整数。在 CSS 2 的模型中,这些阶段被描述为一个四步计算过程,后来的规范沿用了同样的术语。
长度单位
长度单位分为两类。绝对单位不依赖于其他值。最常用的是 px,在 CSS 中它是一个参考像素,相对于观看环境定义,不一定等同于单个物理设备像素。其他绝对单位包括厘米、毫米、英寸、点(1pt 等于 1/72 英寸)和派卡,它们主要用于印刷场景。设计师通常用 px 处理细节,例如一像素的边框、阴影以及小型的固定装饰。
相对单位则依赖于某个其他参照量。em 用于 padding 等属性时以元素自身的字体大小为基准,用于 font-size 时则以父元素的字体大小为基准,因此嵌套的 em 值会逐层叠加。rem(根 em)以根元素的字体大小为基准,通常为 16px,除非作者另行修改,它能在整个页面上提供可预测的缩放比例。百分比相对于随属性而变的参照量进行解析,通常是包含块。ch 以当前字体中 "0" 字形的前进宽度(advance width)为基准,常用于限制行长。视口单位如 vw 和 vh 分别是视口宽度和高度的百分之一,vmin 和 vmax 则分别取两者中较小或较大的一个。在移动端浏览器中,工具栏会展开和收起,因此小视口、大视口和动态视口的变体(svh、lvh、dvh)描述了不同的可见高度,100vh 并不总是用户实际看到的高度。容器查询单位如 cqi 以祖先容器而非视口为基准进行度量,将在响应式设计一节中讨论。
数学函数与流式尺寸
calc()、min()、max() 和 clamp() 这几个函数允许在单个声明中组合不同单位,并在样式解析时计算出结果。calc() 支持四则运算并遵循通常的运算优先级,允许混用不同单位,因此可以将宽度表示为百分比减去一个固定偏移量。在 CSS 语法中,加号和减号两侧必须有空格。min() 取其参数中的最小值,max() 取最大值。clamp(MIN, VAL, MAX) 等价于 ,因此中间值会被使用,除非它超出了两个边界所限定的范围。
这些函数是实现流式尺寸的常用工具。流式尺寸是指让尺寸随视口连续缩放,而不是在固定断点处突变的做法。例如,将标题声明为 font-size: clamp(1rem, 2vw, 2rem),它会随窗口宽度增长,为保证可读性不会小于一个根相对单位,而在超宽屏幕上也不会超过设定的最大值。作者对内边距、间距和容器宽度也采用同样的模式,例如使用 width: min(100% - 2rem, 68ch),使文本栏既不超出可用空间,又保持舒适的行长。
自定义属性与 var()
自定义属性通常被称为 CSS 变量,其名称以两个连字符开头,且区分大小写,因此 --Brand 和 --brand 是两个不同的属性。它们的值以原始记号流(token stream)的形式存储,直到通过 var() 替换到另一个属性中时才会被解释。该函数接受一个自定义属性名和可选的回退值,回退值可以嵌套,从而形成一系列备选方案。由于规范禁止自定义属性引用其自身,循环定义是无效的。
与 Sass 或 Less 等预处理器中的变量不同,自定义属性存在于最终发布的 CSS 中,并由浏览器在每个元素上进行解析。它们参与层叠,遵循优先级、源码顺序和 !important 的规则,并默认从父元素继承。它们的值在每个元素上只计算一次,计算结果即为后代所继承的值,因此在某个子树上被覆盖的值会改变所有引用它的声明。这种运行时行为使得值可以通过媒体查询、类的切换或脚本进行修改,而无需重新生成样式表。不过也存在一些限制:var() 不能用于属性名和选择器,也不能用于媒体查询和容器查询的条件中;浏览器还会限制嵌套替换的展开深度。@property 规则可以注册一个自定义属性,并指定其语法、初始值和是否可继承,从而为作者提供普通声明所缺乏的控制能力。
盒模型与布局系统
盒模型
每个由 CSS 布局的元素都会生成一个或多个矩形盒子。从内到外,盒子依次包括内容区、内边距、边框和外边距。内容区容纳文本或子盒子。内边距位于内容与边框之间,元素的背景会延伸经过内边距区域。边框包围内边距。外边距位于边框之外,用于将盒子与相邻盒子隔开,它是透明的。
默认情况下,声明的宽度和高度只作用于内容区。内边距和边框会在此基础上额外增加,因此渲染宽度为 ,不包括外边距。例如,在默认盒模型下,一个设置了 width: 200px、padding: 20px、border: 5px solid 的盒子会占据 250 像素。这一行为由 box-sizing 属性控制。如果元素带有边框或内边距,则需要将其加到宽度和高度上,才能得到在屏幕上渲染的盒子尺寸。
将 box-sizing 设为 border-box 会改变计算方式。此时声明的尺寸包含内边距和边框,内容区会相应缩小以腾出空间。按照同样的规则,上例中的盒子仍保持 200 像素。开发者常对所有元素(包括通过通配选择器作用于伪元素)应用 box-sizing: border-box,这是样式表中常见的重置做法。一些内置元素本身就使用这种模型:表格、选择框和按钮,以及复选框和单选框等某些 input 类型,默认即为 border-box。内容区不能为负值,因此 border-box 不能使元素完全塌缩。
显示、流与格式化上下文
display 属性决定盒子如何参与布局。在正常流中,块级盒子垂直堆叠,每个都占据可用的全部行内宽度;行内级盒子则位于文本行内,空间不足时换行。块级容器会建立块级格式化上下文,在其中相邻的垂直外边距可以合并,浮动元素被包含在内。行内内容在行内格式化上下文中排列为行框。某些 display 值(如 flex、grid)以及 flow-root 值会为其子元素建立新的格式化上下文,从而改变这些子元素的排列方式。
定位与层叠
position 属性提供五种定位方案。static 为默认值,使元素保持在正常流中。relative 使元素相对于其正常位置偏移,但保留其原本占据的空间。absolute 使元素脱离文档流,并相对于其最近的已定位祖先元素进行定位。fixed 使元素相对于视口定位,除非某个祖先元素具有 transform,使其成为包含块。sticky 在元素到达滚动阈值之前表现如同 relative,之后则在其滚动容器内像 fixed 一样保持位置。
已定位元素,以及带有 opacity 小于 1 或 transform 等属性的元素,可以创建层叠上下文。在层叠上下文内部,绘制顺序由 z-index 决定;一个上下文中很大的 z-index 值无法使元素越过兄弟层叠上下文之上。理解层叠上下文通常是解释为何修改 z-index 看似无效的必要前提。
弹性盒与网格
弹性盒子沿单一主轴排列项目。其 grow、shrink 和 basis 因子用于分配剩余空间或吸收溢出,因此非常适合工具栏、导航栏等一维的行或列布局。
网格布局可同时处理两个维度。它定义了一套基于二维网格的布局系统,专为用户界面设计而优化。行与列被定义为轨道,通常使用 fr 单位确定尺寸,项目可放置于任意单元格或跨越多个单元格。网格布局大约在 2017 年开始在主流浏览器中可用。
Grid Level 2 增加了子网格(subgrid)。subgrid 值表示网格将在该轴上采用其父网格所跨越的部分。因此,嵌套网格会沿用父网格的轨道,即使嵌套多层,其项目也能与外层布局对齐。子网格最初属于 Grid Level 1,但由于其复杂性,工作组于 2017 年将其移至 Level 2。当时没有任何浏览器实现它,直到之后几年才得以广泛使用。
响应式设计与媒体查询
响应式设计是指只编写一份样式表和一份文档,使布局能够适应显示设备的空间大小与能力的做法。CSS 为此提供了大部分机制。主要工具包括:检测运行环境的条件规则、能让尺寸按比例缩放的弹性单位,以及近来出现的、检测单个组件而非整个窗口尺寸的条件。
媒体查询
@media 规则将样式与输出设备的条件关联起来。其支持的特性包括视口宽度和高度、方向、宽高比、显示分辨率,以及用户偏好,例如 prefers-color-scheme、prefers-reduced-motion 和 prefers-contrast。早期的查询依赖 CSS3 媒体查询,它在 2008 年和 2009 年前后接近实际可用的程度,较旧的 min- 和 max- 前缀至今仍很常见。较新的语法来自媒体查询第 4 级,它允许在范围上下文中进行比较,因此条件可以用普通运算符书写,而无需前缀。规范还规定媒体特性只接受单个值,因此不能将复杂的计算式作为测试值传入。
偏好类特性定义于媒体查询第 5 级。该特性定义在 CSS 媒体查询第 5 级中,各方来源对其当前的取值列表说法不一。有些资料描述在 light 和 dark 之外还有第三个值 no-preference,而另一处说明则称第三个选项 no-preference 因缺乏实现而最近已从规范中移除。因此,作者应将 light 视为默认值,除非查阅当前的规范。
容器查询
容器查询将同样的条件逻辑应用于祖先元素的尺寸。@container 规则使样式能够基于包含元素的尺寸或其他特性来应用到元素上,而非基于视口。这对于卡片、导航栏或小部件之类的组件很重要,因为它们可能出现在宽阔的主栏中,也可能出现在狭窄的侧边栏中。能够响应自身容器的组件可以在两处复用,而无需为每种语境单独设置类名。
只有当元素被声明为查询容器时,它才会成为查询容器。通过将 container-type 属性(或 container 简写)设为 size 或 inline-size,元素即被声明为尺寸查询容器。inline-size 值仅在水平书写模式下检测宽度,这已覆盖大多数响应式场景;size 则同时检测高度。其代价是,这种包含限制了容器自身尺寸对子元素的依赖,因此容器无法简单地收缩以适应它本应测量的内容。通过 container-name 指定的命名容器,可以在多个容器嵌套时让查询指向特定的祖先。容器样式查询是另一项独立功能,用于检测祖先元素上某个自定义属性或其他声明的计算值。
容器查询还增加了相对长度单位,如 cqw 和 cqi,它们度量的是容器尺寸的一个比例,而非视口的比例。其支持比媒体查询来得晚。浏览器先推出了尺寸查询,之后再扩展其功能;据一份指南称,该特性在 2024 年达到了广泛可用(Baseline Widely Available)状态。一些较早的文章仍报告 Safari 和 Firefox 不支持,因此应根据目标受众的当前兼容性数据来核实。
流式布局与相对单位
媒体查询在与平滑伸缩、而非在固定尺寸之间跳变的布局配合使用时效果最佳。百分比、em 和 rem 单位、视口单位(如 vw 和 vh),以及 min()、max() 和 clamp() 函数,都能让一个值在上下限之间增大或减小。例如,标题可以使用随视口缩放的字号,但既不会低于可读的最小值,也不会超过设定的最大值。其他小节所述的 Flexbox 和 Grid 则提供了布局结构,使这些值得以发挥作用。
伊桑·马科特(Ethan Marcotte)在 2010 年 5 月发表于《A List Apart》的一篇文章中为这一方法命名,他类比了响应式建筑,并描述了三项技术:弹性网格、弹性图片和 CSS 媒体查询。这些想法更早就已出现。自适应网页设计(adaptive web design)这一技术早于马科特首次使用“响应式网页设计”这一术语。移动优先设计和渐进增强在该术语出现之前也已确立。
减少对脚本的依赖
在这些特性成熟之前,开发者常用 JavaScript 检测屏幕宽度或设备类型,然后加载或重排标记。这类脚本很脆弱,会增加页面加载时间,并且在浏览器窗口调整大小或设备报告异常尺寸时很容易出错。CSS 中的声明式条件把这部分逻辑大量转移到了样式表中,浏览器可以在布局时直接求值,无需运行应用代码。JavaScript 对于改变文档结构等行为仍是必要的,但页面的视觉适配现在大多在 CSS 中完成。
颜色、字体排版与视觉样式
颜色模型与 color-mix()
早期的 CSS 颜色以关键字、十六进制代码或基于 sRGB 的 rgb() 和 hsl() 函数书写。这些模型并不会随感知亮度的变化而均匀变化,因此两种颜色之间的中间色往往显得发暗。较新的色彩模型解决了这一问题。Oklab 是 CSS 工作组在 W3C 的 CSS Color 系列规范中发布的一种色彩空间,它的前身是 CIELAB。现代 CSS 还通过 color(display-p3 …) 提供广色域支持,可以表示超出 sRGB 范围的颜色。
支持色彩空间的 <code>color-mix()</code> 函数用于混合两种颜色。其中的 <code>in</code> 关键字用于指定混合所在的色彩空间。若未指定色彩空间,默认值为 oklab;对于极坐标色彩空间,还可以指定色相插值方法,默认取较短的色相路径。一些流行指南称其默认值为 oklch,因此作者应以规范为准,而不要依赖二手资料。这一选择很重要:在 oklch 中混合能保持感知上的均匀性,而在 srgb 中混合则可能产生浑浊的中间调,对互补色尤其明显。由于结果在运行时计算,单个自定义属性即可驱动整套色调与明度梯度,因此该函数常被视为取代了 Sass 中 lighten() 和 mix() 等颜色函数。
字体、字体加载与文本
自定义字体通过 <code>@font-face</code> 规则声明,该规则将字族名称映射到一个或多个字体文件,并通过 <code>font-weight</code>、<code>font-style</code> 和 <code>unicode-range</code> 等描述符加以限定。该规则允许将一个字族拆分为一组独立的资源,使页面无需下载包含所有变体的单一文件。
<code>font-display</code> 描述符控制字体加载期间读者所看到的内容,其取值包括 auto、block、swap、fallback 和 optional。block 取值会先有一段较短的不可见期(通常约三秒),随后是无限的替换期;swap 则使用极短的不可见期,之后一旦网络字体可用便立即替换。使用 optional 时,若字体未能及时就绪,浏览器甚至可能完全不替换字体。这些选项需要在"文字不可见闪烁"与备用字体被替换时出现的"无样式文字闪烁"之间权衡,因此也关系到布局偏移。
网络字体与其备用字体之间的字体度量(如上升部和下降部)存在差异,这是造成布局偏移的常见原因。CSS Fonts 模块第 5 级的提案中提出了 <code>ascent-override</code>、<code>descent-override</code> 和 <code>line-gap-override</code> 等描述符,以便调整备用字体使其与网络字体匹配。其中一个相关的描述符 advance-override 此后已从提案中删除。<code>line-height</code>、<code>letter-spacing</code> 以及较新的 <code>text-wrap</code> 取值(balance 和 pretty)等文本属性,控制着换行方式和段落的疏密程度。它们的表现既与所渲染的Unicode统一码文本有关,也与字体自身的度量相互作用。
背景、边框、阴影、渐变与图像
单个元素可以拥有多个背景层,每层都有各自的图像、尺寸、位置和裁剪区域。边框可以设置圆角,还可以通过 <code>border-image</code> 用图像填充。盒阴影接受偏移、模糊、扩展和内阴影等参数,无需额外标记即可营造层次感。渐变(线性、径向和锥形)本身就是图像,因此可以用于 <code>background-image</code> 或 <code>mask-image</code>。自 CSS Images 第 4 级起,渐变还可以指定插值色彩空间:线性渐变可以设定为在 oklab 等具名色彩空间中进行过渡,而不是默认的 sRGB。无论是位图还是矢量图,图像都可以通过 <code>object-fit</code> 和 <code>background-size</code> 调整尺寸与裁剪方式。
滤镜、混合模式与变换
CSS 滤镜函数(如 blur、brightness、contrast、grayscale 和 drop-shadow)会改变元素渲染后的像素。<code>backdrop-filter</code> 则有所不同:它不改变元素自身的内容,而是对元素背后的内容施加滤镜。毛玻璃效果即基于此实现。
<code>mix-blend-mode</code> 属性决定元素的像素如何与同一层叠上下文中的背景合成。其关键字取值包括 multiply、screen、overlay、darken、lighten、difference、exclusion,以及 hue、saturation、color 和 luminosity 等模式,这些模式借鉴自图像编辑软件。该属性设定元素内容应如何与其背景(即同一层叠上下文中渲染在其后方的内容)混合。两种效果的组合存在局限:据一位从业者的测试,使用 mix-blend-mode 时,backdrop-filter 的模糊效果会丢失。
渲染开销十分可观。滤镜和不透明度会创建层叠上下文,而背景滤镜原则上必须能够读取其之前绘制的全部内容。每次使用 backdrop-filter 或 mix-blend-mode 都可能需要一次单独的渲染过程,这可能使渲染时间加倍,并增加内存和 GPU 带宽的需求。W3C 的 Backdrop Root 提案和 Filter Effects 第 2 级草案通过定义一个边界来解决这一问题,限制这些效果向后追溯的范围。在实践中,各浏览器引擎会在 GPU 上加速背景滤镜,但仍建议作者避免将其用于频繁动画或在滚动时覆盖其他内容的元素。
过渡、动画与交互性
CSS 提供三类运动机制:在两个计算状态之间建立过渡的 transition、运行一系列状态的关键帧动画,以及从滚动位置或 DOM 状态变化中驱动进度的较新机制。它们共同让设计者无需编写帧循环脚本就能为 HTML 页面添加动效,不过许多动效仍需 JavaScript 来触发。
过渡
过渡是指当样式发生变化时,属性从旧值平滑插值到新值,而不是瞬间跳变。它由四个长写属性控制:transition-property 指定要做动画的属性,transition-duration 设定变化所需的时长,transition-delay 推迟开始时间,transition-timing-function 决定变化的节奏。一些较早的教程称 CSS 默认以匀速过渡元素,但规范中时序函数的初始值实际是 ease。
时序函数有时也称为缓动函数。关键字包括 ease、linear、ease-in、ease-out、ease-in-out、step-start 和 step-end,而三次贝塞尔曲线和阶跃函数则允许自定义曲线。正式定义位于单独的模块中。其输入值是过渡时长的百分比进度,时序函数的输出会对这一进度进行重新塑形,然后再对属性进行插值。
过渡有一些细微的规则。正在运行的过渡即使其设置值在途中发生变化,仍会保留原有的时间参数;但如果 transition-property 的变化使过渡本不该开始,则过渡会停止,属性直接跳到最终值。当某个属性在列表中出现多次时,以最后一个匹配的条目提供时间设置。
关键帧动画
过渡只连接两个状态,因此无法表达多步骤的动作。@keyframes 规则可以在 from 和 to 等关键字或百分比节点上定义一系列中间状态。例如,一个旋转的元素可以通过关键帧从 transform: rotate(0deg) 旋转到一整圈来描述。随后,通过 animation 简写或其长写属性将动画绑定到元素上,这些属性控制名称、时长、时序函数、延迟、迭代次数、方向、填充模式和播放状态。由于关键帧可以复用,同一定义可以驱动多个元素。
滚动驱动动画
普通动画随经过的时间推进。滚动驱动动画则用滚动位置取代时钟。该特性由 animation-timeline 属性控制,据 WebKit 指南,它于 2023 年 6 月作为 CSS Animations Level 2 规范的一部分引入。常见的时间线有两种:以 scroll() 表示的滚动进度时间线,跟踪滚动容器的滚动进度;以 view() 表示的视图进度时间线,跟踪元素自身在滚动容器中的可见情况。
使用滚动时间线时,动画的起点和终点分别对应滚动的上下限。滚动为 0% 时,动画处于其 from 关键帧;滚动到 100% 时,动画处于其 to 关键帧。因此无需设置时长,即使设置了 animation-duration,在这种情况下也会被忽略。animation-range 属性可限定驱动动画的滚动区段;例如,在一个示例中,将范围设为 0 80px 可使动画在滚动 80 像素后结束。视图时间线同样接受参数:值 view(20% block) 表示图片在滚动视口中可见 20% 时开始动画。若滚动容器不是动画元素的祖先,则可以通过 scroll-timeline-name 声明命名时间线,并用 timeline-scope 设定作用域,从而将两者关联起来。截至 2026 年,Scroll-driven Animations 模块仍以编辑草案形式发布,因此其语法可能会发生变化。
View Transitions API
在普通 CSS 中,为两个 DOM 状态之间的变化制作动画很困难,而页面导航是最棘手的情况。View Transitions API 通过让浏览器捕获新旧两种状态来解决这一问题。同一文档内的过渡通过 document.startViewTransition() 启动。调用 startViewTransition 函数时,该 API 会先对旧页面状态截图,再执行 DOM 更新。随后捕获新的实时状态,过渡过程中看到的画面由 CSS 生成,而非实际的 DOM 元素。
浏览器会构建一棵以 ::view-transition 为根的伪元素树,默认动画为交叉淡入淡出。旧快照显示为 ::view-transition-old(root),新快照显示为 ::view-transition-new(root),因此开发者可以用普通动画规则调整时序或视觉效果。对于单个元素,在 CSS 中指定的 view-transition-name 可以让该元素在两种状态之间匹配,使其位置和尺寸在两者间插值。用于独立页面之间的跨文档过渡,则通过将 navigation 设为 auto 的 @view-transition 规则启用。该 API 由 CSS View Transitions Module Level 1 和 Level 2 草案描述,其中 Level 2 涵盖跨文档情形。
由于过渡由快照组合而成,开发者仍应尊重用户偏好。多个资料建议在使用该特性的同时搭配 prefers-reduced-motion 覆盖样式,而且浏览器支持情况随时间变化,因此应以 baseline 数据核实当前兼容性,而不要依赖较早的文章。
印刷、无障碍与用户偏好
分页媒体与打印
CSS 最初是为"连续"媒体设计的,例如可滚动的网页。与之相对,"分页"媒体存在分页符,会把 HTML 文档切分开来,例如 PDF、书籍、目录和演示文稿。分页媒体相关功能正是为了填补这一空白。在 CSS 2.1 中,页面框的尺寸无法指定,不过作者可以在 @page 块中设置其页边距。尺寸控制、页边距框和命名页面后来在独立的 Paged Media 模块中定义,该模块的属性规定见 W3C 的 CSS Paged Media Module Level 3 规范。
分页由 break-before、break-after、break-inside 等属性控制,另外 orphans 和 widows 用于控制页面顶部或底部必须保持在一起的行数。浏览器的支持在很长一段时间里都参差不齐。有报告指出,分页媒体和生成内容相关规范长期停留在 W3C 草案状态,浏览器几乎没有实现。较新的指南显示,当前版本的 Chrome 和 Safari 支持 @page,Firefox 部分支持;而页边距框此前仅 Chrome 支持。
媒体类型
媒体类型是较早的一种机制,用于让样式表适配特定的输出设备。CSS2 定义了同样的类型列表,弃用了 'aural',并新增了 'embossed' 和 'speech'。print 类型面向分页材料,以及在打印预览模式下于屏幕上查看的文档。speech 类型面向语音合成器。
语音方面的设想至今未能落实。听觉样式表于 1998 年随 CSS2 出现,但 2011 年的 CSS 2.1 弃用了 'aural' 媒体类型,并引入了保留的 'speech' 媒体类型(但未规定其行为)。之后的 CSS Speech 模块旨在与语音合成标记语言(SSML,即 W3C 制定的用于控制语音合成器的语言)兼容。实际情况是,PowerMapper 的一项兼容性测试报告称,@media speech 在 CSS Media Queries Level 4 中已被弃用,且没有任何屏幕阅读器支持它。因此,屏幕阅读器用户无法依赖这一媒体类型。
偏好查询
偏好媒体特性让样式表能够响应操作系统或浏览器中的设置。prefers-reduced-motion 特性表示用户是否请求尽量减少页面中的动态效果。由于在源码中位置靠后、特异性相同的减少动效规则优先生效,如果用户开启了减少动效的设置,作者可以把脉动动画换成更温和的动画。
prefers-color-scheme 特性用于检测用户请求的是深色还是浅色主题。结合自定义属性,可以用一组变量切换整个网站的配色。其支持范围很广:一篇无障碍评测指出,其浏览器支持率很高,达 96%(截至 2024 年下半年)。诸如 forced-colors 之类的相关特性对高对比度模式的用户很重要,因为作者设定的颜色、背景和阴影会被系统颜色取代,因此仅靠颜色或背景图片传达的信息可能会消失。
无障碍问题
无障碍指南将足够的对比度、可见的焦点、易于理解的状态,以及保持可操作的控件视为任何呈现方式的基本要求,而非可有可无的细节。若移除默认的焦点轮廓却不提供替代方案,键盘导航就会变得困难;对比度低的文字则会让低视力用户难以阅读。WCAG 等正式基准规定了文字的最低对比度,通常在 AA 级别下,正文文字的对比度应为 4.5:1。
CSS 还可以把内容从部分辅助技术中隐藏起来。display: none 之类的声明会把元素从无障碍树中移除,visibility: hidden 也有类似效果,因此屏幕阅读器会跳过这些内容。视觉隐藏技术则让文字对辅助技术仍然可用,同时对明眼用户隐藏。因此,设计者必须检查,由 CSS 显示或隐藏的内容在 HTML 结构和脚本交互中是否依然说得通。这些问题把打印和偏好功能与更广泛的无障碍访问联系在了一起。
工具、预处理器与开发方法
预处理器
预处理器是应对大型手写样式表重复问题的第一种主要方案。Sass(全称 Syntactically Awesome Stylesheets,意为"语法很棒的样式表")于2006年发布,最初使用Ruby编写。Less(意为"更精简的样式表",即"Leaner Style Sheets")随后于2009年推出,由Alexis Sellier创建。两者都提供变量、嵌套和混合(mixin)功能,并将输入内容编译为普通的CSS。正如一篇概述所说,预处理器会把代码编译为浏览器能够理解的标准CSS。这两种工具还相互影响:Sass较新的SCSS语法就受到了Less语法的启发。两者的语法在细节上有所不同:在Sass中,变量使用"$"符号定义,而在Less中则使用"@"符号。
除变量和嵌套外,Sass还提供了控制指令和函数等功能。它所推广的若干理念,包括自定义属性和原生嵌套,后来被纳入CSS自身,使得某些项目不再需要单独的编译步骤。
后处理器与构建流水线
后处理器处理的是已经合法的CSS。PostCSS并不是预处理器,尽管它在某些情况下可以起到类似的作用。它是一个基于Node.js的工具,接收合法的CSS并对其进行增强。其设计采用模块化思路:用户可以组合一组插件,构建自定义的PostCSS处理流水线。最常用的插件是Autoprefixer,它会自动为需要前缀的CSS属性添加-webkit、-moz和-ms等厂商前缀。它依赖浏览器使用情况和特性支持数据,因此会解析CSS,并根据Can I Use提供的数据添加厂商前缀。前缀处理也有局限:它无法为浏览器完全不支持的属性提供实现。压缩则由cssnano等插件完成,它通过删除空白字符和冗余代码来减小CSS文件的体积。
在这些流水线中,顺序很重要。有一份指南指出,插件的顺序十分关键:postcss-easy-import应始终排在首位,而autoprefixer应是列表中的最后一个插件。Vite、Webpack和Rollup等打包工具通常会在编译之后按固定顺序执行这些步骤,不过具体顺序取决于所使用的插件。
命名方法论
由于层叠是全局性的,各团队发展出了一些约定,以使类名保持可预测。其中最著名的是BEM(Block、Element、Modifier,即块、元素、修饰符)。BEM方法论最早由Yandex于2010年提出。块是一个独立的组件,元素是块的一部分,本身没有独立含义,修饰符则是块或元素的一种变体。实践中会得到类似 block__element--modifier 的名称,其中元素与块之间用两个下划线分隔,修饰符则用两个连字符与前面部分分隔。BEM的目标是保持选择器扁平化:其核心目标之一是让大多数选择器只使用一个类名。代价是HTML和CSS都会变得冗长。BEM是一种命名规范而非工具,因此既可以用于原生CSS,也可以用于Sass或Less。
以Tailwind CSS为代表的原子化优先(utility-first)框架则采取了相反的思路。它们提供大量功能单一的小类,直接应用于标记中,因此开发者很少需要自己发明针对特定组件的类名。这减少了样式表的增长和命名决策,但代价是标记变得密集,复用则转移到了组件模板或抽象层中。CSS-in-JS库更进一步,用JavaScript编写样式并将其附加到组件上,从而实现自动作用域,并使由属性(props)驱动的动态样式变得简单。其代价是浏览器中的运行时开销和增加的打包体积,服务端渲染时也会带来额外的复杂性。
权衡
没有哪种方法能够一统天下。预处理器和构建工具提升了复用性和工具支持,但也增加了编译步骤和配置工作。BEM等命名约定提高了可读性并保持了较低的特异性,但依赖团队的自律。原子化优先和CSS-in-JS的方法可以加快开发速度,并使样式靠近其所属组件,但它们会把复杂性转移到标记或运行时代码中。因此,团队需要在可维护性、页面性能以及成员之间的协作方式之间进行权衡,许多团队会组合使用多种方法,例如原子化框架加上PostCSS流水线。
浏览器实现与渲染
浏览器加载页面时,会把 HTML 转换为文档对象模型(DOM),并把每一份样式表(无论是外部链接的、内嵌的还是行内的)转换为 CSS 对象模型(CSSOM)。各引擎的代码实现有所不同:Blink 和 WebKit 将其解析器命名为 "CSSParser",而 Gecko 使用 Mozilla 的 Stylo 解析器,这是一个基于 Rust 的组件,与 Servo 共享。CSS 解析器在 Blink 和 WebKit 中字面上名为 "CSSParser",而 Gecko 使用 Servo 的 CSS 解析器 "Stylo"。它读取样式表并生成 CSSOM。CSSOM 是规则、选择器和声明的结构化表示,脚本可以通过 document.styleSheets 以及计算样式 API 等接口读取它。样式解析随后将选择器与元素进行匹配,应用层叠和继承规则,并为每个元素的每个属性生成计算值。
渲染流水线分阶段运行。DOM 与 CSSOM 合并为渲染树,渲染树会省略不显示的节点,例如 <code>display: none</code> 的节点。渲染树完成后进行布局,确定渲染树中所有元素的位置和尺寸。随后进行绘制,为盒子、文字、图像和边框填充像素;合成则在 GPU 上将分别绘制的图层合并为最终帧。对 DOM、类名或行内样式的修改可能使这部分工作的一部分失效。如果脚本在修改样式后又读取某个布局属性,就会迫使引擎同步重新计算布局,这也是性能优化指南通常将读取与写入分开处理的原因。
由于 CSSOM 是整体构建而非增量构建,它也会影响加载时间。CSS 会阻塞渲染:浏览器在接收并处理完所有 CSS 之前,会阻止页面渲染。CSS 之所以会阻塞渲染,是因为规则可能被相互覆盖,所以在 CSSOM 完成之前,内容无法渲染。因此,一个加载缓慢的样式表就可能延迟整个页面的首次绘制。这也是内联关键 CSS 等做法存在的原因,本词条其他部分对此有所讨论。
厂商前缀是特性已起草但尚未实现互通时期的一种变通方案。CSS 2.1 规范引入了一项政策:各引擎用各自的字符串标记实验性属性,例如 -webkit-、-moz-、-ms- 或 -o-。由于每个带前缀的名称都被视为独立属性,浏览器会忽略其无法识别的前缀。这使实现者能够在测试想法的同时,不与最终的标准属性名产生冲突。作者通常先列出带前缀的声明,最后列出不带前缀的声明,依靠层叠机制,使标准版本在被支持后生效。然而在实践中,前缀长期存在。网站常常只使用特性的带前缀版本,这使得浏览器在添加对不带前缀的标准版本的支持时,很难放弃对带前缀变体的支持。这种压力促使各引擎相互实现对方的前缀,进一步加剧了生态的碎片化。前缀使用的减少部分源于引擎整合:2019 年,Edge 从自有渲染引擎切换为 Blink。总体来看,2013 年至 2021 年间需要添加前缀的特性数量下降了约 33%。Autoprefixer 等构建工具现在会自动添加所需前缀,而浏览器厂商也越来越倾向于使用特性标志(feature flag)来进行实验。
特性支持情况现在更多地通过共享的兼容性标签来追踪,而不仅仅依赖版本表格。Web 平台基线(Web Platform Baseline)根据特性在一组固定浏览器和平台组合中的可用性对其进行分类。MDN 文档列出的这一组合包括:iOS 和 macOS 上的 Safari、Android 和桌面端的 Chrome、桌面端的 Edge,以及 Android 和桌面端的 Firefox。当所有这些浏览器都支持某特性后,该特性即为"新近可用"(newly available);自其达到新近互通的日期起满 30 个月后,它便成为"广泛可用"(widely available)。在该组合中任一浏览器缺失的特性,属于有限可用(limited availability)。这一标签也有局限:它不涵盖旧设备、操作系统的网页视图或屏幕阅读器等辅助技术,因此它只是一种概括性信息,不能替代实际测试。
在编写样式表时,特性查询可以让样式表适应当前的引擎。@supports 规则仅在浏览器理解给定的属性或值时,才应用其中的一组声明。这使得较新的布局或颜色特性可以带着回退方案使用,从而兼容较旧的引擎。结合基线数据,开发者可以选择哪些特性直接依赖,哪些特性则放在渐进增强的写法中。
与 HTML、DOM 和 JavaScript 的关系
将样式与标记关联
CSS 最初就是为了设计结构化文档的样式,而不是为某一种特定的标记语言。其规范将该语言描述为:一种在屏幕、纸张及其他媒介上描述结构化文档(如 HTML 和 XML)呈现方式的语言。样式引擎从不直接处理原始源代码文本,而是处理通过文档对象模型呈现的文档树,将选择器与节点进行匹配,再为每个元素赋予相应的值。同样的分离方式也适用于 XML:样式表可以通过一个独立的 XML 样式表关联标准与文档建立关联,CSS 对象模型规范本身也引用了该标准。至于如何挂载样式,例如 link 和 style 元素以及行内属性,则在语法一节中介绍。
CSS 对象模型与脚本
CSS 对象模型(CSSOM)是一组接口,使脚本能够读取和修改样式信息。从概念上讲,它是 DOM 在样式方面的对应物。DOM 使 JavaScript 能够检查和修改文档的结构与内容,而 CSSOM 则以同样的方式让 JavaScript 读取和修改文档的样式。这些接口最初定义于 DOM Level 2 Style 建议书中,后来被独立为一个规范,即 CSS 对象模型(CSSOM),旨在取代那份早期工作。
对作者而言,有两类访问方式值得关注。第一类是编辑样式表本身:CSSOM 对象会暴露规则及其选择器,而 insertRule 等方法允许脚本在运行时向样式表中添加规则。这些接口包含 insertRule、selectorText 和 parentStyleSheet 等成员,用于访问和操作构成 CSS 样式表的各条样式规则。这类修改影响的是浏览器内部的表示,而不是服务器上的文件,这一点很容易被忽视。与单个元素的 DOM 样式一样,所谓操作样式表,实际上操作的并不是物理文档,而只是文档的内部表示。
第二类访问方式是读取层叠的结果。getComputedStyle 方法返回一个实时的只读对象,描述在应用活动样式表之后元素各属性的值。Window.getComputedStyle() 方法返回一个实时只读的 CSSStyleProperties 对象,其中包含元素所有 CSS 属性在应用活动样式表并解析其中任何计算之后的最终值。这些是解析后的值,而不是指定的声明。例如,以百分比或 auto 给出的宽度,会被报告为绝对的像素长度;虽然指定的三个值 auto、50% 和 inherit 分别是关键字和百分比值,但使用 window.getComputedStyle(el)["width"] 获取的宽度,返回的是绝对长度的 px 值。计算结果对象设计为只读是有意为之的:文档建议通过 getComputedStyle 读取,通过元素自身的 style 对象写入。getComputedStyle 返回的对象是只读的,应用于检查元素的样式,包括由 style 元素或外部样式表设置的样式。element.style 对象则应用于设置该元素的样式,或检查通过 JavaScript 操作或全局 style 属性直接添加到元素上的样式。两者的区别很重要,因为计算结果对象包含继承的值,而元素自身的 style 对象则不包含。需要注意,行内样式只包含在该元素上定义的样式:其他所有属性的值都为 "",不会显示出来。计算样式还包括在父元素上定义的 font-weight,以及许多其他计算样式。
性能是这种耦合关系带来的一个实际后果。浏览器在应用脚本所做的样式更改之前会处理 CSSOM,而且每次脚本驱动的样式更新都会重复进行布局、绘制和合成。浏览器在应用脚本中的任何样式更新之前,会先解析 CSSOM,包括布局、绘制和合成过程。此外,对于 JavaScript 的样式更新,布局、绘制和合成过程也会重复进行。因此,作者往往会批量进行修改,或切换类名,而不是逐个设置大量行内值。这是根据渲染流程所做的一般性推断,并非某一特定来源的结论。
作为桥梁的自定义属性
自定义属性是脚本与样式表相交汇的最便捷之处。由于自定义属性是一种普通的声明,参与层叠和继承,脚本可以在容器上设置它,而每条通过 var() 读取它的规则都会随之更新,无需脚本了解这些规则。CSS 属性值 API(CSS Properties and Values API)进一步扩展了这一思路,允许为属性注册类型、初始值以及是否继承的标志。通过该 API 注册的属性会附带一个解析语法,用于定义类型、继承行为和初始值。在 JavaScript 中,典型的注册方式是调用 CSS.registerProperty,并传入名称、语法字符串、继承标志和初始值。同样的定义也可以用 CSS 中的 @property 规则书写。类型化注册对动画很重要,因为浏览器无法对无类型的字符串进行插值。有来源指出,该 API 的主要优势在于开发者可以对自定义属性做动画,这在仅依靠现有技术时是做不到的。如果脚本赋予了错误类型的值,浏览器会丢弃该值并使用默认值。也就是说,如果你为一个变量赋了错误类型的值,浏览器会忽略它,并选用默认值。
Houdini 式 API
CSS Houdini 是一组底层 API 的统称,这些 API 暴露了样式和布局机制的部分内容。正如 MDN 文档所言,Houdini 是一组暴露 CSS 引擎部分功能的底层 API,使开发者能够通过介入浏览器渲染引擎的样式与布局过程来扩展 CSS。其目标是让作者编写被浏览器视为 CSS 的代码,使新的行为无需等待原生实现。Houdini 是一组让开发者直接访问 CSS 对象模型(CSSOM)的 API,使开发者能够编写浏览器可以解析为 CSS 的代码,从而无需等待浏览器原生实现即可创造新的 CSS 功能。
其主要组成部分如下。属性值 API 涵盖上文所述的类型化自定义属性注册。类型化对象模型(Typed OM)则着眼于字符串处理的开销:将 CSSOM 的值字符串转换为有意义的类型化 JavaScript 表示,再转换回来,可能会带来显著的性能开销。CSS 类型化对象模型将 CSS 值暴露为类型化的 JavaScript 对象,以便高效地进行操作。绘制 API(Painting API)允许脚本在盒子的背景、边框或内容中绘制内容,并通过 CSS 值中的 paint() 函数调用。该 API 旨在提升 CSS 的可扩展性,允许开发者编写 JavaScript 函数,通过 CSS 的 paint() 函数直接在元素的背景、边框或内容中绘制。绘制工作单元(paint worklet)通过 inputProperties 列表声明它所需要的自定义或标准属性,其 paint 方法随后从第三个参数中读取这些属性的值。我们添加了 inputProperties,用于告诉绘制工作单元需要查找哪些 CSS 属性。之后,paint() 函数便可以通过第三个传入参数 props 访问这些属性的值。更高层级的 API 包括绘制、布局和动画 API,它们直接与渲染引擎交互。高层级 API 与 CSS 渲染引擎交互,由绘制 API、布局 API 和动画 API 组成。
这些 API 的可用性并不统一。它们各自的规范推进速度不同,因此在生产环境中依赖某项 Houdini 功能之前,应先对照各引擎当前的兼容性数据进行核实。
性能、维护与批评
渲染阻塞与加载成本
浏览器默认将样式表视为阻塞资源。在每个样式表都下载并解析完成之前,浏览器不会绘制任何内容。文档头部中的外部样式表造成的成本最大。若某个样式表的媒体查询与当前设备不匹配,它会以较低优先级获取,且不会阻碍渲染,因此作者常用 media 属性把打印或大屏幕的规则与其他规则分开。
优化工作通常由此展开。关键 CSS(即首屏区域所需的规则)被提取出来,直接内联放在 head 中,其余规则则稍后加载。通常的做法是先把这部分 CSS 内联到页面的 head 标签中,再延迟加载非关键 CSS。一些从业者建议让内联代码块足够小,以便能放入 TCP 连接在第一个往返中通常交付的约 14 KB 初始拥塞窗口之内,这样首次绘制只需一次往返即可完成。浏览器开发者工具中的覆盖率工具有助于识别首次渲染时实际用到的规则,而 Filament Group 的 Critical 等提取库可以将这一过程自动化。未使用的规则同样会占用带宽并增加解析时间,因此构建阶段的裁剪十分常见。
批评者指出,CSS 只是问题的一部分。head 中同步执行的 JavaScript 同样会阻塞渲染,因此如果真正的瓶颈是脚本,把 CSS 移出关键路径可能对开始渲染的时间毫无影响。因此,调整 CSS 的交付方式必须与脚本的加载顺序一并考虑。
全局作用域与特异性问题
对大型 CSS 代码库的几乎所有批评,都源于一个事实:所有 CSS 选择器都是全局的,会匹配整个 DOM。为某个组件编写的规则可能悄无声息地影响页面中无关的部分;而选择器也可能在全局特异性的竞争中落败,最终对页面几乎或完全不起作用。作者的应对办法是编写越来越具体的选择器,这反过来又让之后的覆盖更加困难,这种模式常被称为特异性升级。
为应对这些问题而发展出的方法大多是约定与工具。OOCSS、SMACSS、BEM 和 SUIT 等命名方案为每个组件加上前缀,使类名很少发生冲突。其中 BEM 尤其保证每个选择器只包含一个类,从而为组件提供某种作用域,并使特异性保持在较低水平。它的代价是实际操作上的:要给太多东西命名令人头疼,而且随着项目演进,类名会变得越来越长。CSS Modules 等构建工具方案则采取了另一条路线,即在打包时生成唯一的类名,这消除了文件之间的冲突,但增加了构建依赖,也使运行时的类名在开发者工具中更难辨认。
关于嵌套与层的争论
原生特性的引入在它们究竟是减少还是增加复杂度上引发了分歧。级联层的设计主要是为了解决特异性升级的问题。在此之前,开发者常常通过增加 ID、类或嵌套层级的选择器来覆盖来自框架或第三方库的样式。层使整组规则的优先级变得明确,其顺序由各层首次出现的先后决定,而不取决于层内的选择器。
原生嵌套也以类似的方式备受争议。一些评论者认为,嵌套本身会导致特异性升级,可能在层叠中引发意外的样式覆盖和冲突。另一些人则认为,嵌套是组织代码的有效方式,而且它并没有解决潜在的问题。有一位批评者写道,CSS 计算优先级依据的是选择器的类型而非其意图,因此嵌套无法触及问题的核心。从业者的建议普遍倾向于浅层嵌套,理由是深层嵌套增加了复杂度,却收益甚微。
作用域提案也体现出类似的张力。早期的 scope 规范从未被实现。之后的 @scope 规则采用结构化的方法,其作用范围不依赖特异性,因此一条位于作用域之外、但特异性更高的规则仍可能胜出。这种结构化作用域是否优于命名约定,仍是见仁见智的问题,对于那些现有工具已运转良好的团队而言尤其如此。
标准化与不均衡的采用
标准化并不保证每个浏览器都会同时支持某项特性。例如,@scope 规则分别在 Chrome 118 和 Safari 17.4 中可用,时间点并不相同,许多其他现代特性也出现过类似的差距。依赖较新功能的作者因此必须使用特性查询、回退方案或渐进增强,并且要考虑旧版浏览器从实际访问流量中消失所需的时间。规范与已安装用户群之间的这种差距,是 CSS 尽管采用声明式设计,却仍然难以在大型项目中维护的原因之一。
未来发展方向与待解决的问题
原生函数与混入
人们讨论最多的不足,是纯 CSS 中缺少可复用的逻辑。工作组在 2024 年初决定采纳自定义函数与混入(mixin)的提案,2024 年中期随后发布了草案。函数返回可以用于属性值内部的 CSS 值,混入返回声明或整个规则块,且只能用于属性值之外。函数使用 @function 声明并接受参数。混入则通过新的 @apply 规则调用,这与 Sass 的 @include 不同。草案还要求名称以两个连字符开头,以避免自定义名称与内置函数发生冲突。
草案在制定过程中有所变动。某一时期规范只描述了自定义函数,目前仍有一些悬而未决的问题,例如具名参数的传递方式,以及 @function 中的返回值是否必须存在。
原生功能可能减少对 Sass 等预处理器的依赖。自定义属性和嵌套语法已经减少了对预处理器的依赖,但循环和可复用逻辑等功能仍常常需要借助预处理器实现。Sass 的维护者已规划了自己的兼容措施,包括弃用以双连字符开头的函数名与混入名,以免原生语法与 Sass 代码产生冲突。
锚点定位及其他布局工作
锚点定位是浏览器的一种原生机制,无需脚本即可让元素相对于其他元素进行定位。锚点通过 anchor-name 标记,被定位的元素则通过 position-anchor 属性和 anchor() 函数引用它。position-try-fallbacks 允许在首选位置溢出时,由浏览器选择替代的定位方案。
各方对浏览器支持情况的报道并不一致。一种说法是它于 2024 年 5 月随 Chrome 125 发布,另一种说法则称其在 2026 年 1 月火狐启用该功能后,正式达到 Baseline 新近可用(Newly Available)状态。另有一篇较晚的文章指出,截至 2026 年 8 月火狐仍在实现该功能,另一篇文章则提到,Web Platform Baseline 仍将其标记为尚未进入 Baseline。这些来源都是二手博客,日期也有出入,因此应以浏览器官方发布说明和规范本身为准来核实当前状况。规范的维护者也指出了其中仍存在的不足。
除锚点定位外,本文参考的资料没有详细描述原生布局控制方面的其他不足,因此本节不逐一列举。
层叠、作用域与颜色
作用域是最活跃的领域之一。《层叠与继承》第 6 级草案引入了 @scope 规则,可将选择器匹配限定在一个有界区域内。此前,一份较早的作用域规范从未被实现,工作重心转向了主要关注完全隔离的 Shadow DOM。作用域提案则旨在采用一种更轻量的方式,其中一份草案建议,作用域的邻近程度应排在选择器特异性之后、源码顺序之前参与排序。
层叠层也需要进一步完善。《层叠》第 5 级引入了层,作为在单一来源(origin)内组织样式关注点的一种结构化方式。2021 年,工作组修改了早先的决定,使未分层的样式排在层顺序的最后。
在颜色方面,《颜色》第 4 级将色彩空间的范围扩展到 sRGB 之外,更多的色彩空间与渐变方面的工作则在后续草案中继续推进。
标准制定流程与兼容性
如今,年度快照界定了该语言的稳定状态。2026 年快照是一份单一的定义文档,汇集了共同构成 2026 年 CSS 状态的各个模块,它按年度发布,而非以草案形式发布。
核心矛盾在于兼容性。由于 CSS 被大量已有文档使用,新语法不能破坏旧内容。在新功能的支持尚未普及时,作者通常借助 @supports 进行特性检测,并辅以基于 JavaScript 的回退方案。带前缀或带连字符的名称、Sass 等工具中的弃用周期以及年度快照,都是在不使现有样式表失效的前提下引入新功能的机制,但每一种机制都会增加实现者与作者的负担。
参考来源
- Håkon Wium Lie, the Creator of CSS, at Codemotion Milancodemotion.com
- A brief history of CSS until 2016w3.org
- History of CSSsitesbay.com
- A Brief History of CSSshecodes.io
- W3C PR-CSS1-961112w3.org
- 201: Levels of CSSrussmaxdesign.github.io
- W3C Issues CSS2 as a Proposed Recommendationw3.org
- The World Wide Web Consortium Issues CSS2 as a W3C Recommendationw3.org
- REC-CSS2-19980512 Cascading Style Sheets, level 2 CSS2 Specificationw3.org
- REC CSS1 20080411w3.org
- Cascading Style Sheet (CSS) File Formatloc.gov
- CSS current work & how to participatew3.org
- W3C Process Documentw3.org
- CSS Snapshot 2025w3.org
- CSS Working Group Charterw3c.github.io
- WD css beijing 20071019w3.org
- CSS Snapshot 2018w3.org
- CSS Snapshot 2023w3.org
- CSS Snapshot 2020w3.org
- CSS Snapshot 2026w3.org
- Publicationsw3.org
- CSS Syntax Module Level 3w3.org
- At-rules - CSS - MDN Web Docsdeveloper.mozilla.org
- CSS error handling - MDN Web Docsdeveloper.mozilla.org
- CSS Syntax Module Level 3w3.org
- CSS @import Rulew3docs.com
- Introduction to CSS syntax: declarations, rulesets, and statements - CSSdeveloper.mozilla.org
- The Quick Guide to CSS Specificity - Slicejackslicejack.com
- Specificity - CSS - MDN Web Docsdeveloper.mozilla.org
- :is() vs. :where()gomakethings.com
- where() CSS pseudo-class - MDN Web Docsdeveloper.mozilla.org
- Selector list - CSS - MDN Web Docsdeveloper.mozilla.org
- :wherecss-tricks.com
- Specifics on CSS Specificitycss-tricks.com
- Screencast: Origin and Importance in the CSS Cascade — SitePointsitepoint.com
- The cascade - CSSweb.dev
- Cascade layers - Learn web developmentdeveloper.mozilla.org
- 6 Assigning property values, Cascading, and Inheritancew3.org
- A quick introduction to CSS @scopebram.us
- CSS Cascading and Inheritance Level 4w3.org
- Control CSS cascade with cascade layers - LogRocket Blogblog.logrocket.com
- CSS Cascade Layers (@layer): The Complete Guide to Taming Specificity (2026)cssawwwards.com
- Limit the reach of your selectors with the CSS @scope at-ruledeveloper.chrome.com
- @scope CSS at-rule - CSSdeveloper.mozilla.org
- Introduction to the CSS cascade - MDN Web Docsdeveloper.mozilla.org
- CSS Cascade Layers Guide - @layer Ruledesign.dev
- @scope — Scoped Styles - Dev Guidescamoa.github.io
- CSS property value processing - MDN Web Docsdeveloper.mozilla.org
- 6 Assigning property values, Cascading, and Inheritancew3.org
- Value - MDN Web Docs Glossary: Definitions of Web-related termsdeveloper.mozilla.org
- Actual value - CSS: Cascading Style Sheetsudn.realityripple.com
- Using CSS custom properties (variables)developer.mozilla.org
- CSS Variables: A Complete Guide [2026]scrimba.com
- CSS Length Units: px, em, rem, vw, vh, and Moreitechguides.com
- clamp() CSS function - MDN Web Docsdeveloper.mozilla.org
- CSS Units Explained: px vs rem vs em vs vw - Blogtoolbox-kit.com
- CSS Units — px, em, rem, vw, vh Explainedtutorialslogic.com
- box sizingdeveloper.mozilla.org
- box-sizingcss-tricks.com
- CSS Grid Layout Module Level 2w3.org
- Box Modelweb.dev
- Introduction to the CSS box model - MDN Web Docsdeveloper.mozilla.org
- CSS Box Model: How to Use CSS Box Sizingthevalleyofcode.com
- Subgrid moved to Level 2 of the CSS Grid specificationrachelandrew.co.uk
- Subgridtympanus.net
- CSS Media Queries: The Complete Guide for 2026devtoolbox.dedyn.io
- Responsive web designen.wikipedia.org
- prefers-color-scheme - Modern CSS Dailymodern-css.davecross.co.uk
- The complete guide to CSS media queriespolypane.app
- Using container size and style queries - CSS - MDN Web Docsdeveloper.mozilla.org
- Complete Guide to CSS Container Queriesmoderncsstools.com
- Responsive Web Design History: Ethan Marcotte's 2010 A List Apart Revolutionsomanyquestions.show
- An Introduction to Responsive Web Designixdf.org
- Media Queries Level 5w3.org
- Dark Mode in CSScss-tricks.com
- CSS Prefers-Color-Scheme: Complete Guide to Dark Mode and Light Mode Detection - CodeLuckycodelucky.com
- container-type CSS property - MDN Web Docsdeveloper.mozilla.org
- Container queries begin to land in stable browsers while the polyfill gets a big updatedeveloper.chrome.com
- Getting started with CSS container queries - MDN Web Docsdeveloper.mozilla.org
- Container queriesweb.dev
- Responsive web design (RWD)ebsco.com
- Oklab color spaceen.wikipedia.org
- Modern CSS Color: oklch, color-mix(), and the New Color Spacesmoderncsstools.com
- color-mix() CSS function - MDN Web Docs - Mozilladeveloper.mozilla.org
- CSS color-mix(): Native Color Blending That Retires Your Sass Pipeline — W3Tweaksw3tweaks.com
- Optimize WebFont loading and renderingweb.dev
- CSS @font-face — Web Font Loading Strategies - Wu Wenjuntie.pub
- A New Way To Reduce Font Loading Impact: CSS Font Descriptors — Smashing Magazinesmashingmagazine.com
- CSS color-mix()developer.chrome.com
- CSS Blend Modes for Images — Mix-Blend-Mode, Backdrop-Filter, and Isolation Without JavaScriptppimage.com
- mix-blend-mode CSS property - MDN Web Docsdeveloper.mozilla.org
- Using CSS backdrop-filter for UI Effectscss-tricks.com
- Filter Effects Module Level 2drafts.csswg.org
- CSS color-mix() Playground - Mix Colors Across Color Spacesmoderncsstools.com
- color-mix(): blending colors in CSS — cssShowcasecssshowcase.com
- Backdrop Root Proposal [archived copy]lists.w3.org
- Transitionsweb.dev
- transition-timing-function CSS property - MDN Web Docsdeveloper.mozilla.org
- transition-timing-functioncss-tricks.com
- CSS Transitions Module Level 1w3.org
- CSS Scroll-Driven Animationsmskelton.dev
- A guide to Scroll-driven Animations with just CSSwebkit.org
- CSS Scroll-Driven Animations: Scroll Timelines Guide (2026)cssawwwards.com
- An Introduction To CSS Scroll-Driven Animations: Scroll And View Progress Timelines — Smashing Magazinesmashingmagazine.com
- The View Transitions API And Delightful UI Animations (Part 1) — Smashing Magazinesmashingmagazine.com
- Scroll-driven Animations - CSS Working Group Editor Draftsdrafts.csswg.org
- CSS Scroll-Driven Animations: Complete Guide (No JavaScript)csstools.io
- view-transitions/explainer.md at main · WICG/view-transitionsgithub.com
- CSS View Transitions: From Basic to Advancedmoderncsstools.com
- An Introduction to the View Transitions API — SitePointsitepoint.com
- CSS Paged Media Module & Specificationsdocraptor.com
- 13. Paged mediaw3.org
- CSS Paged Media: The Complete Guide to Print & PDF Styling - Doppiodoppio.sh
- Media Queries Level 3w3.org
- Media typesw3.org
- CSS media query with `@media speech` - Screen reader compatibilitypowermapper.com
- All CSS specificationsw3.org
- Emulate CSS media featuresdeveloper.chrome.com
- prefers-reduced-motion CSS media feature - MDN Web Docsdeveloper.mozilla.org
- Preference Queries12daysofweb.dev
- CSS media features to improve accessibility — Blog on Digital Accessibilitya11y-blog.dev
- Respecting Reduced Motion and Forced Colorsmv3-extension.com
- Media Query Preferences: Respect User Choices in Web Interfacesbotbrowser.io
- CSS Media Typesquackit.com
- Aural CSS: Support for CSS 2 Aural Style Sheets / CSS 3 Speech Module from Dotjay’s Lab - dotjay.comlab.dotjay.com
- CSS Preprocessors: Sass vs Less. CSS Preprocessors are scripting…medium.com
- Popular CSS preprocessors with examples: Sass, Less, Stylus and more · Raygun Blograygun.com
- CSS Preprocessors: SASS and LESS - NamasteDev Blogsnamastedev.com
- An Introduction to PostCSS — SitePointsitepoint.com
- autoprefixer coregithub.com
- minifier-css-postcss - Packospherepackosphere.com
- BEM CSS naming conventions including ABEM & BBEMpivale.co
- BEM CSS Methodology: A Step-by-Step Guide for Beginnersvaloremreply.com
- BEM by Example: Best Practices for BEM CSS Namingsparkbox.com
- Setting Up Your Development Environment with PostCSS and Autoprefixer — ResumeLens Tailwind CSS Blogresumelens.org
- Understanding BEM as a CSS Methodology for Modern Web Development - DEV Communitydev.to
- Inside the Browser Rendering Pipeline - Aleksandar Gjoreskialeksandargjoreski.dev
- Critical rendering path - Performance - MDN Web Docsdeveloper.mozilla.org
- Is Vendor Prefixing Dead?css-tricks.com
- Baselineweb.dev
- How the Browser Rendering Pipeline Actually Works - DEV Communitydev.to
- CSS Vendor Prefixesbitsofco.de
- Vendor prefix - Glossary - MDN Web Docsdeveloper.mozilla.org
- Vendor prefixdeveloper.mozilla.org
- Baseline (compatibility)developer.mozilla.org
- Simplify Browser Compatibility Decisions with Baselinedockyard.com
- CSS Object Model (CSSOM)w3.org
- CSS Object Model (CSSOM)developer.mozilla.org
- Using dynamic styling information - Web APIsdeveloper.mozilla.org
- Using dynamic styling information - Web APIsdevdoc.net
- Window: getComputedStyle() method - Web APIsdeveloper.mozilla.org
- Window.getComputedStyle() - Web APIsmdn2.netlify.app
- CSSStyleProperties - Web APIsdeveloper.mozilla.org
- Houdini APIsdeveloper.mozilla.org
- CSS Houdini: Properties, Values, and the Paint API - Moment For Technologymo4tech.com
- Say Hello to Houdini and the CSS Paint API / Coder's Blockcodersblock.com
- The Ultimate Guide to CSS Houdinitomquinonero.com
- CSS Object Model (CSSOM) - Web APIsdeveloper.mozilla.org
- Fix: Eliminate Render-Blocking CSS and JavaScriptsitegrade.io
- How to Eliminate Render-Blocking Resources (CSS and JavaScript)nitropack.io
- CSS Scope Proposal & Explainer - OddBird CSS Sandboxcss.oddbird.net
- The End of Global CSS. CSS selectors all exist within the same…medium.com
- Building a Scalable CSS Architecture With BEM and Utility Classescss-tricks.com
- CSS @Layer: Complete Guide to Cascade Layersdigitalthriveai.com
- CSS Nesting and Cascade: Elevating Web Design - DZonedzone.com
- Cascade Layers (@layer) in CSS: how to solve the specificity problem once and for all - DEV Communitydev.to
- Render-blocking CSSweb.dev
- How to eliminate render-blocking resources — CSS and JavaScript - LogRocket Blogblog.logrocket.com
- Tackling Render Blocking CSS for a Fast Rendering Website — SitePointsitepoint.com
- How to Eliminate Render-blocking Resources - SiteGround Academysiteground.com
- Critical CSS? Not So Fast!csswizardry.com
- How To Fix & Eliminate Render Blocking Resources For Page Speedpagespeedchecklist.com
- Fix Render-Blocking CSS: 2026 Guidewebperfclinic.com
- How CSS modules solve global scope in CSSmstamstrom.medium.com
- CSS Cascade Layers Explainercss.oddbird.net
- Cascade Layers Guidecss-tricks.com
- CSS @scope: the end of BEM (for real this time) - Web Makerwebmaker.app
- CSS @layer: Cascade Layers That End the Specificity Wars — W3Tweaksw3tweaks.com
- CSS Mixins & Functions Explainercss.oddbird.net
- CSS Functions and Mixins Module Notescss-tricks.com
- CSS Functions and Mixins Module Notestechreport.ngo
- Intent to Ship: CSS Custom Functions (@function)groups.google.com
- Compatibility with the CSS custom function and mixin proposal · Issue #3787 · sass/sassgithub.com
- CSS Anchor Positioning and Popover API Hit Baseline 2026: The End of JavaScript Tooltip Librariesfull-net.cz
- CSS Anchor Positioning: The Complete Guide (2026)ultimatedesigntools.com
- CSS Anchor Positioning: Tooltips Without JS — CODERCOPSblog.codercops.com
- CSS Anchor Positioning Reaches Baseline in 2026refontelearning.com
- CSS Anchor Positioning: No More JavaScript for Your Tooltips and Popovers - Kevin Kenfackkenfack.vercel.app
- CSS Anchor Positioning: Browser Support, Features, Issuestestmuai.com
- CSS Cascading and Inheritance Level 6w3.org
- CSS Cascade 6 (Scope)miriamsuzanne.com
- [css-scoping] Proposal for light-dom scoping/namespacing with re-designed `@scope` rule · Issue #5809 · w3c/csswg-draftsgithub.com
- The Future of CSS: Cascade Layers (CSS @layer)bram.us
- PROPOSED CSS Working Group Charterw3c.github.io
- add @mixin and @function · Issue #7301 · Fyrd/caniusegithub.com
- `@mixin` · tailwindlabs/tailwindcss · Discussion #14100github.com
- support for CSS Mixins & Functions · Issue #785 · web-platform-tests/interopgithub.com