1. 引言
本文是针对 CSS Sizing Level 3 的差异规范。 当前为探索性工作草案: 如果你正在实现任何内容,请以上述 Level 3 作为参考。 一旦本草案达到 CR 阶段,我们将把 Level 3 的内容合并进来。
1.1. 模块交互
本模块扩展了 width、height、min-width、min-height、max-width、max-height 以及 column-width,这些特性定义于 [CSS2] 第10章和 [CSS3COL] 中。
1.2. 值定义
本规范遵循 CSS 属性定义约定(见 [CSS2]),并采用 值定义语法(见 [CSS-VALUES-3])。 未在本规范中定义的值类型在 CSS Values & Units [CSS-VALUES-3] 中定义。 与其它 CSS 模块结合时,这些值类型的定义可能会被扩展。
除了在各自定义中列出的属性特定值外, 本规范定义的所有属性 也接受 CSS 全局关键字 作为属性值。 为了可读性,这些值未在定义中重复列出。
2. 术语
3. 盒子尺寸的指定
3.1. 尺寸属性
添加简写。<https://github.com/w3c/csswg-drafts/issues/820>
3.2. 新的尺寸值:stretch、fit-content 和 contain 关键字
| 名称: | width、height、inline-size、block-size、min-width、min-height、min-inline-size、min-block-size、max-width、max-height、max-inline-size、max-block-size |
|---|---|
| 新值: | stretch | fit-content | contain |
- stretch
- 应用拉伸适配尺寸, 尝试使盒子的外边距盒大小 匹配其包含块的大小。 参见§ 6.1 拉伸适配尺寸:填充包含块。
- fit-content
- 本质上等价于 fit-content(stretch),即 min(max-content, max(min-content, stretch))。
- contain
- 如果盒子有首选宽高比, 则应用包含适配尺寸, 尝试在保持其首选宽高比的同时 适应盒子的约束条件。 参见§ 6.2 包含适配尺寸:在保持宽高比的同时拉伸。
4. 宽高比
图片通常有自然宽高比, CSS 布局算法在调整元素大小时会尽量保持该比例。
aspect-ratio 属性允许为非替换元素指定此行为, 并可用于修改替换元素的实际宽高比。
本节细节仍在完善中。 如果这里有任何行为会导致替换元素在拥有首选宽高比时 的表现与 CSS2、Flex 布局 和 Grid 布局 规范的要求不同(即使本规范未生效时),这属于规范错误,请向 CSSWG 报告。
4.1. 首选宽高比:aspect-ratio 属性
| 名称: | aspect-ratio |
|---|---|
| 值: | auto || <ratio> |
| 初始值: | auto |
| 适用对象: | 所有元素,除了 行内盒 和内部 ruby/table 盒 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 指定关键字或一对数字 |
| 规范顺序: | 依语法 |
| 动画类型: | 由计算值决定 |
该属性为盒子设置一个首选宽高比, 在计算 auto 尺寸 及其它某些布局功能时将使用此比例。
- auto
- 拥有自然宽高比的替换元素使用其自然宽高比; 否则,盒子没有首选宽高比。 与宽高比相关的尺寸计算 始终以内容盒尺寸为准。
- <ratio>
- 盒子的首选宽高比为指定的 width / height 比值。 与宽高比相关的尺寸计算 以 box-sizing 指定的盒子尺寸为准。
- auto && <ratio>
- 同时指定auto和<ratio>时, 首选宽高比为指定的 width / height 比值,除非该元素是具有 自然宽高比 的替换元素,此时使用自然宽高比。 所有情况下,相关尺寸计算均基于 内容盒 尺寸。
注意: 拥有首选宽高比不会使盒子变为替换元素; 针对 替换元素 的特定布局规则通常不适用于 拥有 首选宽高比 的非替换盒子。 例如,非替换 绝对定位盒子 会将 justify-self: normal 视为 stretch,而不是 start(参见CSS Box Alignment 3 §6.1.2 绝对定位盒子), 即使其有 首选宽高比
CSS2.1 没有清晰区分替换元素与有宽高比的元素; 需明确不清楚的具体情况,并在对应 Level 3 规范或本规范中加以定义。
< ul > < li > …< li > …< li > …< li > …</ ul >
ul{ display : grid; grid-template-columns : repeat ( auto-fill, minmax ( 12 em , 1 fr )); } li{ aspect-ratio : 1 /1 ; overflow : auto; }
iframe
元素的 width 和 height 属性
设置 aspect-ratio 属性,
为 iframe 提供宽高比用于尺寸计算,
使其表现与具有相同宽高比的图片完全一致。
< iframe src = "https://www.youtube.com/embed/0Gr1XSyxZy0" width = 560 height = 315 >
@supports ( aspect-ratio:attr ( width number) /1 ) { iframe{ aspect-ratio : attr ( width number) /attr ( height number); width : 100 % ; height : auto; } }
如果替换元素唯一的自然尺寸是自然宽度或自然高度, 给其设置首选宽高比也会赋予其缺失的 自然 高度或宽度, 通过现有尺寸和 首选宽高比 转换得到。
4.2. 首选宽高比对自动尺寸的影响
当盒子有首选宽高比时, 其自动尺寸的计算与 拥有自然宽高比且在该轴上无自然尺寸的替换元素相同, 参见 CSS2 § 10 以及 CSS Flexible Box Model Level 1 § 9.2。 哪个轴的首选尺寸计算 依赖该宽高比, 称为 比例依赖轴, 若其输入尺寸为 确定值,则计算结果也为 确定值。 相反的轴(其尺寸依赖于 比例依赖轴)为 比例决定轴。
注意:首选宽高比只有在至少有一个盒子尺寸为 自动 时才会生效。 若 width 和 height 都不是 自动尺寸, 则对其首选尺寸无影响。
当我们把所有尺寸信息移到本规范后, 而不是强行塞进 2.1 时, 其核心原则就是: 比例决定轴(ratio-determining axis)上的最终首选尺寸(在应用 min/max 之前) 通过宽高比进行转换。 min/max 约束随后传递, 然后分别独立地作用于各轴,与宽高比无关。
4.2.1. 外边距折叠
为了实现外边距折叠(CSS 2 §8.3.1 外边距折叠), 如果块轴是比例依赖轴, 则其计算得到的block-size 不被视为 auto。
4.3. 基于内容的自动最小尺寸
为了避免意外溢出, 具有首选宽高比,但既不是替换元素也不是滚动容器的盒子,其比例依赖轴上的自动最小尺寸 是其min-content 尺寸,但不超过其最大尺寸。
div{ aspect-ratio : 1 /1 ; /* 'width' 和 'height' 默认都是 'auto' */ }
+----------+ +----------+ +----------+
| ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ |
| ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ |
| ~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ |
| | | ~~~ | | ~~~~~~~~ |
+----------+ +----------+ | ~~~~~~~~ |
| ~~~~~~ |
+----------+
但当指定 overflow: auto 时, 即使内容溢出,盒子仍会保持 1:1 的宽高比 (并像通常一样通过可滚动来处理溢出)。
div{ overflow : auto; aspect-ratio : 1 /1 ; }
+----------+ +----------+ +----------+ | ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~^| | ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ | | | | ~~~ | | ~~~~~~~~v| +----------+ +----------+ +----------+
覆盖 min-height 属性同样会保持 1:1 的宽高比, 但如果没有其他处理,内容将会溢出盒子。
div{ aspect-ratio : 1 /1 ; min-height : 0 ; }
+----------+ +----------+ +----------+
| ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ |
| ~~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ |
| ~~~~~~~ | | ~~~~~~~~ | | ~~~~~~~~ |
| | | ~~~ | | ~~~~~~~~ |
+----------+ +----------+ +-~~~~~~~~-+
~~~~~~
< div style = "height: 100px; aspect-ratio: 1/1;" > < span style = "display: inline-block; width: 50px;" ></ span > < span style = "display: inline-block; width: 150px;" ></ span > </ div >
容器的 width,由于 auto, 通过宽高比转为 100px。 但它的 min-width,因为 auto, 解析为 150px。 所以容器的最终宽度为 150px。 若希望在计算容器尺寸时忽略内容,可指定 min-width: 0。
4.4. 最小/最大尺寸传递
任一方向(origin 轴)的尺寸约束, 都会通过首选宽高比传递,并应用到另一方向(destination 轴)的任何不定的最小、最大或首选尺寸,具体如下:
-
首先,将任意确定的最小尺寸 从 origin 轴换算并传递到 destination 轴。 这个传递后的最小值会被 确定的首选 或最大尺寸 限制。
-
然后,将任意确定的最大尺寸 从 origin 轴换算并传递到 destination。 这个传递后的最大值会被 确定的首选 或最小尺寸 以及传递过来的最小值(如有)限制。
注意: 这样,任何确定的尺寸都不会受传递约束影响; 且通过传递约束得到的最小值不会使元素超过确定的首选/最大值, 传递最大值同样不会导致元素低于其首选/最小值。
注意: 基本原则是尺寸约束通过宽高比传递到另一方向, 在不违反该方向显式指定尺寸的前提下,尽量保持宽高比。 (这也是 CSS2 第10.4节中的约束表 的理论基础。)
< div id = container style = "height: 100px; float: left;" > < div id = item style = "height: 100%; aspect-ratio: 1/1;" > content</ div > </ div >
由于 #item 的高度为百分比且依赖于确定的容器,
item 的宽度也会解析为 100px,无论其内在尺寸贡献还是最终布局,
所以容器的宽度也会是 100px。
< div id = container style = "height: auto; float: left;" > < div id = item style = "height: 100%; aspect-ratio: 1/1;" > content</ div > </ div >
在下一个例子中, item 的百分比高度无法解析,将表现为 auto (参见 CSS 2 §10.5 内容高度:'height' 属性)。 由于两个轴现在都是自动尺寸, 高度就成为比例依赖轴。 盒子的内在尺寸贡献, 宽度由内容决定, 高度则由宽度和宽高比计算得出, 得到一个以“content”单词宽度为边长的正方形盒子(以及容器)。
本节可能存在表述不当。<https://github.com/w3c/csswg-drafts/issues/6071>
5. 内在尺寸判定
5.1. 内在尺寸
5.2. 覆盖包含的内在尺寸:contain-intrinsic-* 属性
| 名称: | contain-intrinsic-width、contain-intrinsic-height、contain-intrinsic-block-size、contain-intrinsic-inline-size |
|---|---|
| 值: | none | <length> | auto && <length> |
| 初始值: | none |
| 适用对象: | 具有尺寸包含的元素 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 如指定,<length> 值为计算值 |
| 规范顺序: | 依语法 |
| 动画类型: | 按计算值类型 |
这些属性允许具有尺寸包含的元素 指定一个显式内在内部尺寸, 使盒子表现得像其流内内容总宽高等于指定的显式内在内部尺寸(而非表现为空)。
注意: 这并不总等价于布局时元素有一个指定显式内在内部尺寸的子元素。 例如,一个网格容器只有一个指定尺寸的子项, 仍然会按网格规则布局,通常内容尺寸会大于指定值。
值定义如下:
- none
-
对应方向没有显式内在内部尺寸。
- <length>
- auto && <length>
如果元素某一方向有显式内在内部尺寸, 那么在按尺寸包含常规布局后, 该方向内容尺寸应视为 显式内在内部尺寸,而不是布局计算结果, 如有必要则重新布局。 (如果两个方向都有 显式内在内部尺寸,可跳过首次布局。)
这四个属性属于逻辑属性组的一部分。
注意: 含有尺寸包含的元素会像没有内容一样布局 [CSS-CONTAIN-1], 这通常会导致元素内高度塌缩为零。 这可以通过显式指定 height 来修正, 但这在某些布局系统(如 Flex 和 Grid 布局)会带来副作用, 因为显式 height 比隐式内容高度优先级高。 因此,该元素的布局可能与直接填充到该高度的内容不同。 给元素提供显式内在内部尺寸, 能在提高布局性能的同时, 让其表现得像拥有内容一样。
| 名称: | contain-intrinsic-size |
|---|---|
| 值: | [ none | <length> | auto && <length> ]{1,2} |
| 初始值: | 见各自属性 |
| 适用对象: | 见各自属性 |
| 是否继承: | 见各自属性 |
| 百分比: | 见各自属性 |
| 计算值: | 见各自属性 |
| 动画类型: | 见各自属性 |
| 规范顺序: | 依语法 |
contain-intrinsic-size 是一个简写属性, 用于同时设置 contain-intrinsic-width 和 contain-intrinsic-height 属性。
第一个值表示 contain-intrinsic-width, 第二个值表示 contain-intrinsic-height。 如果只提供一个值, 则同时用于这两个属性。
5.2.1. 上次记忆尺寸
尺寸包含对于确保页面高效渲染非常有价值, 它限制了由于元素渲染变化而可能影响的布局工作范围。 但这对作者来说也很苛刻, 需要他们准确预测元素的尺寸; 如果这个预测哪怕有一点点偏差, 也可能导致难看的滚动条或内容被意外隐藏。
contain-intrinsic-size: auto 值提供了一种折中方案: 如果一个元素曾经不是尺寸包含的, 这个值会让元素记住它的尺寸 (由布局常规计算得出); 然后,如果该元素后来获得了尺寸包含, 它将使用记忆的尺寸, 这样既有尺寸包含带来的性能优势, 又大概率能准确匹配其内容尺寸。
-
在 ResizeObserver 事件被确定和派发时, 如果元素有 contain-intrinsic-size: auto,但没有 尺寸包含, 则记录其当前内部尺寸 作为其上次记忆尺寸。
如果元素从未在没有尺寸包含的情况下渲染过, 则该元素可能没有上次记忆尺寸。 (此时,将使用和 auto 一起提供的回退值。)
5.2.2. 与 overflow: auto 的交互
contain-intrinsic-size 属性提供了作者预估元素内容尺寸的估算值, 但这个估算并不是实际内容 也不代表需要向用户展示的内容。 因此,带有 overflow: auto 的元素不能因为 contain-intrinsic-size 而生成滚动条。
但如果 contain-intrinsic-size 估算的尺寸足够大, 以至于如果真有这么大的内容会生成滚动条, 那么该元素布局时应按假定内容有滚动条的方式进行尺寸计算。
div{ width : max-content; contain-intrinsic-size : 100 px 100 px ; overflow : auto; }
该元素最终宽度为 100px,高度为 100px:contain-intrinsic-size 提供了 max-content 宽度, 也提供了高度。
如果元素内容变成 150px 高, 会显示一个垂直滚动条; 如果滚动条不为叠加样式, 它会占据一部分 100px 宽度, 剩下的空间 (通常约为 84px) 用于内容流动。 (详见 CSS Overflow 3 §3.2 滚动条与布局。)
即使现在内容的水平空间少于 100px, 也不会因为 contain-intrinsic-size 指定了 100px 宽度而生成水平滚动条; 只有当实际内容有不可断行部分且宽于剩余空间时才会出现。
div{ width : max-content; contain-intrinsic-size : 100 px 100 px ; height : 50 px ; overflow : auto; }
该元素高度被固定为 50px, 但 contain-intrinsic-size 指定了 100px 的“内容估算高度”。 因此,元素假定将需要垂直滚动条, 最终 max-content 宽度会比 100px 稍大(通常约为 116px), 以容纳 contain-intrinsic-size 提供的 100px 最大内容宽度, 以及垂直滚动条宽度(通常约 16px)。
不过,即便元素预留了滚动条空间, 实际内容没有溢出时也不会真正生成滚动条: 如果内容高度小于 50px, 不会生成垂直滚动条, 但元素宽度仍为 116px。
5.3. 内在尺寸贡献
5.4. 最小内容尺寸贡献归零:min-intrinsic-sizing 属性
| 名称: | min-intrinsic-sizing |
|---|---|
| 值: | legacy | zero-if-scroll || zero-if-extrinsic |
| 初始值: | legacy |
| 适用对象: | 所有元素,除了 行内盒 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 如指定 |
| 规范顺序: | 依语法 |
| 动画类型: | 离散 |
该属性定义了最小内容贡献是否在某些情况下被“压缩”。 具体值含义如下:
- legacy
- 盒子的最小内容贡献按常规处理。
- zero-if-scroll
- 盒子的最小内容贡献在其为滚动容器时“压缩”。
- zero-if-extrinsic
-
盒子的最小内容贡献在具有外在 首选
或 最大尺寸时“压缩”。
注意: 这是大多数替换元素的默认行为。
*, ::before, ::after{ min-intrinsic-size : zero-if-scroll; }
这可以防止滚动容器内容很大(如大表格或长不可断行文本)时导致祖先尺寸被撑爆。 同时,非滚动容器仍然可以影响祖先的min-content 尺寸。
注意:zero-if-scroll 的行为原本更适合做默认值, 但出于兼容性考虑,不能作为初始值。:(
“压缩”的最小内容贡献计算方式为: 假装盒子为空, 但如果有显式 min-content、max-content 和 fit-content 值 的尺寸属性,则应计入这些约束。
6. 外在尺寸判定
6.1. 拉伸适配尺寸:填充包含块
拉伸适配尺寸试图将盒子的使用尺寸 设为其外部尺寸尽量填满包含块所需的长度, 同时仍然遵守 min-height/min-width/max-height/max-width 的约束。
形式上,其行为等同于在相关轴上同时指定自动尺寸和自对齐属性值为 stretch, 只不过最终盒子可能不能完全匹配其对齐容器, 这时可以通过其实际的 自对齐属性值再对齐。
此外, 在格式化上下文和相关自对齐属性不适用的轴上 (比如块级布局的块轴,或 Flex 布局的主轴), 如果该轴上的百分比尺寸能解析为确定值, 拉伸适配尺寸会让盒子试图填满包含块——表现如 100%,但应用到外边距盒而非 box-sizing 指定的盒子。 这里 auto 外边距视为 0, 并且对于块级盒, 如果其 block-start/block-end 外边距会与父元素 block-start/block-end 外边距相邻(前提是父元素的尺寸属性均为初始值), 则其 block-start/block-end 外边距按零处理。
注意: 因此,如果既没有 stretch 对齐, 也没有可以解析的百分比尺寸, 则盒子会回退为自动尺寸。
<div class="outer"> <div class="inner"></div> </div>
在下面这个例子中, 内部盒子的外高度 会正好等于外部盒子的高度(200px), 但其内高度会减少 20px(因为有外边距)。
.outer { height: 200px; border: solid; }
.inner { height: stretch; margin: 10px; }
在下一个例子中, 内部盒子的高度 仍等于外部盒子的高度(200px)。 顶部外边距会折叠, 但底部外边距不会折叠 (因为盒子的底部外边距不会与父元素的非 auto 高度的底部外边距相邻,详见 CSS 2 §8.3.1 外边距折叠), 因此内部盒子的底部外边距会被截断。
.outer { height: 200px; margin: 0; }
.inner { height: stretch; margin: 10px; }
<div class="outer"> <div class="inner">text</div> </div> some more text
.outer { float: left; margin: 0; }
.inner { width: stretch; margin: 10px; }
.outer { height: auto; margin: 0; }
.inner { height: stretch; margin: 10px; }
6.2. 包含适配尺寸:在保持宽高比的同时拉伸
包含适配尺寸本质上是应用拉伸适配尺寸, 但会为保持盒子的首选宽高比, 压缩一个方向上的尺寸, 类似于 contain 关键字在 object-fit 和 background-size 属性中的作用。
首先,确定目标矩形:
- 初始目标矩形是盒子的包含块大小, 未定义的尺寸视为无穷大。 如果两个方向都未定义, 则初始目标矩形为 假设盒子为拉伸适配尺寸时的外部边界。
- 然后,如果盒子有非 none 的 max-width 或 max-height, 则目标矩形在受影响方向上被限制为不大于盒子外边距盒的“最大尺寸”, 即假设盒子被尺寸为 max-width/height 时外边距盒的大小。 (注意,和常规 box-sizing 规则一致, 这个“最大尺寸”会被盒子的 min-width/min-height 限制为下界。)
- 最后,目标矩形在一个方向上被缩小, 以使其刚好符合盒子的首选宽高比。
每个方向上的包含适配尺寸 就是拉伸适配到目标矩形后得到的尺寸。
如果某一方向上的最小尺寸 会导致目标矩形因保持宽高比而溢出, 是优先保持宽高比还是图片变形? 如果优先保持宽高比,则需要类似第2步的处理来应用相关最小值。
6.3. 百分比尺寸
…
变更记录
最近变更
自 2020年10月20日工作草案 以来的重要变更包括:
-
起草了 min-intrinsic-sizing 属性, 以更好地控制滚动容器的最小内容贡献。 (议题 1865, 议题 4585)
-
为 contain-intrinsic-size 添加了可单独控制每个方向的长属性。 (议题 5432)
-
为 contain-intrinsic-size 起草了 auto 值,以便“记住”先前计算的尺寸。 (议题 5668, 议题 5815)
-
定义了 aspect-ratio 中退化比的处理方式。 (议题 5557)
-
定义了 aspect-ratio 如何影响替换元素的自然尺寸。 (议题 5306)
-
修正了 § 4.4 最小/最大尺寸传递 章节中的一些错误, 使行为不与 CSS2 / Flex 布局等规范冲突。 (议题 6071)
自 2020年5月26日首次公开工作草案 以来的重要变更包括:
-
定义 比例决定轴 术语。
-
定义 min/max 尺寸约束会跨宽高比传递, (议题 5257)
此外,任一方向(origin 轴)的尺寸约束 都会通过首选宽高比传递到另一方向(destination 轴), 具体如下:
-
首先,将任意确定的最小尺寸 从 origin 轴换算并传递到 destination 轴。 这个传递后的最小值会被 确定的首选 或最大尺寸 限制。
-
然后,将任意确定的最大尺寸 从 origin 轴换算并传递到 destination。 这个传递后的最大值会被 确定的首选 或最小尺寸 以及传递过来的最小值(如有)限制。
注意: 基本原则是尺寸约束会通过宽高比传递到另一方向, 在不违反该方向显式指定尺寸的前提下,尽量保持宽高比。
-
-
明确 aspect-ratio 对于只指定一个替换元素的自然尺寸时如何决定另一个方向。 (议题 5306)
如果替换元素唯一的自然尺寸 是自然宽度或自然高度, 给其设置首选宽高比也会赋予其缺失的自然高度或宽度, 通过现有尺寸和 首选宽高比 进行换算。
-
定义 aspect-ratio 抑制外边距自折叠 (议题 5328)
为了外边距折叠(CSS 2 §8.3.1 外边距折叠), 如果块轴是比例依赖轴, 则不认为其计算得到的block-size 是 auto。
Level 3 以来的新增内容
- 为尺寸属性添加了 stretch、fit-content 和 contain 关键字。
- 添加了 aspect-ratio 属性。
- 添加了 contain-intrinsic-size 属性。
致谢
特别感谢 Aaron Gustafson、L. David Baron 对本模块的贡献。
隐私与安全考虑
本规范未引入新的隐私或安全问题。