修复: 回到顶部按钮和进度条不显示的bug

This commit is contained in:
2026-09-21 09:47:35 +08:00
parent 219cb712a6
commit ec80ea7bd0
7 changed files with 185 additions and 10 deletions
+96
View File
@@ -0,0 +1,96 @@
/* 构建产物 CSS 自检:拦住「滚动驱动动画被压缩器并进 animation 简写」这一类静默回归。
*
* 背景(2026-09-21 的实际事故):
* 线上「回到顶部」按钮不显示,本地 dev 却正常。
* 原因是 Lightning CSS(Tailwind v4 的 CSS 压缩器)把
* animation: back-to-top-reveal linear both;
* animation-timeline: scroll(root block);
* 合并成了
* animation: linear both back-to-top-reveal scroll(root);
* 而 WebKit 不接受 animation 简写里的 <single-animation-timeline> 分量
* (webkit#293276),于是**整条 animation 声明被判无效丢弃**,元素永远停在
* 初始的 opacity:0 / visibility:hidden。
*
* 最要命的是它只在构建产物里出现:astro dev 不做这个压缩,所以本地怎么试都正常;
* 而 CSS.supports("animation-timeline: scroll()") 又照样返回 true,
* 运行时特性检测也发现不了。见 lightningcss#815。
*
* 这个脚本在 build 之后跑,扫 dist 下所有 CSS:只要 animation 简写的值里出现
* scroll() / view() 时间线函数,就让构建失败。
*
* 用法:
* node scripts/verify-build-css.mjs # 失败时退出码 1
* node scripts/verify-build-css.mjs --warn # 只警告,不失败
*/
import fs from "node:fs"
import path from "node:path"
const DIST = "./dist"
const WARN_ONLY = process.argv.includes("--warn")
/** animation 简写里不该出现的时间线函数 */
const TIMELINE_FN = /(?:^|[^-\w])(?:scroll|view)\(/
/** 收集 dist 下所有 css 文件 */
function collectCssFiles(dir) {
if (!fs.existsSync(dir)) return []
const found = []
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name)
if (entry.isDirectory()) found.push(...collectCssFiles(full))
else if (entry.name.endsWith(".css")) found.push(full)
}
return found
}
/**
* 只检查 `animation:` 简写(以及 -webkit-animation:),不碰
* `animation-timeline:` / `animation-range:` 这两个长写属性 —— 后者是正确写法。
*/
function findShorthandViolations(css) {
const hits = []
// 负向回顾避免匹配到 animation-timeline / animation-name 这类长写属性
const re = /(?:^|[;{}(])\s*(?:-webkit-)?animation\s*:\s*([^;}]*)/g
let match
while ((match = re.exec(css)) !== null) {
const value = match[1]
if (TIMELINE_FN.test(value)) {
hits.push(value.trim().slice(0, 120))
}
}
return hits
}
const files = collectCssFiles(DIST)
if (files.length === 0) {
console.error(`❌ ${DIST} 下没有找到 CSS,构建产物异常`)
process.exit(1)
}
const problems = []
for (const file of files) {
const css = fs.readFileSync(file, "utf-8")
for (const value of findShorthandViolations(css)) {
problems.push({ file, value })
}
}
if (problems.length === 0) {
console.log(`✅ 构建产物 CSS 自检通过(${files.length} 个文件,没有时间线函数混进 animation 简写)`)
process.exit(0)
}
console.error("❌ 构建产物 CSS 自检失败:animation 简写里混进了时间线函数。")
console.error("")
for (const { file, value } of problems) {
console.error(` ${file}`)
console.error(` animation: ${value}`)
}
console.error("")
console.error(" 这类声明会被 WebKit 整条丢弃(webkit#293276),元素会停在初始样式上。")
console.error(" 修法:把 animation-timeline / animation-range 挪到**独立的规则块**里,")
console.error(" 不要和 animation 简写放在同一组声明中(lightningcss#815)。")
console.error("")
process.exit(WARN_ONLY ? 0 : 1)