← All projects

Shiki 双主题高亮方案

一份 CSS 变量就能让代码块跟随系统主题切换,不需要重新渲染,也不需要用 JavaScript 改写 DOM。

  • shiki
  • frontend

代码高亮最常见的做法是「亮色一套主题、暗色一套主题,切换时重新高亮」——既要多跑一遍解析,切主题时还会有明显的闪烁。

Shiki 提供了更干净的路子:开启 defaultColor: false 后,每个 token 不再写死颜色,而是输出两个 CSS 变量。

<span style="--shiki-light:#24292e;--shiki-dark:#e1e4e8">const</span>

于是主题切换退化成一次 CSS 变量取值的变化:

.shiki span {
	color: var(--shiki-light);
}

.dark .shiki span {
	color: var(--shiki-dark);
}

配合 app.html 里那段「首屏前就给 <html> 加上 dark 类」的内联脚本,切换零闪烁、零 JavaScript、零重新解析。

为什么值得这么做

因为这个站点是全量预渲染的:代码高亮在构建期就固化成 HTML,运行时既没有 Shiki 也没有 marked。如果改用「切主题时重新高亮」,就等于把一部分渲染工作重新推回运行时,把好不容易去掉的依赖又请回来。

两个容易踩的坑

第一,background-color 不要挂在 span 上。只有 <pre> 上才有 --shiki-light-bg,span 层级拿不到这个变量,background-color: var(...) 会因为变量未定义而失效。

第二,行内高亮(highlightedLines)的底色是另一个变量,暗色模式下需要单独覆盖,否则会留一条亮色的横条。