kramdown 脚注语法
kramdown 脚注语法
根据 kramdown 官方语法文档中的 Footnotes 部分整理。
原文:https://kramdown.gettalong.org/syntax.html#footnotes
kramdown 的脚注写法和“引用式链接”很相似:
- 在正文中放置脚注标记;
- 在文档任意位置定义脚注内容;
- 转换为 HTML 时,正文中的脚注标记会变成可点击编号;
- 实际脚注内容通常统一输出在文档末尾。
脚注不是原始 Markdown 的标准语法,它来自 PHP Markdown Extra 一类的扩展语法。
1. 最基本的脚注
正文中这样引用脚注:
kramdown 是一个 Ruby 编写的 Markdown 解析器[^1]。
然后在文档其他位置定义脚注:
[^1]: kramdown 可以把 Markdown 转换为 HTML 等格式。
完整示例:
kramdown 是一个 Ruby 编写的 Markdown 解析器[^1]。
[^1]: kramdown 可以把 Markdown 转换为 HTML 等格式。
大致显示为:
kramdown 是一个 Ruby 编写的 Markdown 解析器¹。
1. kramdown 可以把 Markdown 转换为 HTML 等格式。
2. 脚注标记的格式
脚注标记使用方括号,内部以 ^ 开头:
[^1]
也可以使用有意义的名称:
[^source]
[^note]
[^ruby-version]
脚注名称应满足以下规则:
- 必须以
^开头; ^后第一个字符必须是字母、数字或下划线一类的单词字符;- 后续可以继续使用字母、数字、下划线或连字符;
- 名称只负责关联正文标记和脚注定义;
- 页面上最终显示的脚注编号,不一定等于名称中的数字。
正确示例:
[^1]
[^note]
[^source-1]
[^ruby_guide]
不建议或可能无效的写法:
[^]
[^-note]
[^脚 注]
为了兼容性和可维护性,建议使用英文小写字母、数字和连字符:
[^install-note]
3. 脚注编号由出现顺序决定
脚注最终显示为第几个,不是由脚注名称决定,而是由脚注标记首次出现在正文中的顺序决定。
例如:
第一个出现的脚注[^99]。
第二个出现的脚注[^1]。
[^1]: 虽然名称是 1,但它第二个出现。
[^99]: 虽然名称是 99,但它第一个出现。
最终通常会显示为:
第一个出现的脚注¹。
第二个出现的脚注²。
因此,脚注名称最好用于表达含义,而不是依赖它控制编号:
正文内容[^source]。
补充说明[^compatibility]。
4. 脚注定义可以放在文档任意位置
脚注定义不需要紧跟在脚注标记后面。
下面三种位置通常都可以。
放在段落后面
正文内容[^note]。
[^note]: 这是脚注。
放在章节结尾
正文内容[^note]。
## 下一节
其他内容。
[^note]: 这是前面正文引用的脚注。
统一放在文档末尾
第一处说明[^first]。
第二处说明[^second]。
---
[^first]: 第一个脚注。
[^second]: 第二个脚注。
为了方便维护,推荐将脚注统一放在所属章节末尾,或者整篇文档末尾。
5. 脚注定义的基本结构
脚注定义由以下几部分组成:
脚注名称 + 冒号 + 脚注内容
基本格式:
[^name]: 脚注内容
冒号后可以有一个或多个空格:
[^name]:脚注内容
[^name]: 脚注内容
[^name]: 脚注内容
不过为了可读性,建议统一保留一个空格:
[^name]: 脚注内容
脚注定义前最多可以缩进三个空格:
[^note]: 这仍然是脚注定义。
不建议主动缩进,最好顶格写:
[^note]: 推荐写法。
6. 脚注内容支持 Markdown
脚注内容会按块级 Markdown 处理,所以其中可以使用:
- 强调;
- 链接;
- 行内代码;
- 多个段落;
- 引用;
- 列表;
- 代码块;
- 其他块级元素。
强调和行内代码
正文[^format]。
[^format]: 这里可以使用 **粗体**、*斜体* 和 `行内代码`。
链接
正文[^website]。
[^website]: 参见 [kramdown 官网](https://kramdown.gettalong.org/)。
多个行内元素
正文[^detail]。
[^detail]: `auto_ids` 选项用于控制标题 ID,默认值通常是 `true`。
7. 多行脚注
脚注内容较长时,可以继续写到下一行。
后续行通常需要缩进四个空格或一个 Tab:
正文中引用一个较长的脚注[^long]。
[^long]: 这是脚注第一行。
这是脚注第二行。
这是脚注第三行。
为了避免不同解析器对连续文本的处理差异,推荐把长文本整理得更明确:
正文中引用一个较长的脚注[^long]。
[^long]: 这是脚注第一行,
这是脚注第二行,
这是脚注第三行。
如果只是普通段落,不必为了换行效果强行拆行。也可以写成一行:
[^long]: 这是一个内容比较长、但仍然作为单个段落处理的脚注。
8. 脚注中包含多个段落
第二个段落及后续内容应整体缩进四个空格:
正文[^multi]。
[^multi]: 这是脚注的第一段。
这是脚注的第二段。
这是脚注的第三段。
完整示例:
kramdown 的脚注可以包含多个段落[^explain]。
[^explain]: 第一段用于给出简要说明。
第二段可以继续补充技术细节。
第三段还可以给出注意事项。
空行本身也应位于脚注定义结构中。最稳妥的方式是:第二段开始后,所有内容都缩进四个空格。
9. 脚注中包含引用
脚注内容可以以引用开头:
正文[^quote]。
[^quote]:
> 这是脚注中的引用。
也可以先写普通段落,再放引用:
正文[^quote]。
[^quote]: 这是脚注的说明。
> 这是脚注中的引用内容。
注意:当脚注定义的第一行冒号后没有正文时,后续块级内容需要正确缩进。
10. 脚注中包含列表
正文[^list]。
[^list]: 注意以下几点:
- 第一项;
- 第二项;
- 第三项。
有序列表:
正文[^steps]。
[^steps]: 操作步骤:
1. 安装 kramdown;
2. 创建 Markdown 文件;
3. 转换为 HTML。
11. 脚注中包含代码块
代码块本身需要四个空格缩进,而脚注内容已经需要四个空格,因此代码块通常总共需要八个空格。
正文[^code]。
[^code]:
puts "这是脚注中的代码块"
更完整的示例:
Ruby 中可以这样调用 kramdown[^ruby-code]。
[^ruby-code]:
require "kramdown"
source = "# Hello"
puts Kramdown::Document.new(source).to_html
这里代码行前相当于有八个空格:
脚注内容缩进 4 个空格
+
代码块额外缩进 4 个空格
=
总共 8 个空格
为什么下面不是代码块
[^note]: 这通常仍然只是普通文字
冒号后第一行开头的额外空格会被剥离,所以不能单靠冒号后增加很多空格,把第一行变成代码块。
需要让代码块成为脚注的第一个元素时,推荐写成:
[^note]:
puts "真正的代码块"
也就是:
- 脚注定义行在冒号后结束;
- 下一行缩进八个空格;
- 代码块从下一行开始。
12. 同一个脚注可以引用多次
相同名称的脚注标记都会指向同一个脚注定义:
第一次引用[^same]。
中间还有其他内容。
第二次引用同一个脚注[^same]。
[^same]: 两个标记都指向这个脚注。
它们不会生成两个不同的脚注内容。
实际 HTML 中是否为每个引用生成独立返回链接,取决于转换器的实现。
13. 没有定义的脚注不会正常生成
如果正文中出现脚注标记,但文档中没有对应定义:
这里有一个没有定义的脚注[^missing]。
kramdown 不会把它正常转换为脚注链接。
因此发布前应检查:
- 正文中的每个脚注标记都有定义;
- 脚注名称完全一致;
- 没有拼写错误;
- 大小写是否保持一致。
错误示例:
正文[^Source]。
[^source]: 名称大小写不一致。
推荐:
正文[^source]。
[^source]: 名称完全一致。
14. 未被引用的脚注定义会被忽略
如果只定义脚注,却没有在正文中引用:
正文没有引用任何脚注。
[^unused]: 这个脚注没有被引用。
这个脚注定义通常不会出现在最终脚注区域中。
这有利于避免输出无用脚注,但也可能隐藏维护错误。删除正文内容后,应检查是否留下未使用的脚注定义。
15. 同名定义只保留最后一个
如果同一个脚注名称被定义多次,通常只有最后一个定义生效:
正文[^note]。
[^note]: 第一个定义。
[^note]: 第二个定义。
最终使用:
第二个定义。
不要故意依赖覆盖行为。推荐确保每个脚注名称只定义一次。
16. 脚注不能放在链接文字中
下面这种写法不允许:
[链接文字中包含脚注[^note]](https://example.com)
原因是脚注标记在 HTML 中会被转换为链接,而普通 Markdown 链接本身也是链接,最终会形成“链接嵌套链接”。
HTML 不允许这种结构:
<a href="https://example.com">
链接文字
<a href="#fn-note">1</a>
</a>
正确做法是把脚注放到链接后面:
[示例网站](https://example.com)[^note]
[^note]: 这是对这个链接的补充说明。
或者放在完整句子末尾:
可以访问 [示例网站](https://example.com) 查看详情[^note]。
17. 脚注标记上的属性会被忽略
一般行内元素可以通过 IAL 添加属性:
*文字*{: .important}
但脚注标记后附加的行内属性通常不会生效:
正文[^note]{: .important}。
[^note]: 脚注内容。
不要依赖这种方式给脚注编号添加类或其他属性。
需要自定义脚注样式时,应通过:
- HTML 转换器输出结构;
- 主题 CSS;
- kramdown 相关配置;
- 自定义转换器。
18. 可以给脚注定义附加块级属性
虽然脚注定义本身属于“非内容块级元素”,但可以对定义附加块级属性列表。
正文[^note]。
[^note]: 脚注内容。
{: .legal-note}
属性最终如何使用,取决于具体转换器。
有些转换器可能把属性用于生成的脚注元素,有些转换器可能忽略部分属性。因此不能假定所有输出格式表现一致。
19. 脚注内容最终通常位于文档末尾
无论脚注定义写在文档哪里,引用到的脚注内容通常都会统一输出在最终文档的脚注区域。
源文件:
第一段正文[^first]。
[^first]: 第一个脚注定义。
第二段正文[^second]。
[^second]: 第二个脚注定义。
最终页面结构通常类似:
<p>第一段正文<sup>...</sup>。</p>
<p>第二段正文<sup>...</sup>。</p>
<div class="footnotes">
<ol>
<li>第一个脚注定义。</li>
<li>第二个脚注定义。</li>
</ol>
</div>
具体标签、类名、ID 和返回链接由 HTML 转换器决定。
20. 推荐命名方式
不推荐只使用连续数字:
[^1]
[^2]
[^3]
数字较少时可以使用,但文档变长或调整顺序后不容易维护。
推荐使用有意义的名称:
[^source]
[^version]
[^compatibility]
[^security-note]
示例:
kramdown 的语法与原始 Markdown 并不完全一致[^compatibility]。
[^compatibility]: kramdown 为了让解析规则更明确,对部分边界行为作了调整。
名称不会决定显示编号,因此可以放心使用描述性标识。
21. 中文标点放在哪里
脚注标记和句末标点的位置会影响阅读效果。
说明整句话
推荐把脚注放在句号前:
kramdown 使用 Ruby 编写[^source]。
显示时类似:
kramdown 使用 Ruby 编写¹。
只说明某个词
脚注紧跟在该词后:
kramdown[^name] 是一个 Markdown 解析器。
不建议把脚注和说明对象隔得太远
不推荐:
kramdown 是一个 Markdown 解析器。[^source]
这种写法不是不能解析,但在中文排版中,脚注通常紧跟被说明的文字或句子,并放在句末标点之前。
22. 一个脚注包含完整说明
kramdown 的默认输入语法并不等同于 GitHub Flavored Markdown[^gfm]。
[^gfm]: 如果需要更接近 GitHub 的解析行为,可以安装
`kramdown-parser-gfm`,并将输入解析器设置为 `GFM`:
gem install kramdown-parser-gfm
Jekyll 配置示例:
kramdown:
input: GFM
这个示例同时包含:
- 普通段落;
- 行内代码;
- 代码块;
- YAML 配置。
23. 法律文档脚注示例
本法自2026年1月1日起施行[^effective-date]。
[^effective-date]: “施行日期”表示法律规范开始发生效力的日期。
带来源链接:
该条文后来经过修订[^amendment]。
[^amendment]: 修订信息参见
[全国人民代表大会官方网站](https://www.npc.gov.cn/)。
带多段解释:
本条所称“主管部门”应结合上下文确定[^authority]。
[^authority]: 第一,应检查本法总则或者附则中是否存在专门定义。
第二,如果本法没有定义,应结合相关行政法规和部门职责规定判断。
第三,不宜仅根据部门名称作字面推断。
带代码化来源信息:
该版本来源于正式发布文本[^metadata]。
[^metadata]: 文档元数据:
source_type: official
publication_date: 2026-01-01
status: effective
24. 常见错误
错误一:忘记写 ^
错误:
正文[note]。
[note]: 脚注内容。
这是引用式链接语法,不是脚注。
正确:
正文[^note]。
[^note]: 脚注内容。
错误二:定义名称不一致
错误:
正文[^note-one]。
[^note-1]: 脚注内容。
正确:
正文[^note-one]。
[^note-one]: 脚注内容。
错误三:多段脚注没有缩进
错误:
[^note]: 第一段。
第二段。
这里“第二段”会成为普通正文,不属于脚注。
正确:
[^note]: 第一段。
第二段。
错误四:代码块缩进不足
错误:
[^code]:
puts "Hello"
这通常只是脚注中的普通文本,因为四个空格主要用于进入脚注内容。
正确:
[^code]:
puts "Hello"
错误五:把脚注放在链接文字内部
错误:
[网站[^note]](https://example.com)
正确:
[网站](https://example.com)[^note]
错误六:重复定义
不推荐:
[^note]: 旧内容。
[^note]: 新内容。
推荐只保留一个定义:
[^note]: 新内容。
25. 最小可复制模板
正文中需要补充说明的内容[^note]。
[^note]: 这里写脚注内容。
多段脚注模板:
正文中需要详细说明的内容[^detail]。
[^detail]: 这是脚注第一段。
这是脚注第二段。
- 可以包含列表;
- 也可以包含其他块级内容。
含代码块的模板:
正文中引用代码示例[^code]。
[^code]:
puts "Hello"
26. 完整演示
---
layout: default
title: kramdown 脚注示例
---
# kramdown 脚注示例
kramdown 是一个 Ruby 编写的 Markdown 超集解析器[^intro]。
它的脚注名称不决定最终编号[^numbering],同一个脚注也可以引用多次[^repeat]。
这里再次引用同一个脚注[^repeat]。
## 示例代码
Ruby 中可以这样转换 Markdown[^code-example]。
[^intro]: kramdown 可以把 Markdown 转换为 HTML、LaTeX 等格式。
[^numbering]: 脚注编号按照脚注标记首次出现在正文中的顺序生成。
[^repeat]: 名称相同的脚注标记指向同一个脚注定义。
[^code-example]:
require "kramdown"
source = "# Hello"
html = Kramdown::Document.new(source).to_html
puts html
27. 使用建议
- 使用有意义的脚注名称,例如
[^source],不要完全依赖数字。 - 将脚注定义统一放在章节末尾或文档末尾。
- 脚注标记紧跟需要说明的文字。
- 中文句子中通常将脚注标记放在句末标点前。
- 多段脚注的后续段落统一缩进四个空格。
- 脚注中的缩进代码块通常需要八个空格。
- 不要在普通链接的链接文字中嵌套脚注。
- 发布前检查未定义脚注、未引用脚注和重复定义。
- 不要依赖脚注标记后的行内属性列表。
- 不同转换器可能生成不同 HTML 结构,应结合最终输出测试样式。
28. 官方规则摘要
kramdown 脚注的核心规则可以概括为:
正文标记:
[^name]
脚注定义:
[^name]: 内容
并且:
- 脚注定义可以放在文档任意位置;
- 显示编号由正文中首次出现的顺序决定;
- 同名标记引用同一个脚注;
- 未定义的标记不会生成有效脚注;
- 未被引用的定义会被忽略;
- 重复定义时最后一个生效;
- 脚注内容可以包含块级 Markdown;
- 脚注不能嵌套在链接文字中;
- 脚注标记上的行内属性会被忽略;
- 引用到的脚注通常统一输出到文档末尾。
目录
- 1. 最基本的脚注
- 2. 脚注标记的格式
- 3. 脚注编号由出现顺序决定
- 4. 脚注定义可以放在文档任意位置
- 5. 脚注定义的基本结构
- 6. 脚注内容支持 Markdown
- 7. 多行脚注
- 8. 脚注中包含多个段落
- 9. 脚注中包含引用
- 10. 脚注中包含列表
- 11. 脚注中包含代码块
- 12. 同一个脚注可以引用多次
- 13. 没有定义的脚注不会正常生成
- 14. 未被引用的脚注定义会被忽略
- 15. 同名定义只保留最后一个
- 16. 脚注不能放在链接文字中
- 17. 脚注标记上的属性会被忽略
- 18. 可以给脚注定义附加块级属性
- 19. 脚注内容最终通常位于文档末尾
- 20. 推荐命名方式
- 21. 中文标点放在哪里
- 22. 一个脚注包含完整说明
- 23. 法律文档脚注示例
- 24. 常见错误
- 25. 最小可复制模板
- 26. 完整演示
- 27. 使用建议
- 28. 官方规则摘要