六個 ATX 階層

重點摘要
  • 標題共有六階,用一到六個井字號書寫,最後一個井字號後面的空格是必要的。
  • 一份文件只放一個 H1,也不要跳階,因為螢幕閱讀器與爬蟲都把標題讀成一棵樹。
  • 每個標題都會自動拿到錨點 ID:轉小寫、移除標點、空格換成連字號。
  • 緊貼文字底下的一排連字號不是分隔線 - 它會把上面那行變成 H2。

標題的寫法是:行首放一到六個井字號、一個空格,然後是文字。這叫 ATX 語法,也是現在幾乎所有人採用的寫法:

# 發佈說明
## 版本 2.4
### 錯誤修正
#### 解析器
##### 邊界情況
###### 已知退步

這六行分別變成 <h1><h6>。HTML 只到第六階,所以七個井字號不是標題,會原樣顯示成文字。

井字號後面的空格是必要的

這是標題最常見的失敗原因。#發佈說明 是段落,# 發佈說明 才是標題。CommonMark 與 GitHub 風格 Markdown 都要求最後一個井字號後面有空格或 Tab。最初的 Markdown.pl 對這點很寬鬆,少數老舊渲染器至今仍然如此,舊檔案才會在某個工具裡正常、換一個就壞掉。

結尾井字號、行首空白與跳脫

標題後面可以再加一串井字號收尾。它純屬裝飾,長度隨意,不必跟開頭一致:

## 版本 2.4 ##
## 版本 2.4 ###########

兩行都得到 <h2>版本 2.4</h2>,但結尾那串前面一定要有空格。寫成 ## 版本 2.4##,標題文字就會變成「版本 2.4##」。

開頭井字號前面最多三個空白會被忽略,第四個就讓整行變成縮排式程式碼區塊,井字號原樣呈現。想讓一行真的以井字號開頭就跳脫它:\# 這不是標題。只想快速查六個階層的樣子,看一頁式語法參考就夠了。

小提醒:標題怎麼都渲染不出來時,先數行首的空格再查其他原因。四個空格就是縮排式程式碼區塊,而行首空白在多數編輯器裡完全看不出來。

插圖:六行由上而下堆疊的文字,前面的井字號愈來愈多,字級愈來愈小

Setext 標題與破折號底線的陷阱

Markdown 還有一種更古老的第二語法:在文字底下畫一排等號代表第一階,畫一排連字號代表第二階。

發佈說明
=============

版本 2.4
-----------

這叫 Setext 標題。底線一個字元就夠了,============ 意思相同,長度也不必跟標題文字對齊。它的標題文字甚至可以跨多行,這是 ATX 做不到的。

它只有兩個階層

Setext 沒有對應 H3 到 H6 的寫法,用它起頭的文件只要需要第三階就得改回井字號。包含 Prettier 在內的多數格式化工具也都會把它改寫成 ATX。新文件請一律用井字號。

人人都踩過的陷阱

因為文字底下那排連字號會產生 H2,下面這段的結果跟外觀完全不同:

部署完成後執行冒煙測試。
---
下一段從這裡開始。

作者想要的是一條分隔線,實際得到的卻是一個裝著整句話的 H2。加一行空行就修好了,連字號與上方文字切開後才會被讀成分隔線:

部署完成後執行冒煙測試。

---

下一段從這裡開始。

兩個習慣可以讓它不再發生:

  • 分隔線一律寫成 ***___,這兩種永遠不會被誤認成標題底線。
  • 記得 Jekyll、Hugo 與 Astro 的 YAML front matter 同樣用 --- 包起來,而它只有在檔案最開頭才算 front matter。

階層規則:只用一個 H1,也不要跳階

標題階層不是字級大小,而是一棵巢狀結構樹,輔助科技與搜尋引擎都這樣讀它。

一份文件只用一個 H1

H1 是這一頁的標題,代表整份文件在講什麼。放兩個 H1 等於告訴螢幕閱讀器這頁裝了兩份文件。很多靜態網站產生器已經會從 front matter 的標題輸出 H1,這時 Markdown 內文應該從 H2 開始,動手前先看一眼渲染出來的 HTML。

不要跳過階層

因為覺得字比較小好看,就從 ## 直接跳到 ####,會把大綱弄壞。代價出現在三個地方:

  • 螢幕閱讀器導覽。使用者靠叫出標題清單瀏覽頁面,也常用「跳到同階的下一個標題」移動;少掉一階,那個 H4 就沒有父標題了。
  • 無障礙檢測。自動化工具會直接標記它,它也違反 WCAG 1.3.1「資訊與關聯性」。
  • 搜尋。爬蟲用標題樹判斷哪一段回答哪一個問題,精選摘要也仰賴這個結構。

