[css] 自动标题编号

写在前面

本文中包含 1.X 和 2.X 两个版本,1.X 为无背景版,2.X 为有背景版,按需选择即可。

最终效果

2.0 版本效果:

PixPin_2025-08-08_18-35-57

缘由

最近经常使用思源做笔记,但是思源笔记导出成 pdf 后,用黑白打印机打印出来后,各级标题十分难以区分,所以就萌生了利用 css 将标题自动编号的想法,后面在论坛里面找到了 @ABin666 的这个链接,但是在我的电脑上无法使用,于是便自己修改了一下,【自动标题编号 v1.0】版本就此诞生。

1.X 版本

/* ================= 标题自动编号模块 v1.0 ================= */
body {
    counter-reset: h1-count;
}

h1,
.h1 {
    counter-increment: h1-count;
    counter-reset: h2-count;
}

h2,
.h2 {
    counter-increment: h2-count;
    counter-reset: h3-count;
}

h3,
.h3 {
    counter-increment: h3-count;
    counter-reset: h4-count;
}

h4,
.h4 {
    counter-increment: h4-count;
    counter-reset: h5-count;
}

h5,
.h5 {
    counter-increment: h5-count;
    counter-reset: h6-count;
}

/* 通用计数器样式 */
.protyle-wysiwyg [data-node-id][class^="h"] div:first-child:before,
.b3-typography h1:before,
.b3-typography h2:before,
.b3-typography h3:before,
.b3-typography h4:before,
.b3-typography h5:before,
.b3-typography h6:before {
    display: inline-block !important;
    float: none;
    margin-right: 8px;
    font-size: 100%;
    opacity: 1 !important;
    /* 强制显示防止折叠隐藏 */
}



/* ================== 无上级标题时编号优化 ================== */
.b3-typography h2:not(:has(~ h1)):before {
    content: counter(h2-count) "\00A0";
}

.b3-typography h3:not(:has(~ h1, ~ h2)):before {
    content: counter(h3-count) "\00A0";
}

.b3-typography h4:not(:has(~ h1, ~ h2, ~ h3)):before {
    content: counter(h4-count) "\00A0";
}

.b3-typography h5:not(:has(~ h1, ~ h2, ~ h3, ~ h4)):before {
    content: counter(h5-count) "\00A0";
}

.b3-typography h6:not(:has(~ h1, ~ h2, ~ h3, ~ h4, ~ h5)):before {
    content: counter(h6-count) "\00A0";
}

.b3-typography h3:not(:has(~ h1)):before {
    content: counter(h2-count) "." counter(h3-count) "\00A0";
}

.b3-typography h4:not(:has(~ h1, ~ h2)):before {
    content: counter(h3-count) "." counter(h4-count) "\00A0";
}

.b3-typography h5:not(:has(~ h1, ~ h2, ~ h3)):before {
    content: counter(h4-count) "." counter(h5-count) "\00A0";
}

.b3-typography h6:not(:has(~ h1, ~ h2, ~ h3, ~ h4)):before {
    content: counter(h5-count) "." counter(h6-count) "\00A0";
}


/* 层级式编号生成规则 */
.protyle-wysiwyg [data-node-id].h1 div:first-child:before,
.b3-typography h1:before {
    content: counter(h1-count) "\00A0";
}

.protyle-wysiwyg [data-node-id].h2 div:first-child:before,
.b3-typography h2:before {
    content: counter(h1-count) "." counter(h2-count) "\00A0";
}

.protyle-wysiwyg [data-node-id].h3 div:first-child:before,
.b3-typography h3:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "\00A0";
}

.protyle-wysiwyg [data-node-id].h4 div:first-child:before,
.b3-typography h4:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "." counter(h4-count) "\00A0";
}

.protyle-wysiwyg [data-node-id].h5 div:first-child:before,
.b3-typography h5:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "." counter(h4-count) "." counter(h5-count) "\00A0";
    font-size: 90% !important;
}

