问题

Quartz 站点部署后,macOS 暗色模式下页面几乎看不清内容——背景接近白色,文字对比度极差。

排查过程

Round 1:颜色语义搞反了

Quartz 的颜色变量命名和直觉相反

变量实际含义容易误解为
light页面背景色浅色文字
dark标题文字色深色背景
darkgray正文文字色深灰背景
gray侧边栏/次要文字
secondary链接/标签色
tertiary悬停色

初始配置把 darkMode.light 设成了 #e2e8f0(浅灰色),结果暗色模式的背景是浅灰色。

修复light: "#0a0e1a"(深蓝黑)。

Round 2:对比度不足

背景修正后,文字颜色对比度仍然不够:

变量修改前修改后原因
gray#64748b#94a3b8侧边栏文字太暗
darkgray#cbd5e1#e2e8f0正文需要更亮
dark#f1f5f9#f8fafc标题接近纯白
secondary#7c3aed#a78bfa链接对比度 3.8:1 不达标
tertiary#3b82f6#60a5fa悬停色提亮

Round 3:CSS 媒体查询兜底

添加了 @media (prefers-color-scheme: dark) 作为 JS 检测的 CSS 级兜底,防止 prescript.js 加载前的白屏闪烁。

Round 4:浏览器缓存(真正的元凶)

前三轮修复都已部署到服务器,但用户刷新页面后仍然看到白色背景

排查发现 nginx 配置:

location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

immutable 标记告诉浏览器:这个文件永远不会变,不需要重新验证。浏览器缓存了旧的 index.css(不含暗色模式修复),30 天内不会请求新版本。

immutable 缓存的陷阱

immutable 适用于文件名包含 hash 的资源(如 app.3a2b1c.css),因为内容变了文件名也变。但 Quartz 生成的 index.css 文件名不变,用 immutable 会导致更新无法送达用户。

修复:区分缓存策略

# CSS/JS: 允许缓存但每次验证(ETag/Last-Modified)
location ~* \.(js|css)$ {
    add_header Cache-Control "no-cache";
}
 
# 图片/字体: 长缓存(内容稳定)
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|webp)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

no-cache 不是”不缓存”,而是”缓存但每次请求前用 ETag 验证”。如果文件没变,服务器返回 304(无需重新下载);如果变了,返回新文件。兼顾了性能和及时更新。

Quartz 暗色模式工作原理

prescript.js(同步阻塞脚本)
  ↓
window.matchMedia("(prefers-color-scheme: light)")
  ↓ 检测系统偏好
localStorage.getItem("theme") ?? 系统偏好
  ↓ localStorage 优先
document.documentElement.setAttribute("saved-theme", "dark")
  ↓ 设置 HTML 属性
CSS: :root[saved-theme="dark"] { --light: #0a0e1a; ... }
  ↓ CSS 变量覆盖
页面渲染为暗色

教训

  1. 读懂颜色语义light 不是”浅色文字”,是”背景色”。部署前先查 Quartz 文档
  2. WCAG 对比度:暗色背景上文字至少 4.5:1(AA 标准),用 WebAIM Contrast Checker 验证
  3. immutable 慎用:只用于文件名含 hash 的资源。其他文件用 no-cache(每次验证)或短 max-age
  4. 修复后看不到效果?先查缓存curl -I 检查 Cache-Control 头,比调代码有效得多