中文排版是一門減法
這一頁是 Markit 的排版樣張。它同時是兩樣東西:一篇能正常讀下去的文章,以及一份把編輯器支援的全部 Markdown 語法排了一遍的清單。兩者必須同時成立——只堆語法的樣張騙不了眼睛,一段真實的長文才能暴露行距是不是散了、標點是不是漏了氣、中英混排的地方是不是硌手。
排版的判斷標準從來不是「好看」,而是「讀完一千字之後眼睛累不累」。這句話聽起來像廢話,但它推翻了大部分看起來很漂亮的方案。
版面的第一原則:先做減法
一個編輯器介面上真正屬於內容的東西,只有文字本身。剩下的一切——工具列、側欄、按鈕、狀態指示——都是在跟內容搶注意力。所以我們的第一條規則是:預設狀態下,介面元素要麼隱形,要麼退到視線邊緣。
具體到幾個數字:
| 元素 | 取值 | 理由 |
|---|---|---|
| 版心寬度 | 720 px | 中文約 45 字/行,接近紙本書的一行字數 |
| 內文行高 | 1.55 | 中文方塊字比拉丁字母密,1.5 會擠 |
| 標題層級 | 三檔 | 再多一檔,層級就靠猜了 |
| 強調色面積 | ≤ 5% | 超過這個比例,強調就不再是強調 |
表格裡的數字用等寬字形對齊——這是個很小的細節,但一欄數字如果左右跳動,讀者會不自覺地去比對而不是去理解。
留白比線條更能分組
用分隔線切分內容是初學者的做法,用留白切分才是成熟的做法。線條會在版面上留下硬邊,而留白是無形的:
- 段落之間 16 px,讓句子成組
- 小節標題上方 24 px、下方 8 px,上緊下鬆讓標題黏住它管轄的內容
- 大節標題上方 40 px,讓讀者的視線自然停一下
這種「上緊下鬆」的分級留白一旦被壓平——比如被一條統一的 margin 規則蓋掉——版面會立刻退化成一串等距的段落,層級全靠字級硬撐。這類失效是靜默的:程式碼裡宣告還在,看起來一切正常。
字型:中西文是兩件事
CSS 的字型回退是逐字元進行的,這意味著字型堆疊的順序決定了英文由誰來渲染。把中文字型排在前面,camelCase、版本號 v1.522、以及所有拉丁字母都會落到中文字型自帶的西文字形上——那部分往往是附贈品。
正確的寫法是西文在前、中文在後:
font-family: Charter, 'LXGW WenKai', -apple-system, 'PingFang TC', sans-serif;
這條規則的兩種寫法都「能跑」,差別只體現在字形上。看程式碼是看不出對錯的,所以它必須由測試來守。
一個反例
下面這段程式碼示範了容易踩的坑——把中文字型放在首位,同時用了合成斜體:
// ⚠️ 兩處都錯了
const stack = "'PingFang TC', Charter, sans-serif" // 中文在前:英文字形失控
const emphasis = { fontStyle: 'italic' } // 中文沒有斜體傳統
中文的強調傳統是著重號(字下加點),不是把方塊字壓歪。合成斜體會讓筆畫交疊處糊成一團,字級越小越明顯。
合成粗體是同一類問題。如果一款字型沒有真正的粗體字面,瀏覽器會把字形描邊加粗,橫豎筆畫的粗細關係會被破壞。
語法清單
下面把剩餘的語法過一遍,順便驗證它們在這套樣式下彼此不打架。
行內元素
內文裡可以出現加粗、強調、刪除線、行內程式碼,以及外部連結。自動識別的網址也算:https://example.com 。這些元素混在同一段裡時,最容易出問題的是行高——任何一個行內元素帶了額外的 padding,整行就會被頂開。
標籤是後設資訊,不是內文重點,所以它做成低飽和的藥丸而不是彩色標籤牆:#排版 #字型 #工程實務
列表
有序列表用於步驟,無序列表用於並列項:
- 先確認要解決的是什麼問題
- 再確認這個問題值不值得解決
- 影響多少人
- 多久遇到一次
- 最後才是怎麼解決
任務列表用於清單:
- 定下版心寬度與行高
- 定下字型堆疊的順序
- 確認中文強調用不用著重號
- 在真機上驗證懸掛標點
巢狀的無序列表:
- 字型
- 西文:Charter、Georgia、Palatino
- 中文:霞鶩文楷、思源宋體、系統黑體
- 排版
- 中西混排間隙
- 標點擠壓
引用與提示
好內容,值得好版面。
排版的目的不是讓文字變好看,而是讓讀者忘記版面的存在。
五種提示塊各有各的語氣,它們是內容作者選擇的語義,不是介面裝飾:
長文閱讀用宋體或黑體更省眼;楷體適合隨筆與短文。
磁碟上的
.md檔案是唯一真相。任何排版效果都只能活在渲染層。
不要在使用者打字的過程中改動他的文字。自動插入的空格會隨自動儲存落盤。
程式碼區塊
程式碼區塊需要橫向捲動而不是撐破版心,配色也要克制——關鍵字用強調色,註解退到灰階,其餘是內文色。彩虹配色在暖紙底上會顯得很吵:
/// 在 Finder 裡選取目前文件
@objc private func revealInFinder(_ sender: Any?) {
guard let url = session.url else { return }
NSWorkspace.shared.activateFileViewerSelecting([url])
}
一行很長的程式碼應該捲動,而不是換行:
curl -sSL "https://raw.githubusercontent.com/example/repo/main/very/long/path/to/some/file.txt" -o output.txt
圖片
圖片下方緊跟一段說明文字時,兩者的間距應該比普通段間距更緊,讀者才會把它們看成一組。
最後
分隔線在上面這一處是合理的:它標記的是「文章結束、收尾開始」這種真正的斷裂,而不是段落之間的呼吸。用得越少,它出現的那一次就越有力量。
一套排版方案是否成立,最終只有一個判據:把這一頁從頭讀到尾,有沒有哪一處讓你停下來注意到了版面本身。有,就是那裡還需要改。