1. 为什么你的标题下划线在 MPE 里总是不生效
Markdown Preview Enhanced(后面统一简称 MPE)是 VSCode 里做技术文档、写博客草稿、导出 PDF 时最常用的预览插件之一。很多人第一次想给标题加下划线,都是直接往style.less里塞一句h1 { border-bottom: 2px solid #ccc; },然后发现——预览里没反应,导出 PDF 还是老样子。这个场景我遇到过太多次,问题基本不在代码本身,而在样式加载顺序、选择器优先级、以及预览与导出走的是两套渲染管线。
先把核心检索词说清楚:MPE 的标题下划线定制,本质是通过style.less注入自定义 CSS,让h1~h6在预览和导出时都带上border-bottom。它适合三类人:一是用 MPE 写技术文档、需要标题层级视觉区分的开发者;二是要把 Markdown 导出成 PDF/HTML 做交付的写作者;三是已经写过 CSS 但发现“预览生效、导出失效”或“两边都不生效”的排查者。
为什么容易翻车?因为 MPE 的预览是 VSCode Webview 里渲染的,而导出 PDF/HTML 走的是另一条链路(内部用 Chrome 无头或 pandoc 类流程),两者对style.less的读取时机、CSS 作用域、以及默认主题样式的覆盖关系并不完全一致。你写的一句h1 { border-bottom: ... },很可能被 MPE 自带主题的更高优先级规则压掉,或者根本没被导出流程加载。
我试过最典型的坑:代码写对了,但style.less文件放错目录,预览用的是全局样式,导出用的是项目级样式,结果两边表现不同。还有人把border-bottom写在h1上,但 MPE 默认主题里h1有border-bottom: none之类的重置,优先级更高,直接盖掉。
所以这篇不打算只给你一段代码就完事,而是按“原问题 → 前置准备 → 可复制配置 → 验证 → 排错 → 工具入口”的顺序,把 MPE 标题下划线从预览到导出一次性讲透。你跟着做,能拿到两个确定结果:预览里标题带下划线,导出的 PDF/HTML 里也带下划线,且两者一致。
先明确一个判断标准:如果你只改了style.less但没重启预览、没确认文件路径、没检查选择器优先级,那“不生效”几乎是必然的。下面从环境准备开始,一步步来。
2. 前置准备:找到 MPE 的 style.less 与 settings.json 正确路径
在动手改样式之前,必须先把两个文件的真实位置确认清楚,否则后面所有配置都是空中楼阁。MPE 的样式定制入口是style.less,而控制插件行为的入口是 VSCode 的settings.json。这两个文件在不同系统下的路径不一样,而且 MPE 支持“全局样式”和“工作区样式”两种,优先级也不同。
先说style.less。MPE 官方推荐的做法是:在 VSCode 里按Ctrl+Shift+P(macOS 是Cmd+Shift+P)打开命令面板,输入Markdown Preview Enhanced: Customize CSS,回车。这时 MPE 会自动帮你创建并打开style.less文件。这个动作很关键,因为它会把文件放到 MPE 真正会读取的目录里,通常是:
- Windows:
%USERPROFILE%\.crossnote\style.less - macOS / Linux:
~/.crossnote/style.less
如果你手动去建文件,很容易放到~/.mume/style.less这种旧路径,MPE 新版本已经改用.crossnote目录,放错就完全不加载。所以第一步别偷懒,用命令面板打开。
打开后你会看到文件里可能已经有一些注释或示例。MPE 的style.less支持 Less 语法,但普通 CSS 也能直接写,因为 Less 是 CSS 的超集。这里有个细节:style.less里的样式默认会作用于预览,但导出时是否继承,取决于导出配置。很多人以为改了这个文件导出就自动生效,其实不一定。
再说settings.json。VSCode 的 settings 分用户级和工作区级。用户级路径:
- Windows:
%APPDATA%\Code\User\settings.json - macOS:
~/Library/Application Support/Code/User/settings.json - Linux:
~/.config/Code/User/settings.json
工作区级就是项目根目录下的.vscode/settings.json。MPE 相关的配置项以markdown-preview-enhanced.开头。和样式导出相关的关键项有:
| 配置项 | 作用 | 建议值 |
|---|---|---|
markdown-preview-enhanced.configPath | 指定 MPE 配置文件路径 | 一般不用改 |
markdown-preview-enhanced.usePandocParser | 是否用 pandoc 解析 | 导出复杂样式时建议 true |
markdown-preview-enhanced.previewTheme | 预览主题 | 选none.css可减少默认样式干扰 |
markdown-preview-enhanced.codeBlockTheme | 代码块主题 | 按需 |
markdown-preview-enhanced.enableExtendedTableSyntax | 扩展表格 | 按需 |
这里最容易被忽略的是previewTheme。MPE 默认会加载一套主题 CSS,这套主题里对h1~h6往往有明确的border-bottom或padding-bottom定义。如果你的自定义规则优先级不够,就会被它覆盖。把previewTheme设为none.css是一种“清场”手段,让自定义样式更容易生效,但代价是失去默认排版。更稳妥的做法是提高自己选择器的优先级,这个后面讲。
还有一个前置动作:确认 MPE 版本。在 VSCode 扩展面板里找到 Markdown Preview Enhanced,看版本号。老版本(比如 0.5.x 以前)的样式加载逻辑和新版本差异较大,网上很多教程对应的是旧路径。建议更新到较新版本再操作。
最后提醒一点:style.less修改后,MPE 预览不会总是自动热重载。有时候需要手动刷新预览(在预览窗口按Ctrl+Shift+P执行Markdown Preview Enhanced: Refresh Preview,或者直接关掉预览重新打开)。这个动作后面验证环节会再强调。
前置准备做到位,后面配置才不会白写。接下来进入可复制的配置片段。
3. 可复制配置:style.less 片段与 settings.json 完整写法
这一节是全文的核心操作区。我会给出两段可直接复制的配置:一段写进style.less,一段写进settings.json。同时解释每一行的作用,以及为什么这样写能同时覆盖预览和导出。
先看style.less。目标:让h1~h6都带下划线,且层级越深线条越细、颜色越浅,视觉上形成层次。代码如下:
// style.less // MPE 标题下划线定制:预览与导出通用 // 用变量统一管理,方便调整 @heading-border-color: #d0d7de; @heading-border-width: 2px; // 提高优先级:叠加 .markdown-preview 作用域,避免被默认主题覆盖 .markdown-preview { h1, h2, h3, h4, h5, h6 { border-bottom: @heading-border-width solid @heading-border-color; padding-bottom: 0.3em; margin-bottom: 0.6em; } h1 { border-bottom-width: 3px; border-bottom-color: #b0b8c0; } h2 { border-bottom-width: 2px; } h3 { border-bottom-width: 1px; border-bottom-style: dashed; } h4, h5, h6 { border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #e0e4e8; } } // 导出 PDF/HTML 时,部分渲染器不在 .markdown-preview 容器内 // 所以再补一层全局规则,确保导出也生效 h1, h2, h3, h4, h5, h6 { border-bottom: @heading-border-width solid @heading-border-color; padding-bottom: 0.3em; } h1 { border-bottom-width: 3px; border-bottom-color: #b0b8c0; } h2 { border-bottom-width: 2px; } h3 { border-bottom-width: 1px; border-bottom-style: dashed; } h4, h5, h6 { border-bottom-width: 1px; border-bottom-style: dotted; border-bottom-color: #e0e4e8; }这段代码有两个层次。第一层用.markdown-preview包裹,是为了在预览环境里提高选择器优先级,压过 MPE 默认主题。第二层是裸的h1~h6,是为了导出时也能命中——因为导出流程生成的 HTML 不一定有.markdown-preview这个容器类。两层叠加,预览和导出都能覆盖。
注意 Less 变量@heading-border-color和@heading-border-width的用法。如果你不想用 Less 变量,把@heading-border-color直接替换成#d0d7de也能跑,因为 Less 编译时会解析变量,纯 CSS 写法同样被支持。
再看settings.json。这段配置的作用是:指定预览主题为none.css减少干扰、开启 pandoc 解析以便导出时保留样式、并确保导出时加载自定义 CSS。写法如下:
{ "markdown-preview-enhanced.previewTheme": "none.css", "markdown-preview-enhanced.usePandocParser": true, "markdown-preview-enhanced.configPath": "", "markdown-preview-enhanced.enableExtendedTableSyntax": true, "markdown-preview-enhanced.mathRenderingOption": "KaTeX", "markdown-preview-enhanced.exportHTMLHead": "<style>h1,h2,h3,h4,h5,h6{border-bottom:2px solid #d0d7de;padding-bottom:0.3em;}</style>" }逐项说明:
previewTheme设为none.css,是让预览不加载默认主题的标题样式,减少覆盖冲突。如果你喜欢默认主题的排版,可以保留默认值,但要靠style.less里的高优先级选择器去压。
usePandocParser设为true,导出 PDF/HTML 时用 pandoc 解析,样式继承更完整。前提是你本机装了 pandoc,没装的话 MPE 会回退到内置解析器,样式可能丢失。装 pandoc 的方式这里不展开,官网有说明。
configPath留空表示用默认路径,一般不用改。
exportHTMLHead是关键补充。它会在导出的 HTML<head>里注入一段<style>,确保导出文件自带标题下划线样式,不依赖外部 CSS 加载。这段和style.less里的规则是双保险。注意这里用的是纯 CSS,不依赖 Less 编译。
如果你用的是工作区级配置,把这段 JSON 放进项目根目录.vscode/settings.json即可。用户级配置就放进前面说的用户 settings.json。
这里要强调一个易错点:settings.json是 JSON 格式,不能有注释,不能有尾逗号。很多人复制时带了//注释,导致整个配置解析失败,MPE 行为异常。上面这段是合法 JSON,可以直接用。
配置写完,保存两个文件。接下来进入验证环节,用两步动作确认预览和导出都生效。
4. 验证请求:预览刷新与导出比对两步确认下划线生效
配置写完不代表生效,必须做验证。这一节给两个可执行动作:第一步刷新预览看标题下划线,第二步导出 HTML/PDF 比对样式是否一致。两步都通过,才算真正搞定。
第一步,预览刷新。打开任意一个 Markdown 文件,按Ctrl+K V(macOS 是Cmd+K V)在侧边打开 MPE 预览。如果预览已经开着,执行命令面板Markdown Preview Enhanced: Refresh Preview。刷新后观察# 一级标题、## 二级标题这些是否带下划线。
如果没生效,先别急着改代码,按这个顺序排查:
- 确认
style.less是通过命令面板Customize CSS打开的,路径在.crossnote目录下。 - 确认文件已保存(
Ctrl+S)。 - 确认预览已刷新,而不是停留在旧渲染。
- 打开 VSCode 开发者工具(
Help > Toggle Developer Tools),在 Elements 面板里找到h1元素,看它的 computed style 里border-bottom是什么值,以及是哪条规则生效的。这一步能直接看出是没加载还是被覆盖。
我实测下来,最常见的“预览不生效”原因是style.less放错目录,或者预览没刷新。开发者工具一看 computed style 就清楚了。
第二步,导出比对。在 Markdown 文件里右键,选择Markdown Preview Enhanced: Export,或者用命令面板执行导出,选 HTML 或 PDF。导出完成后打开文件,检查标题下划线是否和预览一致。
导出比对的重点是看三处:
- 标题是否带下划线;
- 下划线颜色、粗细是否和预览一致;
- 层级样式(h1 粗实线、h3 虚线等)是否保留。
如果预览有、导出没有,大概率是usePandocParser没开,或者exportHTMLHead没写。如果导出有、预览没有,那是style.less的预览作用域问题。如果两边都没有,回到第一步查文件路径和保存状态。
这里给一个具体的验证用例。新建一个test-heading.md,内容如下:
# 一级标题测试 正文段落。 ## 二级标题测试 正文段落。 ### 三级标题测试 正文段落。按上面两步走:先预览刷新,确认三个标题都带下划线且样式有层级差异;再导出 HTML,用浏览器打开,确认样式一致。两步都过,配置就算成功。
导出 PDF 时还要注意一点:PDF 的分页可能让标题下划线和标题分离,这是分页算法导致的,不是样式问题。可以在settings.json里调整导出参数,或者接受这个表现。如果必须严格一致,优先用 HTML 导出再转 PDF。
验证通过后,如果遇到报错,下一节集中讲常见错误。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth 对照
这一节把 MPE 样式定制和接入类操作中容易遇到的报错集中对照。虽然标题下划线本身不涉及网络请求,但很多人在配置 MPE 的同时会接入模型服务或 API,报错信息容易混淆。下面按真实报错逐条给排查方向。
401 Unauthorized。这个报错通常出现在你通过 MPE 或相关插件调用模型 API 时,Key 无效或没带。排查:确认 API Key 是否正确复制,有没有多余空格;确认请求头里Authorization: Bearer <key>格式正确;确认 Base URL 和 Key 属于同一服务。如果你用的是 TaoToken 的 API,Base URL 是https://taotoken.net/api,Key 在控制台的 API Keys 页面生成。注意 Base URL 不要带 UTM 参数,API 地址就是纯https://taotoken.net/api。
local proxy failed。这个报错一般出现在本地代理配置错误时。排查:检查settings.json或环境变量里有没有残留的代理配置;确认没有把不存在的本地端口写进代理;如果你根本没配代理,检查系统环境变量HTTP_PROXY、HTTPS_PROXY是否被其他软件设置。这个报错和 MPE 样式无关,但会阻断需要联网的功能。
reading choices。这个报错通常出现在调用模型返回结果解析阶段,比如返回体里没有预期的choices字段。排查:确认请求的接口路径正确;确认请求体里model参数是服务端支持的模型 ID;确认返回的是 JSON 而不是 HTML 错误页。如果你在 MPE 里通过代码块执行模型调用,检查代码里的 URL 和参数。
OAuth 相关报错。如果你用 Claude Code 或类似工具做 OAuth 登录,报错可能是回调地址不匹配、token 过期、或权限范围不足。排查:确认回调地址和注册时一致;确认 token 没过期;确认账号有对应权限。Claude Code 接入时,Base URL、Key、Model ID 三件套要写全,缺一个都会失败。
这里把三件套的写法明确一下,以 Claude Code 的settings.json或auth.json为例:
{ "baseUrl": "https://taotoken.net/api", "apiKey": "你的_API_KEY", "model": "claude-sonnet-4-20250514" }注意baseUrl用 API 地址,不带 UTM。apiKey从控制台生成。model填服务端支持的模型 ID。这三项在 Cline MCP、CC Switch、Codex auth.json 里都是核心配置,缺一不可。
回到 MPE 样式本身,如果你在排查下划线问题时看到控制台报 CSS 解析错误,检查style.less里有没有语法错误,比如少了大括号、变量名拼错。Less 编译失败会导致整个样式文件不加载,表现就是“什么都没生效”。
还有一个隐蔽问题:VSCode 里同时装了多个 Markdown 预览插件,快捷键冲突,你以为打开的是 MPE 预览,其实是别的插件。确认预览窗口标题栏或右下角显示的是 MPE。这个坑不常见但很浪费时间。
排错的核心思路是:先确认文件路径和保存状态,再看开发者工具里的 computed style,最后才怀疑代码逻辑。大部分“不生效”都是前两步的问题。
6. 工具入口与长期使用建议
样式配好之后,如果你还想把 MPE 用在更完整的写作或开发流程里,比如接入模型做辅助写作、代码解释、文档润色,可以按需选择入口。下面按场景分流,不堆砌。
需要生成和管理 API Key、查看接入文档的,走这两个入口:
- API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
想先在网页里验证模型效果、对比不同模型输出的,用模型对话入口:
- 模型对话:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=chat
如果你打算长期用 MPE 配合编码、Agent 类工作流,比如让模型帮你写文档、改代码、生成配置,Coding Plan 更合适:
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
需要看控制台整体用量和配置的:
- 控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console
Claude Code 接入相关的完整说明:
- Claude Code Anthropic 接入:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=claude_code
官网首页作为总入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=home
最后给一个实用建议:把style.less和settings.json的配置纳入版本管理。如果你在多台机器上写文档,把.crossnote/style.less的内容和项目级.vscode/settings.json一起提交到仓库,换机器时直接拉下来,标题下划线样式就能保持一致,不用重新配。导出 PDF 前,先用 HTML 导出验证样式,再转 PDF,能避免分页导致的样式偏差。这套流程跑顺之后,MPE 的标题层级视觉区分就稳定了。