【CSS in Depth 2 精译_062】第 10 章 CSS 中的容器查询(@container)概述 + 10.1 容器查询的一个简单示例
当前内容所在位置(可进入专栏查看其他译好的章节内容)
- 【第十章 CSS 容器查询】 ✔️
- 10.1 容器查询的一个简单示例 ✔️
- 10.1.1 容器尺寸查询的用法 ✔️
- 10.2 深入理解容器
- 10.3 与容器相关的单位
- 10.4 容器样式查询的用法
- 10.5 本章小结
文章目录
- 第 10 章 容器查询
- 10.1 容器查询的一个简单示例 A basic example of a container query
- 10.1.1 容器尺寸查询的用法 Using container size queries
《CSS in Depth》新版封面
译者按
终于又来到了第2版的另一个全新章节——第十章的 CSS 容器查询。虽然各大主流浏览器对该语法支持已经有一年多了,启用该特性也无需依赖浏览器的实验环境,但系统介绍容器查询的高质量技术博文依然屈指可数。新特性的掌握主要分两步走:先不折不扣地学一遍,然后再想方设法投入实践。而前者的另一个窍门,则是结合具体的示例场景加深理解。精心挑选的示例在 CSS 新语法特性的学习过程中极为重要,但也是博文创作者经常忽视的一个环节,毕竟经典案例需要作者自己先吃透该知识点。让我们跟随作者的思路,一次性拿下这个业界期盼已久的容器查询功能!!!
第 10 章 容器查询
本章概要
- 容器的定义与
@container
规则的用法- 根据容器大小实现模块的响应式
- 容器相对单位的用法
- 利用样式查询实现基于自定义属性值的样式变更
至此我们已经掌握了将 CSS 组织为方便重用的模块的具体方法。在大型项目团队构建大型网站或 Web 应用时,利用该方法可以得到效果完全一致的 CSS 样式。然而,要是遇到响应式设计的场景,要让设计出的模块在页面任意位置都能使用,难度就会直线攀升。可能经常会遇到要将某个模块放入像侧边栏这样的狭长的列内。虽然视口本身可能够宽了,但留给特定模块的可用空间就另说了。因此,普通的 @media
媒体查询在模块的响应式设计方面未必可行。
而 CSS 新推出的 容器查询(container query) 功能则提供了更为灵活实用的解决方案。容器查询分为两大类:容器尺寸查询(container size queries) 和 容器样式查询(container style queries)。前者可以根据容器元素的宽度调整页面元素的样式;而后者则可以根据容器的自定义属性实现样式修改。
容器的定义
在容器查询的语境下,容器(container) 是指包含了相关元素的特定祖先元素。容器可以是父元素或者更高层级的 DOM 树节点。容器元素在容器尺寸查询和容器样式查询中有各自的确定方式,稍后会详述。
本章将对媒体查询的局限性作简要说明,并介绍容器查询在响应式设计中的具体用法。之后将结合几个实际案例进行深入探讨,助您彻底拿下这个前端期盼已久 CSS 特性。
10.1 容器查询的一个简单示例 A basic example of a container query
不妨再来仔细考察一下上一章介绍过的媒体模块(如图 10.1 所示)。先将该模块添加到一个简单的两栏布局中,并让页面符合响应式的设计要求,以便在视口尺寸较小时两个内容列可以呈上下分布。下面将演示在页面响应式特性的控制上,尤其是在涉及模块化 CSS 的相关场景时,媒体查询的表现未必会令人满意。
【图 10.1 第 9 章中构建的媒体模块效果图】
上述模块的 HTML 标记如以下代码清单 10.1 所示。媒体模块位于一个 <aside>
元素内,该元素在页面上渲染为一个狭长的侧边栏列。
代码清单 10.1 带媒体模块的简单两栏布局页 HTML 标记
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1" /><!-- 视口的 meta 标签 -->
<link href="style.css" rel="stylesheet" />
</head>
<body class="l-page">
<main>
<h1>Franklin Running Club</h1>
</main>
<aside>
<h3>Running tips</h3>
<div class="media"><!-- 媒体模块 -->
<img class="media__image" src="runner.png" />
<div class="media__body">
<h4>Change it up</h4>
<p>
Don't run the same every time you
hit the road. Vary your pace, and
vary the distance of your runs.
</p>
</div>
</div>
</aside>
</body>
</html>
此时,正文列 main
元素中并没有多少内容,本章稍后会进行扩充。这里先关注侧边栏中的媒体模块。
接着打开样式表,并根据代码清单 10.2 提供的样式进行更新。此时页面中的媒体模块效果和上一章相比,除了引入一些媒体查询来实现页面的响应式外,并没有什么不同之处。整体的页面布局也使用了一个媒体查询,以便在视口尺寸较小时,整个侧边栏可以叠放在 <main>
元素的下方。该写法本质上与第 7 章介绍过的响应式知识是一样的。
代码清单 10.2 两栏布局与媒体模块的样式代码
@layer reset, layout, modules;
@layer base {
img {
display: block;
max-width: 100%;
}
}
@layer layout {
.l-page {
margin-inline: 1rem;
}
@media (min-width: 800px) { /* 大尺寸视口下使用双栏布局 */
.l-page {
display: grid;
gap: 1rem;
grid-template-columns: 1fr 30%;
}
}
}
@layer modules {
.media {
padding: 1.5rem;
background-color: #eee;
border-radius: 5px;
}
.media__image {
margin-inline: auto;
}
@media (min-width: 450px) { /* 对于 450px 以上的视口,将文本移至图片旁边 */
.media {
display: flex;
gap: 1.5em;
}
.media__image {
align-self: start;
margin-inline: revert;
}
}
}
在浏览器中加载该页面,媒体模块会出现在较窄的右边栏;而当视口尺寸变得很小时,各列又会叠放在一起,同时媒体模块内部元素也会发生堆叠,最终令图片出现在文本上方,如下图 10.2 所示。由于在小尺寸断点的图片上设置了 margin-inline: auto
,因此图片可以水平居中展示。
【图 10.2 媒体模块在小尺寸视口的响应式页面中的叠放效果图】
在本例中,页面所有内容在视口尺寸较大时显示正常,在视口较小时页面的响应式渲染也表现良好;然而,当视口尺寸处在二者之间时,情况就不太理想了。当视口宽度稍微迈过 800px
时,页面会按两栏布局渲染。此时右边栏的宽度无法按响应式设计很好地处理媒体模块的渲染,导致媒体模块中的文本内容被强行挤到一处非常狭窄的位置,如图 10.3 所示:
【图 10.3 两栏布局中的侧边栏由于宽度不够,无法正确处理媒体模块的渲染】
这里的媒体查询模块的问题在于,尽管视口本身的宽度已经够宽了,似乎可以按并排模式来渲染媒体模块了;但媒体模块却被放到了一个宽度严重不足的容器内。
这个问题理论上可以通过再加一个媒体查询来解决。这样一来,当视口宽度介于 800px
到大约 1100px
之间时,媒体模块将位于较窄的侧边栏内,并按照一个“动态的”(“mobile”)上下布局进行渲染。这无疑需要写更多样式代码,并且还得时刻关注页面其他位置是否也有类似的狭窄容器;更要命的是,这样的解决方案严重违背了模块化设计的一个核心原则:设计模块时不应该依赖可能会用到该模块的上下文。
一种更好地解决方案是完全放弃媒体查询,转而使用 容器查询。使用了容器查询的模块,视口大小与否都无关紧要;重要的是该模块所在的容器大小。
10.1.1 容器尺寸查询的用法 Using container size queries
启用容器尺寸查询只需两个步骤:先定义一个 容器(container),再用 @container
规则查询该容器。接下来将通过容器查询的一个简单示例来完善刚才的媒体模块,之后将带您深入考察定义容器时可用的其他配置项。
注意
容器查询功能虽然已于 2023 年初得到了当时所有主流浏览器最新版的支持,但在决定是否使用该功能时,也要考虑到用户可能推迟升级浏览器的情况。获取浏览器最新的版本支持情况,详见 https://caniuse.com/css-container-queries。
在 CSS 中,容器 这一术语长期以来一直用于指代某个祖先级元素,并通过指定尺寸大小或背景颜色来界定页面上的某个区域;而在容器查询的上下文中同样如此。但在容器尺寸查询中,必须通过设置 container-type
属性来显式定义容器。您也可以在同一元素上设置 container-name
,例如:
container-name: layout;
container-type: inline-size;
这样就给容器指定了一个名称 layout
,并且限定开发者只能根据其行内尺寸或宽度进行容器查询。稍后我们还将重点考察这些属性中的各种取值,但眼下这些配置已经足以构建出一个容器查询的基本示例了。
上述两个属性声明也可以通过 container
简写属性等效替换。这通常也是设置它们的最简单的方式:
container: layout / inline-size;
定义好了容器,就可以使用 @container
来查询宽度了,然后再对容器内的任意元素设置相应的样式。具体写法类似于媒体查询,例如 @container layout (min-width: 450px)
;或者使用带区间范围的写法 @container layout (width >= 450px)
。这里的 layout
指代容器上定义的容器名称。当目标容器满足指定的尺寸大小时,该查询内的样式声明就会生效,在本例中即为:当容器的宽度大于或等于 450px
时生效对应的样式。
代码清单 10.3 给出了示例媒体模块的容器查询样式代码,请按照以下内容更新本地样式表。
代码清单 10.3 添加容器查询后的示例样式代码
@layer layout {
.l-page {
margin-inline: 1rem;
}
.l-page > * {
container: layout / inline-size; /* 令每列均为一个容器 */
}
@media (min-width: 800px) {
.l-page {
display: grid;
gap: 1rem;
grid-template-columns: 1fr 30%;
}
}
}
@layer modules {
.media {
padding: 1.5rem;
background-color: #eee;
border-radius: 5px;
}
.media__image {
margin-inline: auto;
}
@container layout (width >= 450px) { /* 查询 layout 容器的宽度 */
.media {
display: flex;
gap: 1.5em;
}
.media__image {
align-self: start;
margin-inline: revert;
}
}
}
通过调整浏览器窗口大小来查看上述变更情况(或者利用浏览器的 DevTools
开发者工具启用响应式的设计模式)。此时,媒体模块相当于拥有了四个断点尺寸:在小尺寸视口中作上下布局排列;宽度介于 450px
到 800px
时则恢复为水平布局;宽度介于 800px
到约 1530px
时(即侧边栏较窄时)再次作上下排列;最后当宽度大于该断点时,又恢复为水平布局排列。
更重要的是,侧边栏的代码和媒体模块的代码并没有纠缠到一起,可以放心修改二者中的任意一个。至于媒体模块在哪个上下文里响应哪种布局,则完全交由浏览器自行决定。
警告
鉴于容器的固有特性,需要严格遵守一项特殊规定:容器查询不能在容器本身指定样式。容器查询中的选择器只能选中容器内的 后代元素。
与媒体查询一样,您也可以使用 not
关键字来对查询逻辑取反,令样式声明在查询条件为 false
时生效,例如 @container layout not (width >= 450px)
。
关于《CSS in Depth》(中译本书名《深入解析 CSS》)
第 1 版 | 第 2 版 | |
---|---|---|
读者评分 | 原版:4.7(亚马逊);中文版:9.3(豆瓣) | 原版:5.0(亚马逊);中文版:暂无,待出版 |
出版时间 | 原版:2018 年 3 月;中文版:2020 年 4 月 | 原版:2024 年 7 月;中文版:暂无,待出版 |
原价 | 原版:$44.99;中文版:¥139.00 | 原版:$59.99;中文版:暂无,待出版 |
现价 | 原版:$36.49;中文版:¥52.54 起步 | 原版:$52.09;中文版:暂无,待出版 |
原版国内预订 | 起步价 ¥461.00 | 起步价 ¥750.00 |
本专栏为该书第 2 版高分译文专栏,全网首发,精译精校,持续更新,计划今年内完成全书翻译,敬请期待!!!
目前已完结的章节(可进入本专栏查看详情,连载期间完全免费):
- 第一章 层叠、优先级与继承(已完结)
- 1.1 层叠
- 1.2 继承
- 1.3 特殊值
- 1.4 简写属性
- 1.5 CSS 渐进式增强技术
- 1.6 本章小结
- 第二章 相对单位(已完结)
- 2.1 相对单位的威力
- 2.2 em 与 rem
- 2.3 告别像素思维
- 2.4 视口的相对单位
- 2.5 无单位的数值与行高
- 2.6 自定义属性
- 2.7 本章小结
- 第三章 文档流与盒模型(已完结)
- 3.1 常规文档流
- 3.2 盒模型
- 3.3 元素的高度
- 3.4 负的外边距
- 3.5 外边距折叠
- 3.6 容器内的元素间距问题
- 3.7 本章小结
- 第四章 Flexbox 布局(已完结)
- 4.1 Flexbox 布局原理
- 4.2 弹性子元素的大小
- 4.3 弹性布局的方向
- 4.4 对齐、间距等细节处
- 4.5 本章小结
- 第五章 网格布局(已完结)
- 5.1 构建基础网格
- 5.2 网格结构剖析 (上)
- 5.2.1 网格线的编号(下)
- 5.2.2 网格与 Flexbox 配合(下)
- 5.3 两种替代语法
- 5.3.1 命名网格线
- 5.3.2 命名网格区域
- 5.4 显式网格与隐式网格(上)
- 5.4.1 添加变化 (中)
- 5.4.2 让网格元素填满网格轨道(下)
- 5.5 子网格(全新增补内容)
- 5.6 对齐相关的属性
- 5.7 本章小结
- 第六章 定位与堆叠上下文(已完结)
- 6.1 固定定位
- 6.1.1 创建一个固定定位的模态对话框
- 6.1.2 在模态对话框打开时防止屏幕滚动
- 6.1.3 控制定位元素的大小
- 6.2 绝对定位
- 6.2.1 关闭按钮的绝对定位
- 6.2.2 伪元素的定位问题
- 6.3 相对定位
- 6.3.1 创建下拉菜单(上)
- 6.3.2 创建 CSS 三角形(下)
- 6.4 堆叠上下文与 z-index
- 6.4.1 理解渲染过程与堆叠顺序(上)
- 6.4.2 用 z-index 控制堆叠顺序(上)
- 6.4.3 深入理解堆叠上下文(下)
- 6.5 粘性定位
- 6.6 本章小结
- 第七章 响应式设计(已完结)
- 7.1 移动端优先设计原则(上篇)
- 7.1.1 创建移动端菜单(下篇)
- 7.1.2 给视口添加 meta 标签(下篇)
- 7.2 媒体查询(上篇)
- 7.2.1 深入理解媒体查询的类型(上篇)
- 7.2.2 页面断点的添加(中篇)
- 7.2.3 响应式列的添加(下篇)
- 7.3 流式布局
- 7.4 响应式图片
- 7.5 本章小结
- 第八章 层叠图层及其嵌套
- 8.1 用 layer 图层来操控层叠规则(上篇)
- 8.1.1 图层的定义(上篇)
- 8.1.2 图层的顺序与优先级(下篇)
- 8.1.3 revert-layer 关键字(下篇)
- 8.2 层叠图层的推荐组织方案
- 8.3 伪类 :is() 和 :where() 的用法
- 8.4 CSS 嵌套的使用
- 8.4.1 嵌套选择器的使用
- 8.4.2 深入理解嵌套选择器
- 8.4.3 媒体查询及其他 @规则 的嵌套
- 8.5 本章小结
- 第九章 CSS 的模块化与作用域
- 9.1 模块的定义
- 9.1.1 模块和全局样式
- 9.1.2 一个简单的 CSS 模块
- 9.1.3 模块的变体
- 9.1.4 多元素模块
- 9.2 将模块组合为更大的结构
- 9.2.1 模块中多个职责的拆分
- 9.2.2 模块的命名
- 9.3 CSS 的作用域
- 9.3.1 CSS 作用域的就近原则
- 9.3.2 划定作用域的边界
- 9.3.3 CSS 中的隐式作用域
- 9.3.4 关于 CSS 作用域与层叠图层
- 9.4 CSS 模式库
- 9.5 本章小结