.protyle-wysiwyg [data-node-id].h6 div:first-child:before,
.b3-typography h6:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "." counter(h4-count) "." counter(h5-count) "." counter(h6-count) "\00A0";
    font-size: 85% !important;
}

/* ================= 标题视觉标识模块 ================= */
/* 标题颜色分级系统 */
[data-type="NodeHeading"].h1 {
    color: #d40045 !important;
}

[data-type="NodeHeading"].h2 {
    color: #ff7f00 !important;
}

[data-type="NodeHeading"].h3 {
    color: #66b82b !important;
}

[data-type="NodeHeading"].h4 {
    color: #093f86 !important;
}

[data-type="NodeHeading"].h5 {
    color: #340c81 !important;
}


/* ================= 折叠状态适配模块 ================= */
.protyle-wysiwyg [data-fold="1"] [data-node-id][class^="h"] {
    position: relative;
    padding-right: 4em !important;
    /* 为折叠标记留出空间 */
}

2.X 版本

今天突然看到了 QYL 主题的边框文档树的效果:

PixPin20250808174151.png

于是起了更新下代码的想法,好在过程十分顺利,就此【自动标题编号 v2.X】正式诞生啦 🎉

代码如下:

/* ================= 标题自动编号模块 v2.2 ================= */

/* CSS 变量定义 */
:root {
    --h1-color: #d40045;
    --h2-color: #ff7f00;
    --h3-color: #66b82b;
    --h4-color: #093f86;
    --h5-color: #340c81;
    --h6-color: #666666;

    --h1-bg: rgba(212, 0, 69, 0.1);
    --h2-bg: rgba(255, 127, 0, 0.1);
    --h3-bg: rgba(102, 184, 43, 0.1);
    --h4-bg: rgba(9, 63, 134, 0.1);
    --h5-bg: rgba(52, 12, 129, 0.1);
    --h6-bg: rgba(102, 102, 102, 0.08);
}

/* ================= 计数器配置 ================= */
body {
    counter-reset: h1-count;
}

/* 简化计数器重置选择器 */
.protyle-wysiwyg [data-node-id].h1,
.b3-typography h1 {
    counter-increment: h1-count;
    counter-reset: h2-count;
}

.protyle-wysiwyg [data-node-id].h2,
.b3-typography h2 {
    counter-increment: h2-count;
    counter-reset: h3-count;
}

.protyle-wysiwyg [data-node-id].h3,
.b3-typography h3 {
    counter-increment: h3-count;
    counter-reset: h4-count;
}

.protyle-wysiwyg [data-node-id].h4,
.b3-typography h4 {
    counter-increment: h4-count;
    counter-reset: h5-count;
}

.protyle-wysiwyg [data-node-id].h5,
.b3-typography h5 {
    counter-increment: h5-count;
    counter-reset: h6-count;
}

.protyle-wysiwyg [data-node-id].h6,
.b3-typography h6 {
    counter-increment: h6-count;
}

/* ================= 编号显示样式 ================= */
/* 通用编号样式基础 */
.protyle-wysiwyg [data-node-id][class^="h"] div:first-child:before,
.b3-typography h1:before,
.b3-typography h2:before,
.b3-typography h3:before,
.b3-typography h4:before,
.b3-typography h5:before,
.b3-typography h6:before {
    display: inline-block;
    margin-right: 8px;
    font-weight: 600;
    opacity: 0.85;
    user-select: none;
}

/* 层级编号内容 */
.protyle-wysiwyg [data-node-id].h1 div:first-child:before,
.b3-typography h1:before {
    content: counter(h1-count) "\00A0";
    font-size: 1em;
}

.protyle-wysiwyg [data-node-id].h2 div:first-child:before,
.b3-typography h2:before {
    content: counter(h1-count) "." counter(h2-count) "\00A0";
    font-size: 0.95em;
}

.protyle-wysiwyg [data-node-id].h3 div:first-child:before,
.b3-typography h3:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "\00A0";
    font-size: 0.9em;
}

