CSS Anchor Positioning:布局与浮层边界
使用 anchor-name、position-anchor 和 position-try 放置浮层,并测试 fallback 与滚动容器边界,避免把布局误当成 popover 行为。
BotBrowser Team
BotBrowser 可以在声明的 profile 中重复授权的 CSS anchor positioning fixture,但无法让不支持的引擎实现 anchor-name、position-anchor 或 position-try。实际需要回答的问题是:浮层是否保持附着在 anchor 上、是否选择声明的 fallback,以及祖先元素裁剪或滚动时它是否仍然可读。这是布局和 overflow 契约,与 Popover API 焦点和无障碍中的焦点与关闭行为不同。
TL;DR
- 给目标设置稳定的
anchor-name,再用position-anchor把定位元素连接到它。 - 把备选位置写入
position-try;首选位置溢出 containing block 时,浏览器会评估它们。 - 把
position-try-order和position-try-fallbacks当作有限列表,而不是所有窗口都能容纳的承诺。 - 分别测试滚动容器和 overflow clip;两者的可用区域可能不同。
- 记录计算样式、选中的 fallback 和每个边界的截图;属性存在不等于浮层可读。
内容
Anchor 模型
Anchor positioning 把两个盒子关联起来。anchor 在布局树中发布名称,定位 subject 引用它。anchor() 可以读取 anchor 的边或尺寸,position-area 则可以选择周围的逻辑区域。CSSWG 规范定义了模型和 fallback 算法,但兼容性仍取决于浏览器实现,因此应保留普通定位的 CSS fallback。
| 声明 | 作用 | 检查点 |
|---|---|---|
anchor-name: --trigger | 发布命名 anchor | 名称位于会移动或改变尺寸的元素上 |
position-anchor: --trigger | 选择 subject 的 anchor | subject 能解析该自定义标识符 |
position-try: --below, --above | 列出命名备选 | 浏览器能够选择可行选项 |
只设置 position-anchor 的 class,如果匹配的 anchor-name 位于 containing block 外的兄弟元素上,就不会生效。名称也不会创建 stacking context 或焦点关系。需要 visual viewport 测量时,可结合屏幕与 viewport 属性文章的边界说明。
最小浮层 fixture
先写普通的定位 fallback。即使功能缺失按钮也能使用,固定宽度还能避免长文本在检查期间改变测量结果。
<div class="toolbar">
<button id="help" class="trigger">Help</button>
<div class="tip" role="tooltip">Keyboard shortcuts</div>
</div>
.toolbar {
position: relative;
}
.trigger {
anchor-name: --help-trigger;
}
.tip {
position: absolute;
inset-block-start: calc(100% + 8px);
inset-inline-start: 0;
inline-size: 14rem;
max-inline-size: min(14rem, 90vw);
position-anchor: --help-trigger;
position-try: --above, --inline-end;
}
@position-try --above {
inset-block-end: anchor(top);
inset-block-start: auto;
}
@position-try --inline-end {
inset-block-start: anchor(top);
inset-inline-start: anchor(right);
inset-inline-end: auto;
}
Fallback inset 是普通 CSS,因此旧引擎仍会把 tooltip 放在 toolbar 下方。在支持的引擎中,position-anchor 会让相对 anchor 的值生效。@position-try block 是 user agent 按顺序检查的候选,不是 JavaScript callback。可查看 MDN 的 anchor-name 参考确认语法和兼容性表。
使用固定 inline-size、可见 outline 和短标签的本地 fixture。字体加载完成后,记录 getComputedStyle(tip).positionAnchor 以及两个盒子的 getBoundingClientRect()。
Fallback 是选择算法
普通声明给出首选位置。浏览器检查矩形是否适合带 inset 的 containing block;不适合时,再评估 position-try 或 position-try-fallbacks 中的备选。候选可以改变 inset、对齐或尺寸,但不应改变组件含义。
让每个候选只承担一个变化:--above 只向上移动,--inline-end 只移向侧面。这样截图能说明触发了哪个边界。Chrome 的 Anchor Positioning API 文章展示了“先试这个,再试那个”的模型以及 position-try-order。
顺序是可观测的。使用 position-try-order: most-width 时,即使 --above 写在前面,侧向位置也可能因为可用宽度更大而获胜。如果产品需要固定阅读顺序,应明确声明并测试。将 viewport、矩形和选中的 fallback 保存在精简 JSON 中:
{
"viewport": "390x844",
"anchor": { "top": 734, "bottom": 770, "left": 16, "right": 104 },
"subject": { "top": 610, "bottom": 704, "left": 16, "right": 240 },
"selected": "--above",
"overflow": { "blockEnd": 0, "inlineEnd": 0 }
}
这些数字只是可审查结果的示例,不是通用阈值。对 --above 保持 subject.bottom <= anchor.top 这样的关系;computed style 中存在属性,也不能证明 subject 没有被裁剪或遮住 trigger。
Overflow 与滚动容器
滚动容器和 overflow clip 的问题不同:前者会随滚动改变可见像素,后者限制 padding box 外的绘制。用两个 wrapper 分开测试:
.scroll-frame {
block-size: 12rem;
overflow: auto;
border: 1px solid currentColor;
}
.clip-frame {
overflow: clip;
contain: paint;
}
把 anchor 放在 .scroll-frame 底部附近,滚动到边缘后打开浮层。记录 subject 是否跟随 anchor,以及 frame 是否裁剪它。在 .clip-frame 中重复。矩形即使适合 viewport,也可能被祖先裁剪而不可见,所以截图必须和几何日志一起保存。
变换、contain: layout 和嵌套的定位祖先会增加 containing block。一次只改变一个祖先;如果 subject 跳到 viewport,先找出建立 containing block 的元素,再调整 offset。更大的 z-index 不能越过 clip。
可复现的边界矩阵
每行使用新的 profile,保持浏览器 build、zoom、字体加载状态和 device scale factor 不变。
| 行 | 变化 | 预期观察 | 含义 |
|---|---|---|---|
| A | anchor 居中且 viewport 宽裕 | 首选位置保持可见 | 基础关系 |
| B | anchor 靠近 block-end | 选择 --above 或其他候选 | fallback 评估 |
| C | anchor 位于滚动 wrapper | 滚动后 subject 仍跟随 | 滚动包含关系 |
| D | 祖先使用 overflow: clip | 矩形存在,但像素可能被裁剪 | 绘制边界 |
| E | 引擎不支持该功能 | 出现普通 absolute fallback | 兼容路径 |
保存浏览器版本、viewport、缩放、滚动偏移、计算出的 positionAnchor 和两个矩形。报告应写“D 行在 .clip-frame 被裁剪”,而不是泛称“anchor positioning 失效”。E 行不同也要保留可读 fallback,并链接兼容性表。
BotBrowser 能力边界
BotBrowser 能够为授权 fixture 保持 profile、viewport、zoom 和启动策略一致,重复矩阵并保存去敏的几何数据和截图。BotBrowser 不能添加缺失的 CSS 功能、穿过祖先的 clip,也不保证所有版本选择同一 fallback。结果只描述一个具体引擎、build、profile 和 viewport。
不要把 anchor 位置当作身份评分。窗口尺寸、zoom、字体或无障碍设置变化,都可能选择不同候选。需要更广泛的兼容性方法时,参考浏览器功能置信度的 Web 平台测试。
FAQ
position-anchor 能替代 JavaScript 定位吗?
对于 CSS 可以表达的关系,它能替代反复测量并写入样式的循环。状态、焦点或自定义碰撞策略仍可能需要 JavaScript。对不支持的引擎保留 fallback。
为什么 fallback 适合 viewport 却不可见?
祖先可能通过 overflow: clip、contain: paint 或 scroll viewport 建立 clipping padding box。矩形有效,但像素不能越过祖先。先检查祖先链,再修改 z-index。
fallback 能保证浮层永远不覆盖 anchor 吗?
不能。选择受 containing block 和声明的候选限制。小 viewport、大字号或较长译文都可能让所有候选无法完整容纳。请提供可读的最大尺寸和明确关闭路径。
结论
Anchor positioning 是 CSS 关系加上有限 fallback 列表。给移动元素命名,用 position-anchor 引用它,用 position-try 声明小范围候选,并测试边缘、滚动容器和 overflow clip。把矩形与截图一起保存,区分 fallback 选择和绘制裁剪。BotBrowser 能重复这种受控观察,但不能把浏览器限制变成保证。
来源
本文属于指南: Web 平台能力面:一致性复核地图