字級不順眼就用 CSS 調整,階層本身請保持誠實。把草稿丟進目錄產生器跑一次,就會看到自己實際寫出來的大綱,缺的階層也一併現形。

MarkdownHTML典型用途標題範例產生的 ID
# <h1>頁面標題,只用一次# Markdown Style Guide#markdown-style-guide
## <h2>主要章節,目錄採用的階層## Getting Started#getting-started
### <h3>章節內的子主題### Install the CLI#install-the-cli
#### <h4>API 條目、個別選項說明、邊界情況#### Windows notes#windows-notes
##### <h5>少見,只出現在很深的參考文件##### Return values#return-values
###### <h6>更少見,通常代表這頁該拆開了###### Legacy flags#legacy-flags
文字 + ===<h1>Setext 第一階,舊檔案才會看到Release Notes#release-notes
文字 + ---<h2>Setext 第二階,舊檔案才會看到Version 2.4#version-24

標題 ID 與錨點連結是怎麼產生的

在 GitHub、GitLab 與絕大多數文件網站上,每個標題都會被默默加上一個 id,這正是你能連到一份長 README 中段的原因。這個 slug 由標題文字經過一段簡短流程轉出來:

## Setting Up the API Key (v2.1)!

1. 去掉行內語法  -> Setting Up the API Key (v2.1)!
2. 全部轉小寫    -> setting up the api key (v2.1)!
3. 移除標點      -> setting up the api key v21
4. 空格換連字號  -> setting-up-the-api-key-v21

所以錨點是 #setting-up-the-api-key-v21。注意 2.1 的下場:小數點屬於標點,被移除後只剩 v21,憑記憶猜正是錨點壞掉的典型過程。行內格式同樣會被剝掉,所以 ### The **important** part 得到的是 #the-important-part

重複的標題會加上數字後綴

## Installation      -> #installation
## Configuration     -> #configuration
## Installation      -> #installation-1
## Installation      -> #installation-2

這就是「上個月還好好的連結,現在跳到錯誤章節」的原因:有人在前面新增了同名標題,後面每個後綴整批位移一位。重複的標題請加上足以區分的字眼

連到某個標題

