【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 章 容器查询)
    • [10.1 容器查询的一个简单示例 A basic example of a container query](#10.1 容器查询的一个简单示例 A basic example of a container query)
      • [10.1.1 容器尺寸查询的用法 Using container size queries](#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 标记

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 两栏布局与媒体模块的样式代码

css 复制代码
@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,例如:

css 复制代码
container-name: layout;
container-type: inline-size;

这样就给容器指定了一个名称 layout,并且限定开发者只能根据其行内尺寸或宽度进行容器查询。稍后我们还将重点考察这些属性中的各种取值,但眼下这些配置已经足以构建出一个容器查询的基本示例了。

上述两个属性声明也可以通过 container 简写属性等效替换。这通常也是设置它们的最简单的方式:

css 复制代码
container: layout / inline-size;

定义好了容器,就可以使用 @container 来查询宽度了,然后再对容器内的任意元素设置相应的样式。具体写法类似于媒体查询,例如 @container layout (min-width: 450px);或者使用带区间范围的写法 @container layout (width >= 450px)。这里的 layout 指代容器上定义的容器名称。当目标容器满足指定的尺寸大小时,该查询内的样式声明就会生效,在本例中即为:当容器的宽度大于或等于 450px 时生效对应的样式。

代码清单 10.3 给出了示例媒体模块的容器查询样式代码,请按照以下内容更新本地样式表。

代码清单 10.3 添加容器查询后的示例样式代码

css 复制代码
@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 开发者工具启用响应式的设计模式)。此时,媒体模块相当于拥有了四个断点尺寸:在小尺寸视口中作上下布局排列;宽度介于 450px800px 时则恢复为水平布局;宽度介于 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 本章小结
相关推荐
小吕学编程15 分钟前
ES练习册
java·前端·elasticsearch
Asthenia041222 分钟前
Netty编解码器详解与实战
前端
袁煦丞27 分钟前
每天省2小时!这个网盘神器让我告别云存储混乱(附内网穿透神操作)
前端·程序员·远程工作
一个专注写代码的程序媛1 小时前
vue组件间通信
前端·javascript·vue.js
一笑code2 小时前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员2 小时前
layui时间范围
前端·javascript·layui
NoneCoder2 小时前
HTML响应式网页设计与跨平台适配
前端·html
凯哥19702 小时前
在 Uni-app 做的后台中使用 Howler.js 实现强大的音频播放功能
前端
烛阴2 小时前
面试必考!一招教你区分JavaScript静态函数和普通函数,快收藏!
前端·javascript
GetcharZp2 小时前
xterm.js 终端神器到底有多强?用了才知道!
前端·后端·go