资讯动态

Typora 自定义 CSS:代码块配色与标题自动序号全攻略

发布时间:2026/10/21 3:14:41 来源:尧图企业网站定制
很多把 Typora 当主力写作工具的人都会有个共同的小烦恼默认主题的代码块配色看久了总觉得缺了点什么标题段落又多的时候手动敲序号又麻烦又容易乱。尤其当你要把文章截图发到技术社区或者作为组内分享材料时代码块颜色不统一、标题没有自动序号的截图观感差距是很明显的。这篇博文就集中解决两个问题一是给 Typora 的代码块做一套自定义配色二是让标题自动带上多级序号。我会把可直接复制的 CSS 代码贴出来逐段解释它的作用再加上我在实际配置过程中踩过的几个坑。如果你也想把自己的 Typora 折腾成“截图即作品”的状态照着抄就行。1. 先把 Typora 的样式体系摸清楚1.1 Typora 本质上就是用 CSS 渲染 MarkdownTypora 是一个基于 Chromium 内核的桌面编辑器“所见即所得”的意思是你写的 Markdown 语法会被实时解析成 HTML 标签再由内置的 CSS 渲染成最终的视觉效果。换句话说你看到的每一个标题、每一段代码块、每一处引用背后都是一个真实的 DOM 元素比如标题就是h1到h6代码块则是一个pre包裹的 CodeMirror 编辑区。这就意味着能不能实现“代码块配色自定义”和“标题自动序号”本质上是一个 CSS 覆盖问题而不是 Typora 提供不提供某个开关的问题。你不需要会写插件也不需要改 Typora 的源码只要往它的主题 CSS 里追加一段自己的规则效果就能全局生效。理解这点很重要因为很多人在网上搜“Typora 代码块配色修改”找到一堆零散的片段却不知道为什么有的有效、有的无效。核心原因往往只有一个选择器优先级不够或者写错了目标元素。1.2 修改样式前必须知道的两条入口Typora 的自定义样式有两条常见入口第一条是直接在主题文件夹里改。打开 Typora 的“偏好设置 → 外观”一般能找到“打开主题文件夹”按钮。这里面有一个和当前主题同名的 CSS 文件比如默认主题对应的是 github.css浅色和深色可能有不同文件。你在这个文件末尾追加 CSS就是完整地基于当前主题做覆盖。第二条是使用 Typora 提供的“自定义 CSS”入口。较新的版本中在偏好设置里可以直接编辑一份额外的 CSS 文件它会在主题加载完之后再加载。因为加载顺序靠后所以在不修改主题原文件的前提下这份自定义 CSS 天然拥有更高的优先级这是我最推荐的方式。还有一个定位神技如果你不确定自己看到的某个元素对应什么 CSS 类名可以在 Typora 里按快捷键或者从菜单打开“开发者工具”来检查元素。不同版本的入口位置不完全一样常见做法是菜单栏“帮助 → 开发者工具”或者直接右键点击页面元素选择“检查”。在开发者工具里你可以看到类似div classCodeMirror这样的真实 DOM 结构这是后续写选择器的最可靠依据。1.3 代码块和标题是两套完全不同的思路正文中的标题直接就是h1到h6标签改颜色、字号、加序号都是标准的 HTML CSS 操作。而代码块不同。Typora 的代码块组件基于 CodeMirror 实现也就是 Typora 把代码编辑区做成了一套在线编辑器里面每一个关键字、字符串、注释都有单独的 CSS 类名比如.cm-keyword、.cm-string、.cm-comment。所以你要做的不是去改一个统一的“代码块文字颜色”而是要分别控制这些 token 的颜色。把这两套机制分清楚后面就不会混。2. 代码块配色以 One Dark 风格为样板2.1 准备一份固定的颜色变量表我强烈建议你先定义一组颜色变量而不是把这些色值散落在各个规则里。好处是一旦你想整体换风格只需要改最上面几个变量后续维护成本接近零。我这里以 One Dark 风格为例。这套配色之所以受欢迎是因为它整体柔和背景是偏蓝的深灰色不是纯黑关键字用暖紫色字符串用绿色注释用暗灰色加斜体。长时间看不会特别刺眼对比度又足够清晰。以下是色值表你也可以理解为我要做的所有改动的“调色盘”用途色值说明代码块背景#282c34One Dark 标志性的深灰蓝默认文字颜色#abb2bf代码主体文字的浅灰关键字#c678ddif、for、function 这类字符串#98c379双引号、单引号内容注释#5c6370灰色斜体函数名#61afef自定义函数和类名常用数字#d19a66数值字面量属性名#e5c07bJSON 键名、HTML 属性行号#495162行号区域的文字颜色选中区域#3e4451鼠标划选代码时的高亮底色如果你有自己偏好的配色主题比如 Monokai Pro 或者 Dracula只需把这几个变量替换成对应主题的色值后面所有 CSS 规则可以原样复用。2.2 用 CSS 变量组织一套完整代码块配色在自定义 CSS 中第一步先定义变量第二步覆盖各个 CodeMirror token 的颜色。:root { --code-bg: #282c34; --code-fg: #abb2bf; --code-keyword: #c678dd; --code-string: #98c379; --code-comment: #5c6370; --code-function: #61afef; --code-number: #d19a66; --code-attribute: #e5c07b; --code-lineno: #495162; --code-selection: #3e4451; } /* 代码块整体背景和默认文字颜色 */ #write .CodeMirror, #write .CodeMirror-scroll { background-color: var(--code-bg); color: var(--code-fg); } /* 关键字 */ #write .CodeMirror .cm-keyword { color: var(--code-keyword); } /* 字符串 */ #write .CodeMirror .cm-string { color: var(--code-string); } /* 注释 */ #write .CodeMirror .cm-comment { color: var(--code-comment); font-style: italic; } /* 函数名 */ #write .CodeMirror .cm-def { color: var(--code-function); } /* 数字 */ #write .CodeMirror .cm-number { color: var(--code-number); } /* 属性名 */ #write .CodeMirror .cm-attribute { color: var(--code-attribute); } /* 行号区域 */ #write .CodeMirror .CodeMirror-gutters { background-color: var(--code-bg); border-right: 1px solid #3e4451; } #write .CodeMirror .CodeMirror-linenumber { color: var(--code-lineno); } /* 选中区域 */ #write .CodeMirror .CodeMirror-selected, #write .CodeMirror.CodeMirror-focused .CodeMirror-selected { background-color: var(--code-selection); }你可能注意到我所有选择器都带了#write前缀这不是多余的。#write是 Typora 正文区的根容器。加了它之后规则只作用于正文里的代码块不会误伤侧边栏、搜索面板等地方的样式也不会在你导出或者切换视图时影响其他 UI。关于!important如果你是在自定义 CSS 文件里加这段代码由于加载顺序靠后通常不需要加。但如果你是把代码追加到某个第三方主题的 CSS 文件中主题自身可能给部分规则加了!important这时候你就得在对应规则后面也加一个!important才能压过它。我的建议是先不加测试一下哪些规则没生效再针对性地补而不是一上来全部加。2.3 行号显示不归 CSS 管颜色才归 CSS 管代码块要不要显示行号是由 Typora 的偏好设置控制的不是 CSS 能决定的。你可以在“偏好设置 → Markdown → 代码块 → 显示行号”里勾选开关而 CSS 能做的是把行号槽位的背景色、文字颜色、边框颜色调整成和整体方案匹配。上面我已经写了行号区域的样式如果你想进一步微调行号与代码之间的距离可以给.CodeMirror-linenumber加padding-right或改text-align。但要小心行号区域和代码区域是分开渲染的间距改太大看起来会很割裂建议保持默认宽度只改颜色。2.4 行内代码和代码块不要混为一谈很多人在改代码块配色时会顺手把行内代码的样式也改了或者反过来一改行内代码就影响到了代码块内部。原因在于行内代码本质上是 HTML 里的code标签而代码块内部是 CodeMirror 渲染出来的span结构两者并不属于同一个渲染体系。如果只是给行内代码更醒目的背景色建议这样写#write p code, #write li code { background-color: #f0f0f0; color: #c7254e; border-radius: 3px; padding: 2px 4px; font-family: JetBrains Mono, Cascadia Code, Consolas, monospace; }这里明确限定p和li内部的code避免选择器一路通配到代码块组件里。如果你发现行内代码样式无意中影响到了代码块几乎可以断定是选择器写得太宽了把所有code都选中了。3. 标题自动序号CSS 计数器完全方案3.1 原理其实很简单几个“计数器”小匣子CSS 提供了一种计数机制专门用于自动编号这就是counter-reset、counter-increment和content三个属性的组合。你可以把计数器想象成一排小匣子找出所有h1时给第一个匣子加 1找出所有h2时给第二个匣子加 1当一个新的h1出现时再把后面所有匣子归零。在::before伪元素里用content把计数器的当前值拼成文本比如counter(h1) . counter(h2)最终展示在标题前面。这么做的好处是你的 Markdown 源文件里完全不需要手动写“1.1”“1.2”序号是渲染时自动生成的增删标题也不会乱。3.2 一级二级标题序号的基础版如果你只需要简单的两层编号直接看这个基础版就够了#write { counter-reset: h1counter; } #write h1 { counter-reset: h2counter; } #write h1::before { counter-increment: h1counter; content: counter(h1counter) ; } #write h2 { counter-reset: h3counter; } #write h2::before { counter-increment: h2counter; content: counter(h1counter) . counter(h2counter) ; }把counter-reset: h2counter放在h1上是因为每出现一个新的h1就意味着后面所有h2编号都要从头开始算。这是个容易被忽略的细节如果只在h2上做counter-reset那整个文档的h2序号会一直累计下去永远不归零。3.3 兼容到六级标题的完整实现作为一篇技术博文很多人实际写作时会把标题用到三四级甚至更深。我给出一个可以直接抄的完整版本支持从h1到h6的多级自动序号序号的呈现格式是1、1.1、1.1.1这种标准的层级型。#write { counter-reset: h1counter h2counter h3counter h4counter h5counter h6counter; } #write h1 { counter-reset: h2counter h3counter h4counter h5counter h6counter; } #write h1::before { counter-increment: h1counter; content: counter(h1counter) ; } #write h2 { counter-reset: h3counter h4counter h5counter h6counter; } #write h2::before { counter-increment: h2counter; content: counter(h1counter) . counter(h2counter) ; } #write h3 { counter-reset: h4counter h5counter h6counter; } #write h3::before { counter-increment: h3counter; content: counter(h1counter) . counter(h2counter) . counter(h3counter) ; } #write h4 { counter-reset: h5counter h6counter; } #write h4::before { counter-increment: h4counter; content: counter(h1counter) . counter(h2counter) . counter(h3counter) . counter(h4counter) ; } #write h5 { counter-reset: h6counter; } #write h5::before { counter-increment: h5counter; content: counter(h1counter) . counter(h2counter) . counter(h3counter) . counter(h4counter) . counter(h5counter) ; } #write h6::before { counter-increment: h6counter; content: counter(h1counter) . counter(h2counter) . counter(h3counter) . counter(h4counter) . counter(h5counter) . counter(h6counter) ; }复制这段代码到自定义 CSS 后重启 Typora 或切换一次主题所有标题应该都会带上编号。如果你平时不从h1写起而是直接用h2作为第一层标题那可以做一个小改动把#write上的计数器初始化改成只初始化h2counter及其后面的计数器然后从h2::before开始递增。核心思路不变只是把起点调低一级。3.4 进阶用法扁平编号与中文序号层级编号适合论文、手册类内容。但如果你只是想让文章里的h2依次出现“1、2、3、4”而不希望它跟着h1变化就不要在content里引用h1counter改成给h2单独维护一个独立的计数器#write { counter-reset: flatcounter; } #write h2::before { counter-increment: flatcounter; content: counter(flatcounter) . ; }这样整篇文档所有h2会连续编号跟h1完全没有关系。另一个常见需求是中文序号比如希望h1显示成“一、二、三”h2显示成“一二”。CSS 计数器原生支持中文数字计数写法是给counter的第二个参数指定一个计数风格#write h1::before { counter-increment: h1counter; content: counter(h1counter, cjk-ideographic) 、; }不过我实际测试下来中文计数在不同渲染环境下的字体样式差异比较大如果你只是自己本地用问题不大如果要发布到不支持这套 CSS 的平台那它依然只是本地效果。3.5 让序号和标题文字结构更协调加了序号之后标题文字和序号会在同一行显示。如果不做任何间距处理序号就会紧紧贴着标题文字视觉上看起来很挤。我习惯在content末尾加一个空格同时调整::before的粗细和颜色让序号的视觉分量弱于标题文字本身。比如让h1的序号颜色稍微淡一些标题主体保持醒目的深色层次会更清晰#write h1::before { color: #888888; font-weight: normal; margin-right: 8px; }这里有点要注意::before伪元素本身是inline元素margin-right是有效的但如果某个主题给标题设置了display: flex或position: relative间距调整方式可能会变这时候你需要到开发者工具里先确认标题的布局模式。如果你把标题当作“大章节”甚至可以给标题加一条下边线配合自动序号后整篇文章的骨架感会非常强#write h1 { padding-bottom: 6px; border-bottom: 1px solid #eeeeee; }3.6 一个必须提醒的坑手动序号会冲突这是我最常被问到的问题明明加了 CSS为什么标题前面出现了“1. 1. 引言”这种重复序号不用怀疑一定是你的 Markdown 源文件里已经手动写了“1.”这类文本。CSS 计数器渲染出的序号会保留你的原文两个叠在一起就会出现“1. 1.”。遇到这种情况删掉源文件里的手动序号即可只保留纯文字标题让 CSS 统一生成编号。另外提示一点Typora 的侧边栏“大纲”和“文件树”里显示的标题不是通过 CSS 渲染的伪元素而是直接读取标题文本生成的。所以大纲面板里的标题不会显示自动序号这其实是正常现象不用担心是 CSS 没生效。如果你需要在导出或者截图时看到序号正文区域才是唯一可靠的参照。4. 常见问题与排查技巧4.1 修改完 CSS 不生效这是所有 Typora 自定义样式入门者遇到的第一个问题。CSS 文件写到一半回到编辑区一看什么都没变。原因通常是 Typora 不会实时监听外部 CSS 文件的改动你需要重新加载样式。最简单的办法是在“偏好设置 → 外观”里把主题切换到另一个主题再切回来。如果这样还不行直接关闭 Typora 重新打开基本都能解决。如果你已经切换了主题序号还是没出现优先打开开发者工具选中标题元素看::before伪元素到底有没有生成。伪元素不存在说明规则根本没加载或选择器没匹配上伪元素存在但没内容说明content属性可能写错了。4.2 代码块配色只生效了一部分这种状况通常不是代码写错而是选择器优先级被主题自带样式压过了。特别是覆盖代码块 token 颜色时部分主题对.cm-keyword这类类名的样式写得很具体或者额外加了!important。排查方法是把没有生效的规则复制一份加上!important放到文件末尾再切换主题查看效果。如果加了!important还是不行用开发者工具查看该元素最终计算出的颜色看看它实际匹配到了哪条规则顺藤摸瓜找到源头。4.3 导出 PDF 后标题序号消失先说结论Typora 导出 PDF 时序号是能正常保留的因为 PDF 导出走的是浏览器打印渲染链路CSS 会一起参与。如果你发现导出后序号不显示先确认你用的是不是“导出为 PDF”而不是先转成其他格式再打印。但如果你导出的是 Docx、Latex 等其他格式那就不经过 CSS 渲染了这些格式需要的是 Markdown 源文本不会带上伪元素生成的序号。所以如果你的工作流是“Typora 写稿 → 导出 Word → 交给编辑”自动序号这个功能基本派不上用场还是得手动敲或者依赖目标格式的自动编号功能。另外截图发布到公众号、掘金这类平台时CSS 渲染的序号也只会停留在截图里平台不会继承。如果你想让发布后的网页本身带序号需要在发布平台的富文本编辑器中手动处理或者用带序号的 Markdown 语法生成后再粘贴。4.4 深色模式下代码配色发白Typora 的深色模式通常会加载另一份主题 CSS。你在浅色主题 css 末尾追加的规则不一定会被深色主题文件继承。如果你平时主要用深色模式就需要在深色主题对应的 CSS 文件里同样追加一份配色方案。我自己的做法是直接定义一份通用的深色代码块配色浅色和深色主题都追加同一份代码保证两套模式下代码区域底色都是深色文字对比度一致。这样虽然浅色模式下会出现大面积的深色代码块但视觉上其实是一种常见的排版风格很多技术文档网站都在用并不会不协调。4.5 代码块底色变了但文字颜色没变如果代码块背景已经生效文字还是默认的黑色或深灰色说明某些规则没有匹配到具体 token。比如你只改了.cm-string但代码里大量出现的函数名、变量名属于.cm-variable或.cm-def而这两个类名你并没有覆盖。最省事的做法是在开发者工具里点击任意一个代码 token看它实际被标了哪些类名再针对性地补规则。常用类名基本覆盖关键字、字符串、注释、数字、属性名、标签名、变量和函数定义建议把这几个都写全。5. 扩展玩法与我的个人经验5.1 把配色方案当成自己的“签名”代码块配色这件事一旦调顺了会变成你文章封面截图里的一个标志性元素。我见过有人把代码块背景改成暖黄色纸纹风格有人专攻低饱和度莫兰迪色还有人坚持用日系深蓝。往小了说是个人审美往大了说是建立文章辨识度。如果你不想从零开始调色直接从 VS Code 或 JetBrains 系的经典主题里抄色值表效率最高。One Dark、Monokai Pro、Dracula、Solarized、Nord 这些主题都有现成配色你要做的只是把色值映射到cm-keyword这些类名上半小时就能完成一套新皮肤。5.2 序号方案要按内容类型选择我自己写长文时如果是一篇有明确章节的文章一定用完整版层级编号因为读者在阅读时能随时知道当前处在第几层如果只是一篇技巧清单类文章通篇只有若干个小标题用扁平编号反而更干净层级编号会显得“重”。还有一个小技巧是如果你不希望h1显示序号比如文档本身就只有一个h1作为文章标题可以从h2开始写计数器把h1排除在外。做法很简单不要在h1::before里写任何规则同时把h2的递增逻辑单独提出来这样标题就是干净的而下面的小节可以正常编号。5.3 维护一份属于自己的 theme.css随着你用的时间越来越长你会积累出很多符合自己习惯的微调标题间距、引用块样式、图片阴影、表格边框、行内代码底色、甚至是导出 PDF 时的页面边距。把代码块配色和标题序号都集中放在一份独立 CSS 文件里打包备份换电脑的时候直接导入是最省心的维护方式。我个人每次更新 Typora第一件事就是确认自定义 CSS 有没有被重置然后打开自己的配置文档核对一遍。这个习惯帮我避开了很多因为版本升级导致的样式丢失问题。从代码块配色到标题自动编号再到深色模式适配和导出问题排查这套改造基本覆盖了日常写作中会遇到的绝大部分场景。希望你能把这套方案变成自己顺手的那份配置让 Typora 真正成为贴合你写作习惯的工具。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