1. 介绍
CSS 绝对定位允许作者将盒子放置在页面上的任意位置, 并不考虑除其包含块之外其他盒子的布局。 这种灵活性非常有用, 但也有局限——很多时候你希望相对于某个其他盒子进行定位。 锚点定位 (通过 position-anchor 和 position-area 属性 以及/或者 anchor 函数 anchor() 和 anchor-size()) 让作者能够实现这一点, 即将一个绝对定位盒子“锚定”到页面上的一个或多个其他盒子 (它的锚点引用), 同时也允许尝试多个可能的位置, 以找到能够避免重叠/溢出的“最佳”位置。
.anchor{ anchor-name : --tooltip; } .tooltip{ /* Fixpos 意味着我们无需担心包含块关系; 工具提示可以存在于 DOM 的任意位置。 */ position: fixed; /* 所有锚定行为会默认参考 --tooltip 锚点。 */ position-anchor: --tooltip; /* 让工具提示底部与锚点顶部对齐; 同时默认在水平方向居中对齐工具提示和锚点(在横向书写模式下)。 */ position-area: block-start; /* 如果工具提示溢出窗口会自动翻转, 让工具提示顶部与锚点底部对齐。 */ position-try: flip-block; /* 防止工具提示过宽 */ max-inline-size:20 em ; }
请注意,使用 Popover API 会自动设置 position 并创建锚定关系, 无需显式设置 anchor-name 或 position-anchor 的值 (通过定义一个 隐式锚点元素), 所以这些属性不需要再次显式设置。 因此只要有正确的标记,这个例子可以简化为:
.tooltip{ /* 使用 popover + popovertarget 属性会设置 'position: fixed' */ 并自动创建所需的 position-anchor 关系。 */ position-area: block-start; position-try : flip-block; max-inline-size : 20 em ; }
1.1. 值定义
本规范遵循 CSS 属性定义约定, 并使用 值定义语法。 未在本规范中定义的值类型将在 CSS Values & Units [CSS-VALUES-3] 中定义。 与其他 CSS 模块结合使用时,可能会扩展这些值类型的定义。
除了在各属性定义中列出的特定值外, 本规范定义的所有属性 还接受 CSS 全局关键词作为属性值。 为便于阅读未在定义中重复列出。
与除选择器匹配之外的大多数 CSS 操作一样, 本规范中的功能均作用于 扁平化元素树。
2. 锚点确定
2.1. 创建锚点:anchor-name 属性
| 名称: | anchor-name |
|---|---|
| 取值: | none | <dashed-ident># |
| 初始值: | none |
| 适用对象: | 所有生成 主盒子的元素 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散型 |
anchor-name 属性声明 某元素为锚点元素, 其 主盒子为 锚点盒子, 并赋予它一组锚点名称,以供定位。 取值定义如下:
- none
-
该属性无效果。
- <dashed-ident>#
-
如果该元素生成 主盒子, 则该元素为 锚点元素, 并拥有指定的 锚点名称列表。 每个 锚点名称 是松散匹配的 树作用域名称。
否则,该属性无效果。
锚点名称无需唯一。 并非所有元素都能作为 某个盒子的 目标锚点元素。 因此只要作用域合适,一个名称可在多个位置重复使用。
注意: 如果多个元素共享 锚点名称 且都对某个定位盒子可见, 那么 目标锚点元素将是 DOM 顺序中的最后一个。 anchor-scope 属性可进一步限制 某个引用盒子能看到哪些名称。
锚点名称默认不会 受 包含性作用域限制; 即使某元素使用了 样式或 布局包含 (或类似类型的包含性), 其后代的 锚点名称在页面其他地方的元素依然可见。
注意: 当某元素处于另一个元素的 跳过内容范围内 (例如由于 content-visibility: hidden), 它不被视为 可接受锚点元素, 实际上相当于没有名称。
注意: 处于 shadow tree中的定位元素 可以引用“更高”树上定义的 锚点名称。 当前,它们不能引用“更低” shadow tree 中定义的 锚点名称。
2.1.1. 隐式锚点元素
某些规范可规定在特定情况下, 某个元素为另一个元素的隐式锚点元素。
TODO: 填写新 popover 相关示例细节 (待 HTML 规范正式收录后补充)。
隐式锚点元素可通过 auto 关键字在 position-anchor 中引用, 或在 anchor 函数中省略锚点引用来引用。
2.1.2. 锚点盒子
本规范中的多个功能会引用锚点盒子的位置和尺寸。 除非另有说明, 这里指的是 border box 边缘, 即 主盒子的 锚点元素。 锚点盒子的位置和尺寸在布局后确定。
这个位置和尺寸包括 zoom 以及 position 相关调整 (如 position: relative 或 position: sticky), 还包括变换(如 transform 或 offset-path)。 在这些情况下,使用该 锚点盒子在 绝对定位元素的 包含块坐标空间中的 轴对齐包围矩形。 变换通常在不同线程优化, 因此基于变换的 锚点盒子位置更新 可能会延迟几帧。 作者可根据实际情况优先采用绝对或相对定位来避免此延迟。
如果 锚点盒子发生了 分片, 且对应的 包含块在 分片上下文之外, 则使用其 盒子分片的轴对齐包围矩形。 (如果 绝对定位盒子在 分片上下文内, 它看到的 锚点盒子不分片——且自身也可被 分片上下文分片。)
出于性能考虑, 滚动会特别处理,详见 § 3.3 滚动处理。 其他布局后效果,如滤镜, 并不影响 锚点盒子的位置。
2.2. 锚点名称作用域:anchor-scope 属性
| 名称: | anchor-scope |
|---|---|
| 取值: | none | all | <dashed-ident># |
| 初始值: | none |
| 适用对象: | 所有元素 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散型 |
该属性将指定的锚点名称及其查找, 限定在该元素的子树内。 参见§ 2 锚点确定。
取值含义如下:
- none
- 锚点名称作用域无变化。
- all
-
指定该元素或其后代定义的所有锚点名称(未被后代使用 anchor-scope 限制的)仅对子孙元素可见;
并限制后代仅能匹配该子树内的锚点元素上的
锚点名称。
该值仅影响同一树作用域中的锚点名称, 就像它是一个严格匹配的树作用域名称。 (即 anchor-scope: all作用等同于 anchor-scope: --foo, --bar, ..., 列举所有相关的锚点名称。)
- <dashed-ident>
-
指定该元素或其后代定义且未被后代使用 anchor-scope 限制的匹配锚点名称,仅对子孙元素可见;
并限制后代仅能匹配该子树内的锚点元素上的这些锚点名称。
<dashed-ident> 代表严格匹配的树作用域名称, 即只能在同一个 shadow tree 内匹配 锚点名称。[CSS-SCOPING-1]
该属性对隐式锚点元素无影响。
li{ anchor-name : --list-item; anchor-scope : --list-item; } li .positioned{ position : absolute; position-anchor : --list-item; position-area : inline-start; }
如果没有anchor-scope,
所有
li
元素对所有定位元素都是可见的,
这样所有定位元素都会相对于最后一个
li
定位,导致它们堆叠在一起。
2.3. 查找锚点
本规范中的多个内容会根据一个锚点指定符 查找目标锚点元素, 该指定符可以是<dashed-ident> (且为树作用域引用), 用于匹配页面其他位置的anchor-name值, 或关键字auto, 或为空(缺失指定符)。
注意: 这些条件的通用规则是, 某元素只有在它自己的盒子完全布局完成后, 才能作为定位盒子的目标锚点元素, 即定位盒子引用它时也已布局完成。 CSS 的布局规则根据锚点与定位盒子及其包含块的关系, 提供了有用的保证。 下方的条件列表 精确定义了与本目的相关的堆叠上下文规则, 确保锚点定位不会出现循环引用。
-
如果未传入 anchor spec, 则返回 默认锚点元素(若存在),否则返回空。
-
如果 anchor spec 是 auto:
注意: 未来的 API 可能也会定义隐式锚点元素, 届时会在此算法中明确处理, 以确保协调。
-
否则,anchor spec 为<dashed-ident>。 返回树序中最后一个满足以下条件的元素 el:
若无元素满足上述条件,返回空。
注意: anchor-scope 可以限制特定锚点名称的可见性, 影响哪些元素可作为查找的锚点元素。
注意: 不同anchor-name样式定义的shadow tree 不能被其他 shadow tree 中的anchor 函数样式看到, 从而保证封装性。 但不同 shadow tree 中的元素仍可相互锚定, 只要 anchor-name 和 anchor 函数 都来自同一个树的样式, 例如使用 ::part() 给 shadow 内的元素加样式。 (隐式锚点元素也不一定局限于单一树, 但具体细节取决于分配锚点元素的 API。)
-
possible anchor 对于 positioned el 必须在 anchor-scope 的作用范围内, 由 possible anchor 或其祖先决定。
-
possible anchor 必须严格早于 positioned el 布局, 即下列之一满足:
-
如果 possible anchor 处于其他元素的 跳过内容范围, 那么 positioned el 也必须处于该元素的 跳过内容范围内。
注意: 换句话说,positioned el 能锚定到 possible anchor, 前提是它们在同一跳过“叶子”中,但不能跨叶子锚定。 这意味着跳过包含两者的元素不会导致 positioned el 锚定到其他锚点, 但仍然会阻止页面其他地方的定位元素锚定到该被跳过的元素。
2.4. 默认锚点:position-anchor 属性
| 名称: | position-anchor |
|---|---|
| 取值: | auto | <anchor-name> |
| 初始值: | auto |
| 适用对象: | 绝对定位盒子 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散型 |
position-anchor 属性指定默认锚点元素, 该元素会被 position-area、position-try 和(默认情况下)应用于此元素的所有 anchor functions使用。 position-anchor 是 仅重置子属性,属于 position 的一部分。
- auto
- <anchor-name>
-
由指定的 <anchor-name> 选中的 目标锚点元素 成为该盒子的 默认锚点元素。
默认锚点元素的主盒子是该盒子的默认锚点盒子。
.anchored{ position : absolute; top : calc ( .5 em +anchor ( outside)); /* 未指定锚点名称时, 会自动引用默认锚点盒子。 */ } .foo.anchored{ position-anchor : --foo; } .bar.anchored{ position-anchor : --bar; }
2.5. 锚点相关性
判断某元素 el 是否对用户相关时, 如果 el 的某个后代是定位盒子的 目标锚点元素, (该定位盒子本身未被跳过, 且其 包含块不是 el 或其后代), 则 el 必须视为 对用户相关。
注意: 这意味着,例如, 在 content-visibility: auto 子树中的锚点 会阻止其子树跳过内容, 只要依赖它的定位盒子也未被 跳过。 (除非锚点和定位盒子都在同一个 content-visibility: auto 元素下; 它们不能循环让彼此保持可见。)
3. 基于锚点的定位
绝对定位盒子 可以相对于页面上的一个或多个 锚点盒子进行定位。
position-area 属性提供了一个便捷的基于网格的定位概念, 用于相对于 默认锚点盒子定位; 若需更复杂或相对于多个盒子的定位, 可在inset 属性中使用 anchor() 函数 明确引用某个 锚点盒子的边。
3.1. position-area 属性
| 名称: | position-area |
|---|---|
| 取值: | none | <position-area> |
| 初始值: | none |
| 适用对象: | 具有默认锚点盒子的定位盒子 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 关键字 none 或一对关键字,详见 § 3.1.3 <position-area> 的计算值与序列化 |
| 规范顺序: | 按语法 |
| 动画类型: | TBD |
锚点定位的常见用例 只涉及定位盒子的 包含块边缘 和 默认锚点盒子边缘。 这些线可以看作定义了一个 3×3 网格; position-area 让你可以轻松指定 该 position-area 网格的哪个区域布局定位盒子。
- none
-
该属性无效果。
- <position-area>
-
如果该盒子没有 默认锚点盒子, 或不是 绝对定位盒子, 则该值无效果。
否则,选择 position-area 网格的某一区域, 并将其作为盒子的 包含块。
注意: 这意味着 inset 属性指定的是相对于 position-area 的偏移, 一些属性值(如 max-height: 100%) 也会相对于 position-area。
除 none 外的值有如下附加效果:
-
normal 的 自身对齐属性 会解析为相应的值,详见 § 4.1 区域默认对齐。
3.1.1. 解析 Position Area 网格
position-area 网格是一个 3×3 的网格, 每个轴由四条网格线组成。 顺序如下(使用 包含块的书写模式):
注意: 当 默认锚点盒子 部分或全部在修改前的 包含块之外时, position-area 网格的某些行或列可能为零尺寸。
3.1.2. <position-area> 取值语法
位置由一对值指定, 可用 流向相关或 物理术语表达。 <position-area> 的允许语法为:
<position-area> = [
[ left | center | right | span-left | span-right
| x-start | x-end | span-x-start | span-x-end
| self-x-start | self-x-end | span-self-x-start | span-self-x-end
| span-all ]
||
[ top | center | bottom | span-top | span-bottom
| y-start | y-end | span-y-start | span-y-end
| self-y-start | self-y-end | span-self-y-start | span-self-y-end
| span-all ]
|
[ block-start | center | block-end | span-block-start | span-block-end | span-all ]
||
[ inline-start | center | inline-end | span-inline-start | span-inline-end
| span-all ]
|
[ self-block-start | center | self-block-end | span-self-block-start
| span-self-block-end | span-all ]
||
[ self-inline-start | center | self-inline-end | span-self-inline-start
| span-self-inline-end | span-all ]
|
[ start | center | end | span-start | span-end | span-all ]{1,2}
|
[ self-start | center | self-end | span-self-start | span-self-end | span-all ]{1,2}
]
<position-area> 取值通过如下方式指定所选区域占据的网格行和列,从而选中 position-area 网格的某个区域:
- start, end, self-start, self-end
- top, bottom, left, right
- y-start, y-end, self-y-start, self-y-end
- x-start, x-end, self-x-start, self-x-end
- block-start, block-end, self-block-start, self-block-end
- inline-start, inline-end, self-inline-start, self-inline-end
- center
- top, bottom, left, right
-
对应的单个行或列, 取决于该关键字指定的是哪个轴。
类似于 anchor(), 普通逻辑关键字 (start、end 等) 指的是盒子的 包含块的书写模式。 x-start 等在指定物理轴的方向上也是一样。
self-* 逻辑关键字 (self-start、self-x-end 等) 是一样的,但指的是盒子自己的 书写模式。
- span-start, span-end, span-self-start, span-self-end
- span-top, span-bottom, span-left, span-right
- span-y-start, span-y-end, span-self-y-start, span-self-y-end
- span-x-start, span-x-end, span-self-x-start, span-self-x-end
- span-block-start, span-block-end, span-self-block-start, span-self-block-end
- span-inline-start, span-inline-end, span-self-inline-start, span-self-inline-end
- span-top, span-bottom, span-left, span-right
-
对应两个相邻的行或列, 取决于该关键字指定的是哪个轴: 包含中心行/列, 及与该关键字另一半相对应的行/列(参考单轨关键字)。
(例如 span-top 跨越前两行——中心行和顶部行。)
- span-all
-
对应所有三行或列, 取决于该关键字指定的是哪个轴。
有些关键字对于所属轴是模糊的: center、span-all, 以及未明确指定块或内联轴的 start 等关键字。 如果另一个关键字对于轴是明确的, 则模糊关键字指的是相反的轴。 (例如 block-start center 中, center 指的是内联轴。) 如果两个关键字都模糊, 则第一个关键字指盒子的 块轴, 第二个指 内联轴。 (例如 span-all start 等价于 span-all inline-start。)
如果只给出一个关键字, 且该关键字对于轴是明确的,则等价于第二个关键字为 span-all; 否则等价于重复该关键字。 (例如 top 等价于 top span-all, center 等价于 center center。)
3.1.3. <position-area> 的计算值与序列化
计算值为两个关键字,分别指明每个轴选中的区段, 长(block-start)和短(start)逻辑关键字视为等价。 按上方语法顺序序列化, 逻辑关键字用短形式 (如 start start 而不是 block-start inline-start)。
3.2. 锚点相对内边距:anchor() 函数
绝对定位盒子 可以在其 inset 属性中使用 anchor() 函数, 用于引用一个或多个 锚点盒子 的位置。 anchor() 函数会解析为 <length>。 它仅允许用于 inset 属性 (其他地方无效)。
| 名称: | top, left, right, bottom |
|---|---|
| 新增值: | <anchor()> |
<anchor()> = anchor( <anchor-name>? && <anchor-side>, <length-percentage>? )
<anchor-name> = <dashed-ident>
<anchor-side> = inside | outside
| top | left | right | bottom
| start | end | self-start | self-end
| <percentage> | center
anchor() 函数有三个参数:
-
<anchor-name> 值 用于确定要从哪个 锚点元素 获取定位信息。 其可能取值为:
- <dashed-ident>
- 省略
-
选择盒子定义的 默认锚点元素(如有)。
详细见 目标锚点元素。
-
<anchor-side> 值 表示引用 目标锚点元素的对应边的位置。 其可能取值为:
- inside
- outside
-
根据它在 inset 属性中的使用情况, 解析为 锚点盒子 的某一侧。 inside 表示与 inset 属性同侧 (使定位盒子贴附在 锚点盒子“内侧”), outside 表示相反一侧。
- top
- right
- bottom
- left
- right
-
引用 锚点盒子的指定边。
注意: 这些值只能在对应轴的 inset 属性中使用。 例如,left 可用于 left、right 或逻辑上的 inset 属性(指水平方向)。
- start
- end
- self-start
- self-end
- end
-
引用 锚点盒子 在与 inset 属性同轴上的某一侧, 通过关键字与定位盒子的 书写模式解析: 对于 self-start 和 self-end,参考定位盒子自身; 对于 start 和 end,参考定位盒子的包含块。
- <percentage>
- center
-
引用从 start 到 end 边之间的百分比位置, 0% 相当于 start, 100% 相当于 end。
center 等价于 50%。
- inside
-
可选的 <length-percentage> 最后一个参数是回退值, 指定当函数为 不可解析锚点函数时应计算为何值。
代表 可解析锚点函数的 anchor() 函数 会在 计算值阶段解析 (使用 样式与布局交错), 得到将定位盒子的 <length>, 使其 inset 修正包含块对应边 与指定的 目标锚点元素的 锚点盒子边对齐。
注意: 这意味着使用 过渡 或 动画 的属性如果包含 锚点函数, 都能“按预期”响应各种变化: 包括 锚点盒子移动、 锚点元素增删、 anchor-name 属性变化等情形。
.bar 元素的 block-start 边
与 --foo 锚点的 block-start 边对齐的长度。
另一种情况,
.bar { inset-block-end: anchor(--foo block-start); },
则会解析为
让 .bar 元素的 block-end 边
与 --foo 锚点的 block-start 边对齐的长度。
由于 inset-block-start 和 inset-block-end 分别指定了相对于元素 包含块 的 block-start 和 block-end 边的内边距, 同一个 anchor() 通常会在二者中解析为不同的长度。
需要更好的示例; 这个例子其实可以用 anchor-center 简单实现。[Issue #10776]
例如,下面的写法会让元素的 inset 修正包含块 在 锚点盒子上居中, 且宽度最大化但不溢出 包含块:
.centered-message{ position : fixed; max-width : max-content; justify-self : center; --center : anchor ( --x50 % ); --half-distance : min ( abs ( 0 % -var ( --center)), abs ( 100 % -var ( --center)) ); left : calc ( var ( --center) -var ( --half-distance)); right : calc ( var ( --center) -var ( --half-distance)); bottom : anchor ( --x top); }
这适用于例如
input
元素上的错误信息,
居中显示有助于用户发现是哪个输入框被引用。
3.2.1. anchor() 的解析
只有同时满足以下所有条件时, anchor() 函数才是 可解析锚点函数:
-
它应用于 绝对定位盒子。
-
如果其 <anchor-side> 指定了物理关键字, 那么它必须用于该轴可用的 inset 属性。 (例如,left 只能用于 left、right, 或水平方向的逻辑 inset 属性。)
-
所应用的盒子,以及函数中指定的 <anchor-name> 值,都有对应的 目标锚点元素。
如果上述任一条件不满足, anchor() 函数会被 解析为指定的回退值。 如果未指定回退值, 则会使引用它的声明在 计算值阶段无效。
3.3. 滚动处理
出于性能考虑, 实现通常会在单独的滚动/“合成”线程上执行滚动, 该线程能力有限 (只能做简单移动/变换等,不能做布局等耗时操作), 因此可以确保滚动响应足够快,被人类感知为“即时”。
如果滚动仅仅导致锚点定位的元素移动, 理论上不会有问题; 这种移动可以在滚动线程上完成, 定位元素会随着滚动内容平滑移动。 但 锚点定位 允许一个元素自身的对边位置依赖于不同滚动上下文的内容, 这意味着滚动可能只移动一侧导致尺寸变化, 进而需要布局。 这种情况无法在滚动线程上完成!
为此, 又要尽可能允许锚定到各种元素, 锚点定位 采用了 记忆滚动偏移 与 滚动补偿 的策略。
-
当定位元素首次显示, 或 切换回退 时, 会根据所有 锚点引用的最新位置 正确计算定位。
如果这些 锚点引用 在不同滚动上下文, 会记住它们的总滚动偏移, 后续布局会继续使用这些记忆偏移, 即使这些元素后来被滚动。 (只记住滚动偏移; 实际布局位置每次都会重新计算,始终准确。) 只有定位元素停止显示后再次显示, 或切换回退时才会重新计算。
-
唯一例外是 默认锚点元素; 如果它被滚动离开 记忆滚动偏移, 定位元素会跟随其移动。 因为这只是位置的平移, 定位元素不会改变尺寸, 也无需因响应而布局。
最终结果是 锚点定位通常都能“正常工作”, 不管锚定对象是什么, 只是响应滚动的能力可能会有限。
当为某元素 确定定位回退样式时, 也会发生 锚点重算点; 如果因此切换回退样式, 则采用所选回退样式关联的 锚点重算点 的结果。
当元素 abspos 发生 锚点重算点 时, 对于 abspos 的每个 锚点引用所引用的元素 anchor, 都会关联一个 记忆滚动偏移, 等于当前 滚动偏移之和, 该偏移是 anchor 所有 滚动容器祖先的滚动偏移, 直到但不包括 abspos 的 包含块。 记忆滚动偏移也会考虑其他依赖滚动的定位变化, 如 position: sticky 等。 如果 abspos 有 默认锚点元素, 总是会为其计算 记忆滚动偏移, 即使 abspos 没有实际的 锚点引用。
上述机制允许定位元素对 锚点引用 的滚动位置响应一次, 但若其中任意一个被滚动, 定位元素就不再“锚定”到它们 (但会继续响应它们的非滚动移动)。 虽然这个问题一般无法彻底解决, 但可以对一个 锚点引用的滚动做出响应; 具体来说,是 默认锚点元素:
-
abspos 有 默认锚点盒子。
-
abspos 对 默认锚点盒子有 锚点引用, 或至少对同一滚动上下文中的对象有锚点引用, 即至少满足下列之一:
-
abspos 在该轴的 自身对齐属性值为 anchor-center;
-
abspos 的 position-area 值不为 none
-
至少一个 anchor() 函数在 abspos 的该轴 已用 inset 属性上, 引用了具有与 abspos 的 默认锚点盒子相同最近 滚动容器祖先的 目标锚点元素。
-
注意: 如果 abspos 有 定位选项列表, 是否在某轴上 补偿滚动也会受选中回退样式影响。
abspos 的 默认滚动位移是水平和垂直两个轴的一对长度。 每个长度计算如下:
在为 abspos 完成布局后, 还会额外应用 默认滚动位移, 效果类似于 transform (在其他变换之前)。
注意: 记忆滚动偏移 会影响 anchor() 函数的值, 默认滚动位移则是直接对元素进行偏移, 在确定 inset 属性、 应用对齐等之后。 这通常难以区分, 但如 round(anchor(outside), 50px) 这样对 默认锚点元素位置做非线性变换的场景, 就能体现不同效果。
4. 基于锚点的对齐
4.1. 区域特定默认对齐
当 position-area 不为 none 时, normal 自身对齐值的行为 会根据 <position-area> 的值发生变化, 以将盒子对齐到锚点:
-
如果某轴只选中了中心轨道, 则该轴默认对齐为 center。
-
如果三条轨道全部选中, 则该轴默认对齐为 anchor-center。
-
否则,该轴默认对齐到未指定的侧边轨道: 如果指定的是该轴的“start”轨道, 则默认对齐为 end;依此类推。
但如果相关轴上只有一个 inset 属性为 auto, 则默认对齐会朝向非 auto 的内边距所在的边; 此为 unsafe 对齐。
注意: 单个 auto 的行为保留了单个指定的内边距控制 绝对定位盒子位置的方式。
此行为增加了定位盒子可见性, 即使其 包含块 比预期更小时,仍能保持在预期范围内。
例如,position-area: bottom span-right 让定位盒子默认在其锚点左侧到包含块右侧之间拉伸并左对齐。 但如果定位盒子比该空间大(比如锚点非常靠近屏幕右侧), 它会向左偏移以保持可见。
4.2. 锚点居中:anchor-center 对齐值
| 名称: | justify-self, align-self, justify-items, align-items |
|---|---|
| 新增值: | anchor-center |
自身对齐属性允许 绝对定位盒子 在 inset 修正包含块内对齐自身。 常规对齐值加上精心设置的 inset 属性 通常已足够实用, 但锚点定位常见场景——在 锚点盒子上居中——实现起来需要复杂设置。
新增的 anchor-center 值 让这种场景极其简单: 如果定位盒子有 默认锚点盒子, 则会(尽可能)在相关轴上居中于 默认锚点盒子。
如果盒子不是 绝对定位, 或没有 默认锚点盒子, 则该值行为等同于 center, 且对 inset 属性的解析无其他影响。
注意: 使用 anchor-center 时,默认情况下 如果锚点离盒子的 原始包含块边缘太近, 会“偏移”以保持在 原始包含块内, 不再纯居中。 更多细节见 CSS Box Alignment 3 § 4.4 溢出对齐: safe/unsafe 关键字与滚动安全限制。
4.3. 条件居中:dialog 对齐值
| 名称: | justify-self, align-self, justify-items, align-items |
|---|---|
| 新增值: | dialog |
对话框通常在屏幕居中显示, 除非锚定到其他元素。
新增的 dialog 值 可以指定此行为: 若盒子为 绝对定位 且 position-area 不为 none, 则对齐方式同 normal (见 § 4.1 区域特定默认对齐); 否则, 对齐方式同 center。
5. 基于锚点的尺寸
绝对定位盒子 可以在其 尺寸属性中使用 anchor-size() 函数, 用于引用一个或多个 锚点盒子 的尺寸。 anchor-size() 函数会解析为 <length>。 它只允许用于 被接受的 @position-try 属性(其他地方无效)。
5.1. anchor-size() 函数
| 名称: | width, height, min-width, min-height, max-width, max-height, top, left, right, bottom, margin-top, margin-left, margin-right, margin-bottom |
|---|---|
| 新增值: | <anchor-size()> |
anchor-size() = anchor-size( [ <anchor-name> || <anchor-size> ]? , <length-percentage>? ) <anchor-size> = width | height | block | inline | self-block | self-inline
anchor-size() 函数与 anchor() 类似, 参数基本一致, 只不过 <anchor-side> 关键字换成了 <anchor-size>, 用于表示两个对边之间的距离。
物理 <anchor-size> 关键字 (width 和 height) 分别表示 目标锚点元素 的宽度和高度。 与 anchor() 不同,不要求轴必须匹配; 例如 width: anchor-size(--foo height); 是合法的。
逻辑 <anchor-size> 关键字 (block, inline, self-block, 和 self-inline) 会根据盒子的 书写模式 (对于 self-block 和 self-inline), 或盒子的 包含块的 书写模式 (对于 block 和 inline)来映射到物理关键字。
如果省略了 <anchor-size> 关键字, 则默认行为为自动匹配 anchor-size() 所用属性的轴的那个关键字。 (例如 width: anchor-size() 等价于 width: anchor-size(width)。)
代表 可解析 anchor-size 函数的 anchor-size() 会在 计算值阶段 (通过 样式与布局交错) 解析为 <length>, 即 目标锚点元素 的 锚点盒子相关边(左-右或上-下)的距离。
5.1.1. anchor-size() 的解析
只有同时满足以下所有条件时, anchor-size() 函数才是 可解析 anchor-size 函数:
-
它应用于 绝对定位盒子。
-
所应用的盒子,以及函数中指定的 <anchor-name> 值,都有对应的 目标锚点元素。
如果上述任一条件不满足, anchor-size() 函数会被解析为指定的回退值。 如果未指定回退值, 则会使引用它的声明在 计算值阶段无效。
6. 溢出管理
锚点定位, 虽然强大, 但也可能难以预料。 锚点盒子 可能在页面任何位置, 所以以某种方式定位盒子 (比如在锚点上方或右侧), 可能导致定位盒子溢出其 包含块 或部分显示在屏幕外。
为改善此情况,绝对定位盒子 可以使用 position-try-fallbacks 属性 引用多个定位/对齐属性的变体集合 (可从盒子的现有样式生成, 或在 @position-try 规则中指定), UA 可在盒子初始位置溢出时依次尝试这些方案。 每一组样式会逐一应用于盒子, 直到第一个不会导致盒子溢出其 包含块的方案胜出。
position-try-order 允许这些选项 按可用空间进行排序, 如果优先确保盒子拥有尽可能多的空间比严格按照某一声明顺序更重要,可以用此方式排序。
6.1. 提供回退选项:position-try-fallbacks 属性
| 名称: | position-try-fallbacks |
|---|---|
| 取值: | none | [ [<dashed-ident> || <try-tactic>] | <position-area> ]# |
| 初始值: | none |
| 适用对象: | 绝对定位盒子 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散型 |
该属性提供一组备用定位样式, 当绝对定位盒子 溢出其 inset 修正包含块时可以尝试。 此 定位选项列表 初始为空。
列表中每个逗号分隔项都是一个独立选项: 可以是 @position-try 块的名称, 或者是 <try-tactic>,代表自动变换盒子的现有计算样式。
取值含义如下:
- none
-
该属性无效果; 盒子的 定位选项列表为空。
- <dashed-ident>
-
如果存在同名的 @position-try 规则, 其对应的 定位选项 会加入 定位选项列表。
否则,该值无效果。
- <try-tactic>
-
根据盒子的计算样式自动生成 定位选项, 通过 try-tactic 变换 按指定关键字处理, 并将生成的 定位选项 加入盒子的 定位选项列表。 并加入 定位选项列表。
<try-tactic> = flip-block || flip-inline || flip-start
- flip-block
-
交换 块轴上的值 (如 margin-block-start 与 margin-block-end), 本质上是在 行轴线镜像。
- flip-inline
- flip-start
-
交换 start 属性彼此间的值, 以及 end 属性彼此间的值 (如 margin-block-start 与 margin-inline-start), 本质上是在从 start-start 角到 end-end 角的对角线上镜像。
如指定了多个关键字, 则按顺序复合变换, 生成单一 定位选项。
- <dashed-ident> || <try-tactic>
-
结合前两种效果: 若存在同名 @position-try 规则, 先将其 定位选项 应用于基础样式, 再按指定 <try-tactic> 变换, 并将结果加入盒子的 定位选项列表。
否则无操作。
- <position-area>
-
自动生成仅包含指定 position-area 属性的 定位选项。
-
如果 directions 在同一轴上是对立的,则为“相对”; 否则(指定不同轴时),为“垂直”。
-
确定 被接受的 @position-try 属性 在 el 上的指定值,令 styles 为结果。
-
在 styles 中替换变量、 env() 函数、 及类似的任意替换函数。
对于 env() 函数, 如引用的环境变量与方向或轴关联 (如 safe-area-inset-top), 则切换引用的 环境变量对应方向。
例如,如果指定了 top: env(safe-area-inset-top);, 且 directions 是 up 和 left, env() 会按 env(safe-area-inset-left) 解析。 (下一步会实际交换到 left 属性。) -
交换 styles 在与 directions 关联属性间的值。
注意: 如果方向为同轴对立, 某些属性(如 width 或 align-self) 不会交换, 因为它们在两个方向上都与自身关联, 但它们的值可能会被下步更改。
-
按如下方式修改属性值以匹配新方向:
-
对于 inset 属性, 更改 anchor() 函数中指定的边, 使其与新方向保持原有相对关系。
如使用了 <percentage>, 且 directions 为对立, 则改为 100% 减原百分比。
例如,“top”和“left”交换时, margin-top: anchor(bottom) 会变成 margin-left: anchor(right)。如果“top”和“bottom”交换, margin-top: anchor(20%) 会变成 margin-bottom: anchor(80%)。
-
对于 尺寸属性, 更改 anchor-size() 函数中指定的轴, 使其与新方向保持原有相对关系。
-
对于 自身对齐属性, 如 directions 为对立, 更改指定的 <self-position> (或 left/right 关键字),如有, 使其与新方向保持原有相对关系。
例如,“top”和“bottom”交换时, align-self: start 会变成 align-self: end。而 align-self: center 不变, 因为其在两个方向上关系一致。
类似地,align-self: first baseline 也不变, 因为它是 <baseline-position> 而非 <self-position>。
-
对于 position-area, 更改值以使 position-area 网格 的选中行/列与新方向保持原有相对关系。
-
-
返回 styles。
6.2. 确定回退顺序:position-try-order 属性
| 名称: | position-try-order |
|---|---|
| 取值: | normal | <try-size> |
| 初始值: | normal |
| 适用对象: | 绝对定位盒子 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散型 |
该属性指定 定位选项列表 尝试的顺序。
<try-size> = most-width | most-height | most-block-size | most-inline-size
- normal
-
按 定位选项 在 position-try-fallbacks 属性中指定的顺序尝试。
- most-width
- most-height
- most-block-size
- most-inline-size
- most-height
-
对 定位选项列表中的每个项, 应用该定位选项到盒子, 并找出由这些样式得到的 inset 修正包含块的尺寸, 按尺寸大小对 定位选项列表进行稳定排序, 最大的排在前面。
.anchor{ anchor-name : --foo; } .list{ position : fixed; position-anchor : --foo; position-area : block-end span-inline-end; align-self : start; position-try-fallbacks : --bottom-scrollable, flip-block, --top-scrollable; position-try-order : most-height; } @position-try --bottom-scrollable{ align-self : stretch; } @position-try --top-scrollable{ position-area : block-start span-inline-end; align-self : stretch; }
自动生成的 flip-block 选项和 --top-scrollable 选项总会找到相同的可用高度, 因为它们都从包含块顶部延伸到锚点顶部, 所以会保留它们的声明顺序。 这会让盒子首先尝试以自然高度与锚点对齐(使用 align-self: end,自动反转基础样式), 如果仍然溢出,则回退为填充空间并可滚动。
6.3. position-try 速记
| 名称: | position-try |
|---|---|
| 取值: | <'position-try-order'>? <'position-try-fallbacks'> |
| 初始值: | 见各属性 |
| 适用对象: | 见各属性 |
| 是否继承: | 见各属性 |
| 百分比: | 见各属性 |
| 计算值: | 见各属性 |
| 动画类型: | 见各属性 |
| 规范顺序: | 按语法 |
该速记同时设置 position-try-fallbacks 和 position-try-order。 如果省略 <'position-try-order'>, 则按属性的初始值设置。
6.4. @position-try 规则
@position-try 规则 定义一个带有名称的 定位选项, 指定一组或多组可通过 position-try-fallbacks 应用于盒子的定位属性。
@position-try 规则的语法为:
@position-try <dashed-ident> {
<declaration-list>
}
前导中的 <dashed-ident> 为规则名。 如果声明了多个同名 @position-try 规则, 它们会像 @keyframe 规则一样 级联。
@position-try 规则只接受以下 属性:
在 <declaration-list> 内对属性使用 !important 是无效的, 使用会导致该属性无效,但不会使整个 @property-try 规则无效。
所有 @position-try 内的属性会作为 定位回退原点 应用于盒子, 这是一个新的 级联原点, 位于 作者原点 和 动画原点之间。
类似 动画原点, 使用 revert 值 表现为属性属于 作者原点, 因此会回退到 用户原点。 (和 动画原点一样, revert-layer 未做特殊处理,按指定表现。)
注意: 被接受的 @position-try 属性 是只影响盒子自身尺寸和位置的最小属性集,不会改变内容或其他样式。 这样能大大简化定位回退的实现,并满足响应可用空间调整锚点定位盒子的核心需求。 由于这些规则会覆盖 作者原点的正常声明, 这也限制了 @position-try 声明与其他属性正常级联和继承的负面影响。 预期未来扩展 容器查询 可允许基于定位回退查询元素,实现此受限属性列表不允许的条件样式。
注意: 如果多个盒子使用不同锚点, 但希望采用相同的回退定位方案(只需相对各自锚点), 可在 anchor() 中省略 <anchor-name>, 在各盒子的 position-anchor 单独指定锚点。
注意: 最常见的回退定位方式 (让定位盒子通常在锚点一侧,需要时翻转到另一侧) 可直接用 position-try-fallbacks 的关键字实现, 无需用 @position-try。
6.5. 应用定位回退
当一个定位盒子 (已应用 默认滚动位移) 溢出了其 inset 修正包含块, 且有非空 定位选项列表, 它会 确定定位回退样式 以尝试找到避免溢出的选项。
这些修改后的样式通过 交错 应用于元素, 会影响 计算值 (可触发过渡等), 即使它们依赖于布局和 已用值。
重写本节,使其更清晰、准确。 见 Issue 12818、 Issue 12890。
-
令 current styles 为 abspos 当前的已用样式 (可能是先前回退的结果)。
-
-
令 adjusted styles 为 应用定位选项 option 到 abspos 的结果。
-
令 el rect 为 abspos 的 margin box 的尺寸和位置, cb rect 为 abspos 的 inset 修正包含块的尺寸和位置, 按 adjusted styles 布局时确定。
-
如果 cb rect 在任一轴上为负尺寸并被修正为零尺寸, 跳过。
注意: 这防止零尺寸的 el rect 被认为“在”负尺寸 cb rect 内而被选为成功选项。
-
如果 el rect 没有完全包含在 cb rect 内, 跳过。
-
返回 adjusted styles, 及为其假设计算的 记忆滚动偏移 集合。
-
断言:上一步未找到避免溢出的 定位选项。
-
返回 current styles。
注意: 子元素溢出 el 不影响此计算, 只考虑 el 自身的 margin box。
注意: 因为特意跳过当前正在生效的 定位选项, 其 记忆滚动偏移不会更新; 如果其他回退都无效且继续用当前样式, 所有 记忆滚动偏移保持不变。
在完整布局过程中, 一旦盒子确定其回退样式(或确定未使用回退), 后续盒子的布局不会改变这一决定。
换言之,布局不会“回退”。
ResizeObserver
事件时:
-
如果 el 有 last successful position option, 则在以下任一条件为真时移除其 last successful position option:
-
其任何 长属性的 position-try 计算值发生变化。
-
其任何 被接受的 @position-try 属性的计算值发生变化。
-
其引用的任意 @position-try 规则被添加、移除或更改。
然后,为 el 确定定位回退样式, 并将其 last successful position option 设置为当前使用的 被接受的 @position-try 属性及其值集合。
-
否则,如果盒子 el 是 绝对定位, 则将其 last successful position option 设置为其当前使用的 被接受的 @position-try 属性及其值集合。
注意: 此记录/移除的时机与 最后记忆尺寸 的处理完全一致。
实现可以选择对 定位选项列表的长度施加实现自定限制, 以限制可能产生的额外布局工作量。 此限制必须至少为五。
#myPopover{ position : fixed; top : anchor ( --button bottom); left : anchor ( --button left); position-try-fallbacks : flip-inline, flip-block, flip-block flip-inline; /* 气泡至少与按钮等宽 */ min-width:anchor-size ( --button width); /* 气泡至少等高于2个菜单项 */ min-height:6 em ; }
6.6. 条件隐藏:position-visibility 属性
| 名称: | position-visibility |
|---|---|
| 取值: | always | [ anchors-valid || anchors-visible || no-overflow ] |
| 初始值: | anchors-visible |
| 适用对象: | 绝对定位盒子 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 按指定 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散型 |
有些情况下显示一个绝对定位盒子可能没有意义。 该属性允许根据一些常见布局条件让这些盒子有条件地显示。
- always
-
该属性无效果。 (无论锚点或溢出状态如何,盒子始终显示。)
- anchors-valid
-
如果盒子的任一必需锚点引用 未解析为一个目标锚点元素, 则盒子的visibility 属性解析为 。
什么是必需锚点引用? 没有回退值的 anchor() 函数; 默认锚点有时也算? 这里需要更详细定义。
- anchors-visible
-
如果盒子有默认锚点盒子, 但该锚点盒子为不可见或被中间盒子裁剪, 则盒子的visibility 属性解析为 。
- no-overflow
-
如果盒子在应用inset 修正包含块和position-try后仍溢出, 则盒子的visibility 属性解析为 。
定义可见性检查的时机。[Issue #12732]
注意: 这意味着如果 abspos 在 DOM 中与锚点相邻, 即使默认锚点被滚动出屏幕,仍会可见,因为也被相同滚动容器裁剪。
确保此裁剪定义与 View Transitions 保持一致,后者也需要类似概念。
注意: 这保证了在“链式锚点”场景下, 如果第一个 abspos 因该属性(锚点被滚动出)被隐藏, 那么用它作为锚点的另一个 abspos 也会被隐藏, 不会也浮动到不合理位置。
7. 辅助功能影响
请记住锚点定位并不会自动建立定位盒子与锚点之间的语义关系, 因为其用途多样。 作者不能仅依赖定位带来的视觉联系来实现语义关联, 否则这些元素通常在 DOM 中没有有效关系, 使得在非视觉用户代理(如屏幕阅读器)中难以使用。
Web 平台上的许多现有和新特性都允许显式建立这种连接, 使非视觉用户代理也能受益。
例如,HTML 中的 Popover API 会自动将触发按钮与弹出元素关联, 包括自动调整 tab 顺序; 它还会将触发按钮设为弹出层的隐式锚点元素, 方便使用锚点定位。
更一般的情况,
ARIA 特性如
aria-details
或 aria-describedby 属性
可手动提供这种信息;
配合定位元素上的
role
属性,
非视觉用户代理可告知用户两个元素之间的关系并让其自动导航。
8. DOM 接口
8.1. CSSPositionTryRule 接口
CSSPositionTryRule
接口代表
@position-try 规则:
[Exposed =Window ]interface :CSSPositionTryRule CSSRule {readonly attribute CSSOMString name ; [SameObject ,PutForwards =cssText ]readonly attribute CSSPositionTryDescriptors style ; }; [Exposed =Window ]interface :CSSPositionTryDescriptors CSSStyleDeclaration {attribute CSSOMString ;margin attribute CSSOMString ;marginTop attribute CSSOMString ;marginRight attribute CSSOMString ;marginBottom attribute CSSOMString ;marginLeft attribute CSSOMString ;marginBlock attribute CSSOMString ;marginBlockStart attribute CSSOMString ;marginBlockEnd attribute CSSOMString ;marginInline attribute CSSOMString ;marginInlineStart attribute CSSOMString ;marginInlineEnd attribute CSSOMString ;margin-top attribute CSSOMString ;margin-right attribute CSSOMString ;margin-bottom attribute CSSOMString ;margin-left attribute CSSOMString ;margin-block attribute CSSOMString ;margin-block-start attribute CSSOMString ;margin-block-end attribute CSSOMString ;margin-inline attribute CSSOMString ;margin-inline-start attribute CSSOMString ;margin-inline-end attribute CSSOMString ;inset attribute CSSOMString ;insetBlock attribute CSSOMString ;insetBlockStart attribute CSSOMString ;insetBlockEnd attribute CSSOMString ;insetInline attribute CSSOMString ;insetInlineStart attribute CSSOMString ;insetInlineEnd attribute CSSOMString ;top attribute CSSOMString ;left attribute CSSOMString ;right attribute CSSOMString ;bottom attribute CSSOMString ;inset-block attribute CSSOMString ;inset-block-start attribute CSSOMString ;inset-block-end attribute CSSOMString ;inset-inline attribute CSSOMString ;inset-inline-start attribute CSSOMString ;inset-inline-end attribute CSSOMString ;width attribute CSSOMString ;minWidth attribute CSSOMString ;maxWidth attribute CSSOMString ;height attribute CSSOMString ;minHeight attribute CSSOMString ;maxHeight attribute CSSOMString ;blockSize attribute CSSOMString ;minBlockSize attribute CSSOMString ;maxBlockSize attribute CSSOMString ;inlineSize attribute CSSOMString ;minInlineSize attribute CSSOMString ;maxInlineSize attribute CSSOMString ;min-width attribute CSSOMString ;max-width attribute CSSOMString ;min-height attribute CSSOMString ;max-height attribute CSSOMString ;block-size attribute CSSOMString ;min-block-size attribute CSSOMString ;max-block-size attribute CSSOMString ;inline-size attribute CSSOMString ;min-inline-size attribute CSSOMString ;max-inline-size attribute CSSOMString ;placeSelf attribute CSSOMString ;alignSelf attribute CSSOMString ;justifySelf attribute CSSOMString ;place-self attribute CSSOMString ;align-self attribute CSSOMString ;justify-self attribute CSSOMString ;positionAnchor attribute CSSOMString ;position-anchor attribute CSSOMString ;positionArea attribute CSSOMString ; };position-area
它的 name 属性
表示该规则前导部分声明的名称。
它的 style 属性
表示规则体中声明的属性,按指定顺序排列。
获取时,必须返回该 @position-try 规则的 CSSPositionTryDescriptors
对象,
其属性如下:
- computed flag
-
未设置
- readonly flag
-
未设置
- declarations
-
规则中声明的描述符,按指定顺序排列。
- parent CSS rule
-
上下文对象
- owner node
-
Null
9. 附录:样式与布局交错
样式与布局交错 是一种技术, 在布局过程中允许对子树进行样式更新, 从而可对元素的 计算样式进行追溯性更新。
此概念并非本规范的正确定义, 可能应移至 Cascade, 但此处需要一个可引用的草稿。
注意: 样式与布局交错 已用于 容器查询 和 容器查询长度。 如 10cqw 这类长度,会根据查询容器的尺寸布局信息解析为 计算长度, 因此当容器在布局间改变尺寸时可触发 过渡。
被接受的 @position-try 属性 在解析回退时也会 交错 (见 position-try)。
显然还需补充更多细节, 但目前“像容器查询一样处理”即可。 该行为本身也未定义, 但至少在一定程度上已实现互操作。
10. 安全性考虑
本文件尚未发现安全性问题。
11. 隐私性考虑
本文件尚未发现隐私问题。
12. 变更记录
自 2025年5月9日工作草案以来的主要变动:
-
将 x/y-self-start/end 关键字重命名为 self-x/y-start/end 用于 position-area 以解决排序混淆问题。 (Issue 12749)
-
使 position-area 在盒子的 包含块为 滚动容器时使用(新定义的)可滚动包含块。 (Issue 10861)
-
单轴为 auto 的 inset 让 position-area 默认对齐到非 auto 边。 (Issue 12512)
-
使变换影响 锚点盒子的几何形状。 (Issue 8584)
-
为 align-self 和 justify-self 增加 dialog 值,以解决 HTML popover UA 默认样式规则问题。 (Issue 10258)
-
澄清分片如何影响 锚点盒子几何形状。 (Issue 12287)
-
澄清何时清除 last successful position option。 (Issue 12577)
-
澄清锚点名称 tree-scoped 的匹配规则, 用于 anchor-scope 与 position-anchor。 (Issues 7916, 9408, 和 5984)
-
澄清顶层如何与查找 可接受锚点元素 交互, 并重写有效锚点规则以更易理解。 (Issue 11602)
-
修正 position-try-fallbacks 的语法, 排除 none 关键字列表。 (Issue 12838)
-
若干小修正与澄清。 (Issue 11028, Issue 11080, Issue 12058, Issue 12636, Issue 12653, Issue 12732)
另见 历史变更。