高亮规则写法速查
高亮规则能写的全部内容:关键词、样式属性、替换为、点击和段落条件,以及从剪贴板导入用的 JSON 格式。
适用版本:Reeden 2.0.0 及以上。这里没列出的写法不支持。
这页是高亮规则页「+」→「用 AI 写规则」时交给 AI 的写法说明。自己照着写 JSON 也行,写好后复制,在「+」→「从剪贴板导入」。完整流程见 用 AI 写规则。
1. 导入格式
从剪贴板导入时,内容是一个 JSON 对象(可以包在 ```json 代码块里):
{
"reedenHighlightRules": 1,
"rules": [
{
"name": "规则名",
"keyword": "要匹配的文字或正则",
"isRegex": true,
"css": "color: {primaryColor};"
}
]
}每条规则的字段:
| 字段 | 必填 | 说明 |
|---|---|---|
name | 是 | 规则名,简短说清用途 |
keyword | 是 | 要匹配的文字;isRegex 为 true 时是正则(见第 2 节)。配合段落条件时可以留空,表示整段 |
isRegex | 否 | 默认 false |
isMultiline | 否 | 正则的多行模式:^ $ 匹配每段的开头结尾;不开时只匹配整章(或整个标题)的开头结尾。默认 false |
caseSensitive | 否 | 区分大小写,默认 false |
target | 否 | 作用在哪:all(默认)、title(只章节标题)、body(只正文) |
css | 否 | 样式(见第 3 节)。可以同时写顶层声明、.名字 { } 块、@keyframes |
replacement | 否 | 把命中的文字换成别的(见第 4 节)。不写表示不替换;写空字符串表示删除命中的文字 |
options | 否 | 段落条件、点击行为等(见第 5 节),是一个 JSON 对象 |
comment | 否 | 给用户看的备注 |
一次可以导入多条规则。导入后的先后不一定和 rules 里的顺序一致,用户要在列表里长按拖动调整。
多条规则命中同一处文字时
规则按列表从上往下生效,同一处文字命中多条规则时:
- 文字效果(
color、font-size、font-family、font-weight、font-style、line-height):逐项比,排在后面的规则写了的为准,没写的仍用前面那条的 - 下划线、波浪线、删除线:可以同时显示
- 背景效果(
background-color、background-image、background简写、渐变、box-shadow、border):排在前面的占住;后面带背景效果的规则只要和它碰到同一个字,在这一处整个不显示,连文字颜色也没有 replacement:排在前面的占住;后面的在重叠处不替换,样式照常叠加。所有replacement都在同一份文字上匹配,匹配不到别的规则替换出来的内容- 关键词匹配的是简繁转换、替换规则、全文翻译(译文也会再过一遍替换规则)、
replacement处理之后的文字:前面换掉的字,后面按原字写的关键词就找不到 target: "title"的规则同时只生效排在最前面的一条;用户开着标题样式时,所有规则在标题上都不生效
写规则时照这几条避开冲突:
- 会和别的规则叠在一起用的单字、标点规则(引号、标点、人名)只用文字效果,不要加背景效果
- 整句、整段的规则(对话气泡、卡片)带背景效果,和单字规则一起用时排在前面
- 规则的生效依赖先后时(比如要排在对话气泡前面),在
comment里写明要放在哪类规则的前面或后面,方便用户导入后拖动
完整说明见 规则生效顺序。
2. 关键词
- 普通文字:照字面匹配。
- 正则(
isRegex: true):JavaScript 风格,Unicode 模式。可以用\p{Han}(汉字)、后行断言(?<=…)、命名分组(?<名字>…)。不支持(?i)这类内联修饰,大小写用caseSensitive。 - 命名分组的名字可以在 CSS 里用
.名字 { }单独设样式,在「替换为」里用$<名字>引用。 - 写在 JSON 里时反斜杠要转义:正则
\d+写成"\\d+"。 - 匹配范围是整章:一章的各段按顺序接成一段文字,段与段之间是一个换行
\n。target: "body"时不含标题,target: "title"时只有标题。.不匹配换行,.*最多走到这一段末尾。[\s\S]、\s、\n和排除型字符类[^…]都能匹配换行,会跨到下一段;只想在段内找时把\n写进排除里,如“[^”\n]+”。- 要每段开头、结尾用
^$,同时写isMultiline: true。 - 从某处一直框到章末(作者有话说、章末附言)可以写
固定开头[\s\S]*,如作者有话要?说[::]?[\s\S]*,内置模板就是这么写的。[\s\S]*前面一定要有固定的开头文字。 - 要框到某个结束标记为止,用段落条件
until(第 5 节),不要写[\s\S]*?结束标记。 - 不要把
[\s\S]*、.*放在关键词开头,不要在一条关键词里写好几个跨段的[\s\S]*,也不要写会大量回溯的嵌套重复(如(a+)+)。长章节里会很慢,太慢时这条规则在本次阅读里会被暂停。
特殊关键词(isRegex: false 时整个关键词写成下面这样):
| 关键词 | 匹配 |
|---|---|
{bookRoles} | 本书角色的名字(配合「替换为」里的角色占位用) |
{vocabulary} | 生词本里的词 |
{translated} | 全文翻译时插入的译文段 |
{translationSource} | 全文翻译时有译文跟着的原文段 |
{titleFirstLine} {titleLastLine} | 章节标题的第一行、最后一行(target 用 title) |
{titleLeadingLine} {titleTrailingLine} | 多行标题里最后一行之前的行、第一行之后的行 |
{titleGroup} | 整个章节标题 |
3. 样式(css 字段)
css 是一段 CSS 文字,有三种内容:
- 顶层声明:
color: red; font-weight: 700;,作用在整处命中上。 - 分块:
.名字 { … },只作用在命中里同名的部分。名字可以是:- 关键词里的命名分组;
- 「替换为」里
<span class="名字">包出来的部分; first-para/last-para:命中跨多段时的第一段、最后一段;v-数字:按数值分档时的档位(第 5 节value)。 另外可以写img、ruby、rt三个标签名,作用在「替换为」插入的图片和注音上。不支持别的选择器(不支持组合、伪类、属性选择器、媒体查询)。
- 关键帧:
@keyframes 名字 { from { … } to { … } },配合animation用。
约束:
- 颜色优先用动态颜色占位符(见下表),会跟随阅读主题变;写死颜色在深色主题下可能看不清。
- 不写
font-family:导入的字体在这里引用不到,规则导入后在编辑器的「字体」里选。 - 图片只能用内置素材
url(builtin:名字)(名字见「背景与图片」里的列表),不支持网址。图库里的图片导入后在编辑器里换。 - 只支持下面列出的属性,其他属性会被忽略。
- 字号、间距优先用
em,跟随用户设置的字号。
可用的属性
下面按用途列出,「说明 / 可选值」里的 | 表示几种写法任选其一。
文字
| 写法 | 说明 / 可选值 |
|---|---|
color | #FFB800 | rgba(255,184,0,.8) | {primaryColor} |
font-weight | normal | 400 | 700 | bold |
reeden-font-bold | 1–9,阅读页的描边加粗等级 |
font-style | normal | italic |
font-size | 14px | 1em | 1.1rem |
font-variant | normal | small-caps |
line-height | 1.5 | 24px |
letter-spacing | 0.3em | 1px | -0.5px |
text-align | left | center | right | justify |
text-indent | 0 | 2em |
text-transform | none | uppercase | lowercase | capitalize |
white-space | normal | nowrap |
text-shadow | 0 1px 2px rgba(0,0,0,.18),可写多层 |
-webkit-text-stroke | 1px #000000,文字描边 |
-webkit-text-stroke-width | 1px | 0.06em |
-webkit-text-stroke-color | #000000 | {primaryColor} |
下划线与着重号
| 写法 | 说明 / 可选值 |
|---|---|
text-decoration | underline wavy {primaryColor} |
text-decoration-line | underline | line-through |
text-decoration-style | solid | wavy | dashed | dotted | double |
text-decoration-color | #22C55E | {primaryColor} | {roleColor} |
text-decoration-thickness | 2px | 0.1em,线的粗细 |
text-underline-offset | 0.2em,下划线离文字的距离 |
text-emphasis | filled dot {accentColor},着重号,也可加 -webkit- 前缀 |
text-emphasis-style | filled dot | open circle | sesame |
text-emphasis-color | {accentColor} |
text-emphasis-position | over | under |
背景与图片
| 写法 | 说明 / 可选值 |
|---|---|
background-color | #FFF3C4 | {primaryColor|a:0.15} |
background | 简写:图片 位置 / 尺寸 平铺 颜色,多层用逗号隔开,颜色写在最后一层 |
background-image | url(builtin:gem),内置装饰素材 |
builtin:名字 | 内置装饰素材,CSS 写 url(builtin:名字),「替换为」写 <img src="名字">:bubble-pink、bubble-white、tag、scroll、torn、banner、note-card、avatar、gem、bell、pin、sakura、boom、quote-left、quote-right、pencil、ornament、flourish、cap-frame、emoji-smile、emoji-cry、emoji-wow、comment-bubble |
background-position | left top | center | right center |
background-repeat | no-repeat | repeat |
background-size | cover | contain | auto | 100% 100% | 1em |
reeden-background-height | half | 50% | full | 100%,半高荧光笔用 half |
渐变与阴影
| 写法 | 说明 / 可选值 |
|---|---|
background-image | linear-gradient(...) | radial-gradient(...) |
background-clip | text,文字渐变用;普通背景渐变不要加 |
-webkit-background-clip | text,文字渐变兼容写法 |
-webkit-text-fill-color | transparent,配合文字渐变 |
box-shadow | 0 2px 8px rgba(15, 23, 42, .20) |
color + background-image + background-clip | 文字渐变组合 |
display:inline + background-image | 行内背景渐变,不要加 background-clip:text |
display:inline + box-shadow | 行内阴影 |
display:block + box-shadow | 块级阴影 |
display:block + background-image | 块级背景渐变 |
边框、圆角与轮廓线
| 写法 | 说明 / 可选值 |
|---|---|
border | 1px solid #22C55E |
border-left / right / top / bottom | 4px solid #94A3B8,单边边框 |
border-width | 1px | 1px 1px 3px |
border-style | solid | dashed | dotted | double |
border-color | {mutedColor} |
border-bottom-width | 3px,单边的 -width / -style / -color 都能写 |
border-radius | 12px | 0.3em | 999px |
border-top-left-radius | 0.2em,四个角可单独写 |
box-decoration-break | clone | slice,行内跨行时每行是否各画完整边框 |
outline | 1.5px solid {primaryColor},不占位置 |
outline-width / style / color | 轮廓线长写 |
outline-offset | 2px |
内边距与外边距
| 写法 | 说明 / 可选值 |
|---|---|
padding | 4px 10px |
padding-left / right / top / bottom | 8px |
margin | 4px 8px。顶层声明里不支持负值(行内块和块级的 margin-top 除外) |
margin-left / right | 8px | auto |
margin-top / bottom | 8px,行内块和块级生效,行内不生效 |
展示与尺寸
| 写法 | 说明 / 可选值 |
|---|---|
display | inline | inline-block | block,留空时自动判断,其他值按 inline。block 只在顶层声明里有效;分块(.名字 { })里写 block 不会另起一行,换行写法见第 6 节「标签压在边框上」 |
float | left | right,用于首字下沉等环绕效果 |
opacity | 0–1 |
width / min-width / max-width | fit-content | 78% | 6em | calc(100% - 7em) |
height / min-height / max-height | 2em |
vertical-align | middle | top | -0.1em,行内块的上下位置 |
宽高、垂直对齐 | 只对行内块和块级生效 |
变换与动画
| 写法 | 说明 / 可选值 |
|---|---|
transform | rotate(-8deg) | scale(1.2) | translateY(-0.1em),只画不排,要配 inline-block |
transform-origin | center | left bottom |
animation | reeden-breathe 2.4s ease-in-out infinite |
animation-name / duration / timing-function / delay | 动画长写 |
animation-iteration-count / direction / fill-mode / play-state | 动画长写 |
内置动画 | reeden-breathe | blink | shake | float | rainbow | pop | spin | fade-in | sweep |
@keyframes 名字 { … } | 自定义动画,只播透明度、颜色、背景色和变换 |
分块样式
| 写法 | 说明 / 可选值 |
|---|---|
.名字 { … } | 只作用于正则命名分组 (?<名字>…) 或「替换为」里 class 同名的部分 |
img / ruby / rt { … } | 「替换为」换出来的图片、注音,注音小字是 rt |
.first-para / .last-para { … } | 命中跨多段时的第一段、最后一段 |
动态颜色占位符
| 写法 | 说明 / 可选值 |
|---|---|
{textColor} | 当前主题文字色 |
{backgroundColor} | 当前主题背景色 |
{primaryColor} | 当前主题强调色 |
{accentColor} | 同 {primaryColor} |
{mutedColor} | 次要文字色,介于文字色和背景色之间 |
{roleColor} | 命中角色的颜色,没有角色颜色时回退到文字色或强调色 |
{primaryColor|a:0.15} | 任一占位符加 |a:透明度,做浅色底 |
其余支持的属性
写法同标准 CSS: background-origin、padding-left、padding-right、padding-top、padding-bottom、margin-left、margin-right、margin-top、margin-bottom、border-left、border-right、border-top、border-bottom、outline-width、outline-style、outline-color、-webkit-text-emphasis、-webkit-text-emphasis-style、-webkit-text-emphasis-color、-webkit-text-emphasis-position、-webkit-box-decoration-break、width、min-width、max-width、height、min-height、max-height、border-top-width、border-right-width、border-left-width、border-top-style、border-right-style、border-bottom-style、border-left-style、border-top-color、border-right-color、border-bottom-color、border-left-color、border-top-right-radius、border-bottom-right-radius、border-bottom-left-radius、animation-name、animation-duration、animation-timing-function、animation-delay、animation-iteration-count、animation-direction、animation-fill-mode、animation-play-state
4. 替换为(replacement 字段)
把命中的文字换成另一段,可以夹标签。替换后的文字会作为正文,参与复制、搜索、听书和导出。只想改样式时不要写 replacement。
- 引用:
$0整处命中,$1第几个分组,$<名字>命名分组,$$写一个$。 - 占位:
{roleName}{roleColor}{roleAvatar}(关键词是{bookRoles}时可用)、{roleIntro}(只在弹出卡片里可用)。 - 文字转换:
{1|num}(中文数字转阿拉伯数字)、{1|cn}(阿拉伯数字转中文)、upper、lower、full(转全角)、half(转半角)、head:N(取前 N 个字),可以串起来:{name|half|upper}。花括号里的1、name是分组序号或名字。 - 标签只有四种,不支持
style属性(样式写在css里):<span class="名字">…</span>:包一段文字,CSS 里用.名字 { }设样式;<img src="…" size="text" alt="…" class="名字">:插图,src写内置素材名或{roleAvatar},size是text(跟字一样高,默认)、line(占满一行宽)、original(原图大小),alt是图片在正文里算作的文字;<ruby>文字<rt>注音</rt></ruby>:注音。
- 转义:
<>&";任意字符用&#数字;,换行写 。 - 替换为里的空格、换行都会原样出现在正文里,不要为了好看加缩进或换行。
5. 扩展配置(options 字段)
options 是一个 JSON 对象,下面的键都可以不写:
{
"match": { "scope": "paragraph", "startsWith": "作者有话说" },
"tap": { "action": "fold", "summary": "作者有话说…" },
"hits": true,
"scene": "reader",
"preview": "示例文字"
}match:段落条件
先按条件选段落,再在选中的段落里找关键词(关键词留空就是整段)。各条件同时满足才算。章节标题、空段、只有插图的段不算一段。
| 键 | 说明 |
|---|---|
scope | 写 "paragraph":段里有一处命中,整段都套样式 |
parity | "odd" / "even":只要奇数段 / 偶数段 |
nth | 第几段的列表,从 1 起;负数是倒数,如 [1, -1] 是首段和末段 |
every | 每隔几段取一段(第 1、1+N、1+2N… 段) |
minLength / maxLength | 段落字数范围(不算空白) |
startsWith / endsWith | 段落开头 / 结尾是这段文字(按字面,不是正则) |
until | 正则:命中一直延伸到出现它的那一段为止,用来框住「从某段到某段」 |
untilMissing | until 找不到时:"chapterEnd"(到章末,默认)或 "none"(不算命中) |
exclude | 排除列表:"quote"(在引号里的)、"title"(在标题里的),其余按正则匹配段落文字 |
scope: "paragraph" 和 replacement 一起用时,命中的是整段,「替换为」会把整段文字换掉:
- 段里的正文没写进「替换为」就不显示。要保留正文,在「替换为」里用
$0(整段文字),或者让关键词从段首匹配到段尾(^…$,配isMultiline: true),再用分组把正文写回去。 - 关键词没有从段首匹配到段尾时取不到分组,
$1、$<名字>都是空的。 - 只想给整段加底色、边框,不改文字时不要写
replacement。想在段首加标签、正文照常显示,用第 6 节「两栏卡片」的写法,不用scope。
tap:点击行为
| 键 | 说明 |
|---|---|
action | "reveal":默认遮住,点一下显示;"fold":折成一行摘要,点开看全文;"popup":点一下弹出卡片 |
summary | fold 时折叠后显示的摘要,写法同「替换为」;不写时用首段前 12 个字 |
popup | popup 时卡片内容,写法同「替换为」 |
card | popup 卡片样式:"text"(标题 + 文字,默认)、"comments"(每行一条「名字:内容」,显示成段评列表) |
on | 只在点到这个 class 的部分(「替换为」里 <span class="…"> 包的)时响应;不写时点到整处命中都响应 |
reveal 的「遮住」效果在 css 里写(比如文字颜色和背景同色)。
其他
| 键 | 说明 |
|---|---|
value | 按数值分档:{ "group": "分组名", "min": 0, "max": 100, "step": 10 },把命名分组里的数字按 step 分档,命中带上 v-档位 的 class,CSS 写 .v-70 { … } |
hits | true:点到命中时显示「本章第几处 / 全书几处」和上一处、下一处 |
scene | 只对带 replacement 的规则有用:"all"(默认)、"reader"(只在阅读时替换)、"audiobook"(只在听书时替换) |
preview | 编辑器里预览用的示例文字,能命中关键词最好;多段用 \n 分开 |
6. 示例
{
"reedenHighlightRules": 1,
"rules": [
{
"name": "对话淡底",
"keyword": "“[^”\\n]+”",
"isRegex": true,
"target": "body",
"css": "background-color: {primaryColor|a:0.1}; border-radius: 0.2em;"
},
{
"name": "金额加粗",
"keyword": "(?<num>\\d+(?:\\.\\d+)?)(?<unit>万|亿)?元",
"isRegex": true,
"css": "color: {primaryColor};\n.num { font-weight: 700; }\n.unit { font-size: 0.85em; }"
},
{
"name": "生僻词注音",
"keyword": "魑魅魍魉",
"replacement": "<ruby>$0<rt>chī mèi wǎng liǎng</rt></ruby>",
"css": "rt { color: {mutedColor}; }"
},
{
"name": "折叠作者有话说",
"keyword": "",
"css": "color: {mutedColor}; font-size: 0.9em;",
"options": {
"match": { "scope": "paragraph", "startsWith": "作者有话说" },
"tap": { "action": "fold", "summary": "作者有话说…" }
}
}
]
}两栏卡片
左边一栏标签、右边一栏正文的卡片(比如「本章概要:……」这种段落)。要点:
- 不写
scope: "paragraph"。关键词从段首匹配到段尾(^…$,isMultiline: true),把要显示的正文抓进命名分组。 - 顶层声明写
display: block,整段画成一张卡片。 - 「替换为」里放两个
<span>:左栏是标签,右栏用$<名字>把正文写回去。 - 两栏都写
display: inline-block; vertical-align: middle;。左栏给固定宽度,右栏宽度用calc(100% - 左栏宽度 - 间距),两栏就排在同一行;左栏的border-right当中间的竖线。 em按这一栏自己的字号算。右栏写了font-size: 0.9em时,它的1em只有正文的 0.9 倍,calc里要多减一些(左栏 5em、间距 1em 时写calc(100% - 7em))。两栏加起来超过一行,右栏就会掉到标签下面另起一行。- 手机上一行只有 18–20 个字。左栏控制在 5em 左右,标签的中英文上下排(各写
display: inline-block; width: 100%;),不要横排成一长条,否则右栏一行只剩几个字。
{
"reedenHighlightRules": 1,
"rules": [
{
"name": "章节概要卡片",
"keyword": "^(?<label>本章概要|内容提要)[::]\\s*(?<body>.+)$",
"isRegex": true,
"isMultiline": true,
"target": "body",
"css": "display: block; text-indent: 0; margin: 0.9em 0; padding: 0.9em 1em; background-color: {primaryColor|a:0.06}; border-left: 3px solid {primaryColor}; border-radius: 0 6px 6px 0;\n.tag { display: inline-block; width: 5em; vertical-align: middle; text-align: center; border-right: 1px solid {mutedColor|a:0.4}; }\n.label { display: inline-block; width: 100%; color: {primaryColor}; font-weight: 700; letter-spacing: 0.1em; }\n.en { display: inline-block; width: 100%; color: {mutedColor}; font-size: 0.55em; letter-spacing: 0.24em; }\n.txt { display: inline-block; width: calc(100% - 7em); margin-left: 1em; vertical-align: middle; font-size: 0.9em; line-height: 1.75; }",
"replacement": "<span class=\"tag\"><span class=\"label\">$<label></span><span class=\"en\">SUMMARY</span></span><span class=\"txt\">$<body></span>",
"options": { "preview": "本章概要:一场意外的更新,揭开了沉寂已久的隐藏任务。" }
}
]
}标签压在边框上、正文另起一行
一个框住整段的边框,左上角一个小标签压在边框线上,正文从标签下面一行开始(比如「作者有话说:……」)。要点:
- 顶层声明写
display: block和边框,padding-top写 0:标签会往上挪到边框线上,它原来的位置还占着,就当成正文上面的留白。 - 标签写
display: inline-block; transform: translateY(-0.6em);。transform只改画的位置、不改排版,所以挪上去以后不会把正文顶下来。 - 标签再写
margin-right: 100%; white-space: nowrap;,把后面的文字挤到下一行。分块里写display: block不会另起一行。
{
"reedenHighlightRules": 1,
"rules": [
{
"name": "作者有话说标签框",
"keyword": "^(?<label>作者有话说|作者的话)[::]?\\s*(?<body>.+)$",
"isRegex": true,
"isMultiline": true,
"target": "body",
"css": "display: block; text-indent: 0; margin: 1.2em 0 0.6em; padding: 0 0.9em 0.6em; border: 1.5px solid {primaryColor|a:0.45}; border-radius: 0.6em;\n.tag { display: inline-block; transform: translateY(-0.6em); margin-right: 100%; white-space: nowrap; padding: 0.1em 0.7em; font-size: 0.85em; font-weight: 700; letter-spacing: 0.1em; color: {backgroundColor}; background-color: {primaryColor}; border-radius: 0.35em; }",
"replacement": "<span class=\"tag\">$<label></span>$<body>",
"options": { "preview": "作者有话说:今天加班到很晚,只能更一章啦。明天补上,谢谢大家的月票!" }
}
]
}7. 常见错误
- JSON 解析不了:反斜杠、引号没转义;
reedenHighlightRules漏写。 [\s\S]*前面没有固定的开头文字,或者写成[\s\S]*?结束标记去框范围,长章节里很慢、被暂停。框范围用until(第 5 节)。- 想按每段的开头结尾写
^…$,却没写isMultiline: true,只能匹配整章的开头结尾。 - CSS 用了没列出的属性或选择器,导入后被忽略。
.名字 { }的名字在关键词的命名分组和「替换为」的 class 里都找不到,这块样式不会生效。- 只想改样式却写了
replacement,正文文字被替换。 scope: "paragraph"又写了replacement,整段正文被换掉,$1、$<名字>是空的(见第 5 节段落条件)。- 分块里写
display: block想让后面的文字换行,结果还是接在同一行(见第 6 节「标签压在边框上」)。
Reeden