.protyle-wysiwyg [data-node-id].h4 div:first-child:before,
.b3-typography h4:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "." counter(h4-count) "\00A0";
    font-size: 0.88em;
}

.protyle-wysiwyg [data-node-id].h5 div:first-child:before,
.b3-typography h5:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "." counter(h4-count) "." counter(h5-count) "\00A0";
    font-size: 0.85em;
}

.protyle-wysiwyg [data-node-id].h6 div:first-child:before,
.b3-typography h6:before {
    content: counter(h1-count) "." counter(h2-count) "." counter(h3-count) "." counter(h4-count) "." counter(h5-count) "." counter(h6-count) "\00A0";
    font-size: 0.8em;
}

/* ================= 标题视觉样式 ================= */
/* 编辑器模式颜色映射 - 提高选择器优先级 */
.protyle-wysiwyg [data-type="NodeHeading"].h1,
.protyle-wysiwyg [data-node-id].h1 {
    color: var(--h1-color) !important;
    background-color: var(--h1-bg);
}

.protyle-wysiwyg [data-type="NodeHeading"].h2,
.protyle-wysiwyg [data-node-id].h2 {
    color: var(--h2-color) !important;
    background-color: var(--h2-bg);
}

.protyle-wysiwyg [data-type="NodeHeading"].h3,
.protyle-wysiwyg [data-node-id].h3 {
    color: var(--h3-color) !important;
    background-color: var(--h3-bg);
}

.protyle-wysiwyg [data-type="NodeHeading"].h4,
.protyle-wysiwyg [data-node-id].h4 {
    color: var(--h4-color) !important;
    background-color: var(--h4-bg);
}

.protyle-wysiwyg [data-type="NodeHeading"].h5,
.protyle-wysiwyg [data-node-id].h5 {
    color: var(--h5-color) !important;
    background-color: var(--h5-bg);
}

.protyle-wysiwyg [data-type="NodeHeading"].h6,
.protyle-wysiwyg [data-node-id].h6 {
    color: var(--h6-color) !important;
    background-color: var(--h6-bg);
}

/* 通用标题基础样式 */
.protyle-wysiwyg [data-type="NodeHeading"],
.b3-typography h1,
.b3-typography h2,
.b3-typography h3,
.b3-typography h4,
.b3-typography h5,
.b3-typography h6 {
    border-radius: 4px;
    padding: 6px 8px;
    margin: 8px 0;
    transition: background-color 0.2s ease;
}

/* 预览模式分级样式 */
.b3-typography h1 {
    color: var(--h1-color) !important;
    background-color: var(--h1-bg);
    padding: 8px 12px;
    margin: 16px 0;
    border-left: 4px solid var(--h1-color);
}

.b3-typography h2 {
    color: var(--h2-color) !important;
    background-color: var(--h2-bg);
    padding: 6px 10px;
    margin: 14px 0;
    border-left: 3px solid var(--h2-color);
}

.b3-typography h3 {
    color: var(--h3-color) !important;
    background-color: var(--h3-bg);
    padding: 5px 8px;
    margin: 12px 0;
    border-left: 2px solid var(--h3-color);
}

.b3-typography h4 {
    color: var(--h4-color) !important;
    background-color: var(--h4-bg);
    padding: 4px 6px;
    margin: 10px 0;
}

.b3-typography h5 {
    color: var(--h5-color) !important;
    background-color: var(--h5-bg);
    padding: 3px 5px;
    margin: 8px 0;
}

.b3-typography h6 {
    color: var(--h6-color) !important;
    background-color: var(--h6-bg);
    padding: 2px 4px;
    margin: 6px 0;
}

/* ================= 编辑器内标题文本颜色强制设置 ================= */
/* 针对编辑器内标题内容的颜色设置 */
.protyle-wysiwyg [data-node-id].h1 div,
.protyle-wysiwyg [data-type="NodeHeading"].h1 div {
    color: var(--h1-color) !important;
}