開始之前請先看[安裝步驟](#setting-up-the-api-key-v21)。

[安裝步驟](docs/setup.md#setting-up-the-api-key-v21)

相對路徑、參考式連結與跳脫規則都在連結參考裡。跳轉連結沒反應時,不要花時間推敲 slug 規則:打開渲染後的頁面,把滑鼠移到標題上,直接從連結圖示複製錨點。

小提醒:想把這些 ID 一次變成整份目錄,把文件貼進目錄產生器就好。它套用的正是上面這套 slug 規則,產出的錨點就是渲染器會建立的那些。

手動把 ID 釘死

Python-Markdown、kramdown、Pandoc 與 MkDocs 還允許你在標題尾端加 {#api-key} 把 ID 釘死,GitHub 則會把大括號原樣印出來。在 GitHub 上請改用標題正上方的 <a id="api-key"></a>,因為 Markdown 本來就接受原始 HTML

插圖:一個標題被送進小機器,另一端吐出全小寫、用連字號串起來的網址片段

引言、清單與段落裡的標題

標題是區塊元素,任何能容納區塊的地方都放得下它。

放在引言區塊裡

> ## 停用通知
>
> v1 端點將於三月停止回應。

每一行前面都要加大於符號,標題那行也不例外。它一樣會拿到 ID,也一樣會出現在自動產生的目錄裡。巢狀引言與建立在引言之上的提示框語法,請見引言區塊參考

放在清單項目裡

把標題縮排到項目的文字欄位,上面留一行空行:

1. 準備機器

   ### 系統需求

   Node 20 以上,以及 2 GB 可用磁碟空間。

2. 執行安裝程式

這樣寫得出來,但請三思。清單裡的標題會在大綱中佔一個位置,視覺上卻像個子項目,而且會讓清單變成鬆散清單,改變每個項目的行距。那個位置通常用粗體更合適,見粗體與斜體參考;巢狀內容的縮排規則則在清單參考

上面沒有空行的標題

CommonMark 與 GFM 允許 ATX 標題直接插斷段落,所以下面這段會渲染成一個段落加一個 H2:

建置在 40 秒內完成。
## 下一步

比較舊的引擎沒這麼寬容,有些會把標題吞進段落裡,井字號原樣印出來。Setext 的行為又不一樣,因為緊貼段落底下的連字號會被讀成底線。

注意:不管渲染器允許什麼,每個標題上方都請留一行空行。下方的空行不是必要的,但幾個月後回頭看原始檔時,你會慶幸自己有留。

表情符號、中文字與標題該多長

表情符號

標題裡放表情符號可以正常渲染,直接貼上或在 GitHub 上用 :rocket: 這類短碼都行。麻煩的是 ID:產生 slug 時表情符號會被移除,留下的空格變成連字號,所以開頭的表情符號會產生一個以連字號起頭的錨點。

## 🚀 快速上手    -> #-快速上手

這種錨點可以用,只是手寫時看起來很怪。把表情符號改放在文字後面,或直接從渲染後的頁面複製。

中文、日文與韓文

中日韓文字在 GitHub 上會完整保留,所以 ## 安裝步驟 的 ID 就是 #安裝步驟。從網址列複製出來會看到百分號編碼,這是正常的,也一樣有效。靜態網站產生器就不太一致:

  • 有的和 GitHub 一樣原樣保留中文字。
  • 有的轉成拼音或羅馬字
  • 也有少數直接放棄,退回 #section-1

先測一個錨點再動手寫五十個。全形標點跟半形標點一樣會被移除,所以 ## 安裝步驟(進階) 的 ID 是 #安裝步驟進階

長度

標題請控制在大約三十個中文字以內。太長的標題在側邊欄與目錄裡會難看地折行,產生冗長的錨點,被搜尋引擎擷取時也會被截斷。把重要的詞放前面:「設定單一登入」勝過「關於如何為你的團隊設定單一登入的一些筆記」。

常見錯誤與修正寫法

1. 井字號後面沒有空格。那一行仍然是段落,井字號原樣印出來。錯誤與正確寫法:

#簡介

# 簡介

2. 想畫分隔線卻變成標題。緊貼文字底下的連字號會把上面那行變成標題。錯誤寫法:

設定到這裡告一段落。
---

加一行空行就修好了,或直接把分隔線寫成 ***

設定到這裡告一段落。

---

3. 為了視覺效果跳階。下面是壞掉的大綱與修正版,覺得字太大就用 CSS 調小:

## 設定
#### 環境變數

## 設定

### 環境變數

4. 用粗體假裝標題。這對文件大綱、螢幕閱讀器的標題導覽與所有目錄產生器都是隱形的,而且產生不出可以連過去的 ID。錯誤與正確寫法:

**環境變數**

### 環境變數

5. 照著標題文字手打錨點。大寫、空格與標點都不保留。錯誤與正確寫法:

[跳過去](#Setting Up the API Key (v2.1))

[跳過去](#setting-up-the-api-key-v21)

標題怎麼調都不對時,把它單獨貼進編輯器,上下各留一行空行。如果那樣渲染正常,問題就出在上下文:某個縮排、少掉的空行,或它被困在清單項目裡。Markdown 完整指南把標題與其他元素依序串成一份文件;Markdown 是什麼解釋了這個格式為何如此在意空行;程式碼區塊圖片參考則涵蓋標題底下最常出現的東西。

相容性資料驗證日期

常見問題

Markdown 標題有幾個階層?

六個,用一到六個井字號書寫,分別對應 <h1><h6>。七個以上的井字號不是標題,會原樣顯示成文字。另一種 Setext 語法(在文字底下畫 =-)則只有前兩階。

為什麼我的 Markdown 標題沒有渲染?

幾乎都是少了空格:#標題 是段落,# 標題 才是標題。另外兩個原因是行首有四個以上的空白(那一行會變成程式碼區塊),以及比較嚴格的渲染器要求標題上方留一行空行。補上空格與空行就會正常。

一份 Markdown 文件只能有一個 H1 嗎?

是的。H1 是文件標題,出現第二個等於告訴螢幕閱讀器與爬蟲這一頁裝了兩份文件。如果你的靜態網站產生器已經從 front matter 渲染出標題,Markdown 內文請從 ## 開始,避免整頁出現兩個 H1。

Markdown 標題的錨點 ID 是怎麼產生的?

文字會全部轉小寫、移除標點、把空格換成連字號,所以 ## Setting Up the API Key (v2.1)! 的 ID 是 #setting-up-the-api-key-v21。重複的標題會依序加上 -1-2。與其手打,不如從渲染後標題旁的連結圖示複製,或交給目錄產生器一次寫好。

為什麼我打的三個連字號變成標題而不是分隔線?

因為緊貼在一行文字底下的連字號是 Setext 的第二階標題語法,上面那行文字就被變成 H2 了。在文字與連字號之間加一行空行,或把分隔線一律寫成 ***,後者永遠不會被讀成標題底線。

Markdown 要怎麼加一條水平分隔線?

單獨一行打三個以上的連字號、星號或底線就行,---***___ 畫出來的都是同一條 <hr> 分隔線。上方記得留一行空行。少了那行空行,緊貼在文字下方的 --- 會被當成 Setext 底線,把上面那行文字變成標題,而不是畫線。養成寫 *** 的習慣就完全不會撞到這個問題,其他區塊級符號則整理在語法速查表裡。

延伸閱讀

立即使用編輯器

立即使用編輯器