CSS / Tailwind v4 / 动画布局实证陷阱
沉淀自 Yggdrasil 会话记忆(2026-08~10 实证)。以当前
input.css、public/style.css与浏览器实际行为为准。
1. animation fill:both 残留 identity transform,会改变 fixed 的 containing block
animation: ... both 的 forwards fill 让合成 transform 以 matrix(1,0,0,1,0,0) 永久残留——即使关键帧终态写 transform: none,getComputedStyle 仍报 matrix,该元素成为所有 position: fixed 后代的 containing block。
症状:fixed 元素 top: 50% 相对祖先(如 article)居中而非视口,出现在页面中部。
修法:终态等于元素自然值时用 backwards 而非 both(.animate-page-enter 已改)。放进文章容器内的 fixed 元素都受益。
2. overflow-y:auto 会让 overflow-x:visible 计算成 auto
CSS 规范行为。经典滚动条环境(macOS 外接鼠标 /「始终显示滚动条」)下纵向滚动条吃约 8px,窄容器(如 32px 收起条)内容横向溢出,底部冒出横向滚动条色块。
修法:显式 overflow-x: hidden。scrollbar-width: none 只藏滚动条、不禁滚动(程序化 scrollTo 不受影响),适合「收起 none / 展开 thin」双态。
验证坑:headless chromium 默认 overlay 滚动条(clientWidth 不减),复现需注入 ::-webkit-scrollbar{width:8px}。
3. Tailwind v4 的 translate utility 走独立 translate 属性
v3 里 translate-x/y 写 transform,v4 写独立 translate 属性——与 @keyframes 的 transform 是叠加而非覆盖 → 双倍位移。
解法:静态定位用任意属性类 [transform:translateX(-50%)](直接写 transform 属性),与动画 fill 值完全相等。
4. 动画 fill 期间 transform 覆盖静态 transform
结束瞬间位置跳变。静态变换值必须烘进关键帧首尾(popover-enter 先例:首尾都带 translateX(-50%))。
5. animation-timeline: scroll() 在路由替换正文那一帧短暂失效
currentTime 为 null(即使新页面可滚动也失效 1 帧),动画脱落、露出基础样式。滚动驱动样式的基础值必须是「未滚动」态,关键帧写 to 终态。
6. 构建后的 CSS 自定义属性时间值可能被压缩成秒
实测 --spring-snappy-duration 读出来是 ".41s"。JS 读取必须按单位换算,parseFloat 直接当 ms 会得到 0.41ms。
7. flex 局部滚动:逐层 flex-1 min-h-0,别用百分比高度链
h-full 百分比链在 flex 容器里是循环依赖:长内容先把父撑大(实证 1034 → 1342),子孙 height:100% 再相对撑大后的父解析,约束完全失效(短内容时凑巧等于有界高度,极具迷惑性)。
正确:容器 min-h-0 + 每层 flex-1 min-h-0,固定区 flex-shrink-0,滚动区 overflow-y-auto,零百分比。需要圆角裁切时把圆角加在实际 scroll 容器上。
诊断:沿 parentElement 逐层取 rectH / clientH / scrollH / overflowY / minHeight / flex,谁 scrollH > clientH 谁就是真实滚动者。
8. mermaid 11 的 htmlLabels:true 多行节点裁切
foreignObject 高度 = 渲染前测量值(零余量:2 行=48、3 行=72),SVG 规范默认 overflow:hidden;CJK 字体行盒偏高,跨浏览器 sub-pixel 差异让实际行高略大 → 多行/副标题节点的 descender 被裁。
真根因不是 CSS:继承链无额外 line-height,且 CSS foreignObject{overflow:visible} 对 Chrome 无效。解法:htmlLabels:false(走 SVG text+tspan,度量自洽、不继承宿主 CSS,dagre 按实际尺寸摆位)。曾把它诊断成 htmlLabels 之外的原因再撤回,别再走弯路。
9. mermaid 11 render 失败会泄漏临时容器
mermaid.render(id, text) 在 body 建 div#d{id} 做度量:成功时移除,失败时既不清理也不返回(直接 throw) → 残留含「Syntax error in text」的 SVG,污染页面底部。
修法:调用方 try/catch,失败先 document.getElementById('d' + id)?.remove() 再 rethrow。
另:flowchart 里未加引号的 () 会被当圆角节点形状定界符 → 含括号的标签必须双引号(Node["text (parens)"]、subgraph id ["label"]);stateDiagram-v2 / sequenceDiagram 不受此限制。
10. 滚动/定位验证技巧
- 自动滚动测试受全局 smooth 影响时用
behavior: 'instant'。 - 截图环境中 page-enter 动画可能卡在 currentTime 0 导致空白:注入
*{animation:none!important}+getAnimations().forEach(a => a.cancel())再截。 - 动画未完成时量位置不可靠:轮询
getAnimations()的 playState/currentTime 后再量。