rehype-pretty-code 双主题配置完全正确,代码却全灰——选择器打错了
给技术博客接入代码高亮,选了 rehype-pretty-code + 双主题方案(亮色/暗色各一套)。配置看起来无懈可击:theme: { light: "github-light", dark: "github-dark" }、keepBackground: false,globals.css 里写好了 token 变色规则。构建一切正常,打开页面——代码块是一整片灰色,没有任何高亮。
行内 `code` 正常,只有围栏代码块全军覆没。
现象
打开 DevTools 检查 DOM,发现 token span 上确实有内联样式:
<span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span>变量值完全正确,Shiki 已经算好了每个 token 的颜色。但每个 token 最终呈现的颜色都一样,就是 Prose 的默认前景色——灰。
再看 CSS:
/* 原来写的 */
.shiki, .shiki span {
color: var(--shiki-light);
}
html.dark .shiki span {
color: var(--shiki-dark);
}浏览器里选中这两条规则,匹配元素数:0。
根因
.shiki 类名是裸 Shiki(直接用 shiki 包调用 codeToHtml())输出的,rehype-pretty-code 不加这个类。
rehype-pretty-code 的真实 DOM 结构是:
figure[data-rehype-pretty-code-figure]
└ pre[data-theme="github-light github-dark"]
└ code ← 没有 .shiki 类
└ span[data-line]
└ span[style="--shiki-light:#..;--shiki-dark:#.."]
pre 和 code 上没有任何 class,只有 data-* 属性。选择器 .shiki span 一个元素也选不中,token 上的 CSS 变量永远不会被读取,每个 token 老老实实继承父元素的颜色——全灰。
把高亮 CSS 写成 .shiki span { color: var(--shiki-light) } 是从 Shiki 官方文档复制过来的常见误操作。rehype-pretty-code 包装了 Shiki,但刻意用 data-rehype-pretty-code-figure 等属性作为锚点,不保留 .shiki 类名。DevTools 里看到 token 有 --shiki-light 变量却不生效,八成就是这个原因。
正解
把选择器换成 rehype-pretty-code 实际输出的 data-* 属性,并用你的主题切换属性(这里用 html[data-theme])来切换变量:
/* 重置 figure 默认的 margin-left: 40px */
[data-rehype-pretty-code-figure] {
margin: 0;
}
/* 亮色:读 --shiki-light */
[data-rehype-pretty-code-figure] code span {
color: var(--shiki-light);
}
/* 暗色:读 --shiki-dark */
html[data-theme="dark"] [data-rehype-pretty-code-figure] code span {
color: var(--shiki-dark);
}
/* 可选:高亮行背景 */
[data-rehype-pretty-code-figure] [data-highlighted-line] {
background-color: var(--color-surface-hover);
}pre 的背景色继续由你的 Prose/pre 样式控制(正好对应 keepBackground: false 的语义)。
选择器固定用 [data-rehype-pretty-code-figure] code span,主题切换通过 html[data-theme="dark"] 前缀区分亮暗——这是文档推荐的、无需额外依赖的双主题写法。
验证
修完之后在控制台抓两个语义不同的 token,颜色必须不同:
// 关键字(keyword red)
getComputedStyle(importSpan).color // → "rgb(215, 58, 73)"
// 字符串(string blue)
getComputedStyle(stringSpan).color // → "rgb(3, 47, 98)"如果两个值不同,高亮就真正工作了。
附:Turbopack 下的额外坑
用 Next.js + Turbopack 开发时,rehype-pretty-code 必须以字符串元组形式传入,不能直接 import 函数再传:
// next.config.js — Turbopack 兼容写法
rehypePlugins: [["rehype-pretty-code", { theme: { light: "github-light", dark: "github-dark" } }]]
// 以下写法在 Turbopack 下会报错(函数无法序列化)
import rehypePrettyCode from "rehype-pretty-code";
rehypePlugins: [[rehypePrettyCode, { ... }]]一句话外卖
token 变量存在但颜色全灰,先查 CSS 选择器是否真的命中了元素——
rehype-pretty-code输出的是data-*属性,不是.shiki类。