.protyle-wysiwyg [data-node-id].h2 div,
.protyle-wysiwyg [data-type="NodeHeading"].h2 div {
    color: var(--h2-color) !important;
}

.protyle-wysiwyg [data-node-id].h3 div,
.protyle-wysiwyg [data-type="NodeHeading"].h3 div {
    color: var(--h3-color) !important;
}

.protyle-wysiwyg [data-node-id].h4 div,
.protyle-wysiwyg [data-type="NodeHeading"].h4 div {
    color: var(--h4-color) !important;
}

.protyle-wysiwyg [data-node-id].h5 div,
.protyle-wysiwyg [data-type="NodeHeading"].h5 div {
    color: var(--h5-color) !important;
}

.protyle-wysiwyg [data-node-id].h6 div,
.protyle-wysiwyg [data-type="NodeHeading"].h6 div {
    color: var(--h6-color) !important;
}
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28446 引用 • 119790 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    285 引用 • 1988 回帖
3 操作
liuyz0112 在 2025-08-09 08:05:11 更新了该帖
liuyz0112 在 2025-08-08 18:55:59 更新了该帖
liuyz0112 在 2025-08-08 18:36:58 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • zhaozhongyi

    厉害

  • simuliya 1 评论

    与此同时大纲里面的标题也应该有自动编号才好吧,可以实现吗

    :root { --h1-list-graphic: rgba(221, 136, 134, 1); --h2-list-graphic: rgba(171, 155, 199, 1); --h3-list-graphic: rgba(137, 180, 202, 1); --h4-list-graphic: rgba(125, 165, 151, 1); --h5-list-graphic: rgba(215, 192, 111, 1); --h6-list-graphic: rgba(212, 165, 155, 1); } /* 初始化计数器 / .sy__outline ul.b3-list.b3-list--background { counter-reset: h1 h2 h3 h4 h5 h6; } / 通用列表项编号 / .sy__outline ul.b3-list.b3-list--background [data-subtype="h"] > span:first-child::after { position: relative; left: 8px; border-radius: 4px; opacity: 1; font-size: 10px; pointer-events: none; font-family: "Segoe UI"; content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6); } /* 每级递增计数器 */ .sy__outline ul.b3-list.b3-list--background [data-subtype="h1"] { counter-increment: h1; > span:first-child::after { content: counter(h1); color: var(--h1-list-graphic); } } .sy__outline ul.b3-list.b3-list--background [data-subtype="h2"] { counter-increment: h2; > span:first-child::after { content: counter(h1) "." counter(h2); color: var(--h2-list-graphic); } } .sy__outline ul.b3-list.b3-list--background [data-subtype="h3"] { counter-increment: h3; > span:first-child::after { content: counter(h1) "." counter(h2) "." counter(h3); color: var(--h3-list-graphic); } } .sy__outline ul.b3-list.b3-list--background [data-subtype="h4"] { counter-increment: h4; > span:first-child::after { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4); color: var(--h4-list-graphic); } } .sy__outline ul.b3-list.b3-list--background [data-subtype="h5"] { counter-increment: h5; > span:first-child::after { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) "." counter(h5); color: var(--h5-list-graphic); } } .sy__outline ul.b3-list.b3-list--background [data-subtype="h6"] { counter-increment: h6; > span:first-child::after { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6); color: var(--h6-list-graphic); } }
    simuliya
  • shawnkurt 1 评论

    之前我用另外一位佬写的自动编号 https://gitee.com/shaoxiayo/siyuan/blob/master
    今天编写一个长文档发现一个问题,在笔记滚动的时候,应该是动态加载块的,所以往下翻着翻着,可能由于前面的块不加载了,会导致自动的编号发生变动,本来应该是 3 4 5 了,有时候会变成 2 3 4 或者 1 2 3。今天试了一下你的版本,也是这个问题。我不懂技术,但是感觉应该是思源为了性能考虑,动态加载块,导致前面的块被翻过去了,不加载了,编号就会变动。