← 首页 / ⚓ CSS Anchor Positioning · 把元素「钉」在另一个元素旁 待审核 7 页

⚓ CSS Anchor Positioning · 把元素「钉」在另一个元素旁

tooltip、下拉菜单、popover、气泡提示——本质都是「让 B 元素贴着 A 元素的某条边出现」。长期以来这件事并不容易:要么把 B 放进 A 的 position:relative 容器里(于是被 overflow:hidden 裁掉、被 z-index 压住),要么用 JS 监听滚动 / resize 反复 getBoundingClientRect() 手算坐标。CSS Anchor Positioning 把它变成纯 CSS:给 A 起个 anchor-name,让 B 用 anchor() / position-area 引用它—— B 不必再是 A 的子孙、能待在 top layer(popover / dialog),也能精确对齐并自带溢出翻转。 (一条常被忽略的约束:为跨浏览器稳妥,应锚在前、包含块能包住锚——见 「细节与边界」页。) 阅读本系列需要先了解 position: absolute、包含块 (containing block) 与 stacking 的基础, 可参考 how-css-worksstacking-contexts 两个系列。

一句话路线图: 给锚元素 anchor-name: --x → 给浮层 position: absolute; position-anchor: --x → 用 anchor()(精确到边)或 position-area(九宫格,更简洁)摆位 → 用 position-try-fallbacks 处理「接近视口边缘时自动翻到另一边」。 why · 心智模型

为什么要它:两个角色 + 跨包含块的定位

先看传统方案的局限(relative 容器被裁剪 / JS 手算坐标会抖动)。再认识 anchor positioning 的两个角色:锚元素(anchor-name)与浮层position-anchor + 绝对定位)。动手拖动锚,观察浮层跨越 DOM 层级、无视 overflow地跟随——这是它和 position:relative 的本质区别。

anchor() · 精确到边

anchor() 与 anchor-size():贴边对齐 & 跟随尺寸

底层主力函数。anchor(top/bottom/left/right/start/end/center/%) 用在 top/left/inset-* 里,把浮层的某条边钉到锚的某条边;anchor-size(width/height/...) 让浮层尺寸跟着锚走(下拉框宽度=输入框宽度的经典需求)。拖滑块把浮层贴到锚的任意边角,实时看生成的 CSS。

position-area · 九宫格

position-area:一行实现的九宫格摆位

高层简化 API:把锚周围想象成 3×3 网格,position-area: top center 就把浮层放到正上方,无需触碰 top/left。点九宫格任意格子观察浮层移动;再尝试 span-* 跨格、逻辑关键字(block-start/inline-end)、以及 justify-self: anchor-center 对齐。

position-try · 自动翻转

溢出回退:position-try-fallbacks / @position-try

浮层接近视口边缘、首选位置放不下时,position-try-fallbacks 提供一串候选位置:内置 flip-block / flip-inline / flip-start 翻转,或用 @position-try 自定义一整套位置。再配 position-try-order 选空间最大的候选、position-visibility 在锚滚出视野时自动隐藏。把锚拖到取景框边缘,观察浮层自动翻面

recipes · 实战 & 支持度

实战配方:Popover 组合、等宽下拉、anchor-scope

anchor() / position-area / position-try 组合成实用案例:Popover API + anchor(top-layer 浮层 + 纯 CSS 定位,无 JS 跟随)、等宽下拉(anchor-size)、一个锚挂多个浮层、列表里同名锚用 anchor-scope 隔离。最后讲渐进增强——用 @supports 为不支持的浏览器提供降级,以及当前 2026 的支持现状。

connections · 串线

进阶用法:用 anchor() 在两个元素之间画连接线

其他各页的浮层都只锚定一个锚。这页换一种思路:让同一个元素的四条边分别 anchor() 到两个不同的锚,把它撑成「跨越 A 与 B」的盒子,只画左+下两条边 → 一根评论↔回复的 thread 连接线(灵感来自 Roland Franke 的 frontend story)。纯 CSS、随内容自适应,再用 anchor-scope 让一长列 thread 各连各的。

gotchas · 细节与边界

细节与边界:规范角落里的 fine print

主线能力之外,真正上手才会遇到的细节:一个元素挂多个 anchor-name、隐式 vs 显式绑定;实测锚被 display:none / visibility:hidden / content-visibility:hidden 时浮层各自的表现(与「常见说法」不一样);insetposition-area 并存时谁优先;anchor-size() 的默认尺寸与适用范围;以及 position-area 全取值速查(span-* 的反直觉对齐)。

为什么这是结构性的新能力而不是语法糖?它解决了 CSS 二十年的老结构矛盾: 「想让浮层跟着某元素」与「想让浮层在 top layer / 不被祖先裁剪」从前是互斥的—— 要跟随就得当子元素(受 overflow / clip / transform / stacking 牵连),要自由就得用 JS 手算。 Anchor positioning 让视觉对齐彻底脱离 DOM 包含关系,这正是 tooltip / menu / popover 一直缺的那块拼图。

🔗 相关链接

📐 规范 / 文档

🎨 实例与用法(本系列灵感来源)

  • Custom range slider:anchor positioning + 滚动驱动动画 Frontend Masters 把滑块 thumb 起名 anchor-name: --thumb,让 <output> 气泡 position-area: top 跟着跑;再用 scroll-driven 的 view-timeline 读出实时值、anchor-scope: all 隔离多个滑块。和我们的 css-animation 系列正好串起来。
  • Anchor Interpolated Morphing (AIM) Adam Argyle 进阶用法:把 anchor 与 @starting-style + interpolate-size + transition(allow-discrete)叠加,让 popover / 对话框从触发点"生长"出来(纯 CSS、可打断,无需 View Transitions)。同样能接上 css-animation 系列。