排印即界面:​这个​博客​的​设计​系统

一个以阅读体验为最高优先级的博客,它的每个设计决策都应该可以被追问「为什么」。这篇文章记录全部答案,同时也是排印系统的全要素测试页。

一个只有文字的网站,排印就是它的全部界面。这篇文章记录这个博客每个设计决策背后的取舍,同时它本身也是排印系统的测试页——你现在看到的每一种元素,都是被刻意设计过的。

字体系统

中英混排是这个博客最基础的约束。技术写作绕不开 Transformercontext window 这类英文术语,所以字体系统必须让两种文字在同一行里和平共处。

方案是三层分工:

  • 标题:衬线字体。拉丁字符用 Newsreader(带光学尺寸轴,大字号下笔画对比更优雅),中文回退到思源宋体
  • 正文:无衬线。屏幕上的中文长文,无衬线的可读性依然更稳
  • 代码:JetBrains Mono,0O1l 永不混淆

正文行高设为 1.95。西文排版的经验值(1.5 上下)对中文来说太挤——汉字是方块字,没有西文的升降部留白,需要更大的行距来制造呼吸感。行宽约束在 40rem,折算成中文大约每行 35 个字,接近传统书籍排版的舒适区间。

色彩:纸、墨与克莱因蓝

底色不是纯白,而是带一丝暖调的纸色 #FAFAF7;文字也不是纯黑,而是 #191A1D 的冷墨色。纯黑配纯白的对比度过高,长时间阅读会累1

全站只有一个强调色:克莱因蓝。奶油纸底配钴蓝是编辑设计的经典组合,暗色模式下它会亮化成电光蓝 #6C86FF。它出现的地方极少——章节编号、悬停状态、目录高亮、站点印章——正因为少,每次出现才有意义。

高级感的来源不是加法,而是减法之后剩下的东西都经得起追问。

动效原则

动效的职责是确认操作、引导视线,而不是表演。这个博客的动效语言只有四条规则:

  1. 所有缓动使用自定义贝塞尔曲线,出场 cubic-bezier(0.22, 1, 0.36, 1)
  2. 时长分三档:160ms(悬停反馈)、320ms(状态切换)、640ms(入场)
  3. 页面跳转用 View Transitions 做标题的共享元素变形,让「点击列表 → 进入文章」有物理连续性
  4. 尊重 prefers-reduced-motion,没有例外

主题切换用了一个稍微炫技的细节:以按钮为圆心的圆形揭示。实现只需要 View Transitions API 加十行代码:

const transition = document.startViewTransition(applyTheme);

transition.ready.then(() => {
  document.documentElement.animate(
    {
      clipPath: [
        `circle(0px at ${x}px ${y}px)`,
        `circle(${radius}px at ${x}px ${y}px)`,
      ],
    },
    {
      duration: 480,
      easing: 'cubic-bezier(0.65, 0, 0.35, 1)',
      pseudoElement: '::view-transition-new(root)',
    }
  );
});

技术选型

决策选择理由
框架Astro 5默认零 JS,内容站的性能上限
写作Markdown发布一篇文章 = 提交一个文件
代码高亮Shiki 双主题构建期完成,运行时零开销
部署纯静态任何 CDN 都能托管

整个站点打包后,除了字体之外的 JavaScript 不到 10KB——阅读进度条、目录高亮、主题切换、代码复制,全部算上。


性能数字不是虚荣指标。首屏在 200ms 内渲染完成,这件事本身就是阅读体验的一部分:读者点开链接的那一瞬间,文字已经在那里等着了。

脚注

  1. WCAG 对正文对比度的要求是 4.5:1 起,但上限同样值得注意——过高的对比度在暗色模式下尤其刺眼,这也是暗色主题的文字用 #E6E7EA 而非纯白的原因。