問題背景

在撰寫技術文章時,我們會在文章中加入程式碼範例。但是 blog 的目標是分享我處理問題的思路,而不是提供解決方案,所以我希望預設把程式碼隱藏。

最初的解決方案

使用 HTML5 的 <details><summary> 標籤:

1<details>
2<summary>點擊查看程式碼</summary>
3
4\```javascript
5console.log('Hello World');
6\```
7
8</details>

這個方案雖然功能正常,但會觸發 MD033 Markdown Linter 警告

1MD033/no-inline-html: Inline HTML [Element: details]

為什麼會有這個警告?

Markdown 設計哲學

Markdown 的設計理念是:

  • 保持純文字的可讀性
  • 避免直接使用 HTML 標籤
  • 使用語義化的標記語法

MD033 規則的目的

MD033 規則旨在:

  1. 維持 Markdown 的純淨性:避免 HTML 與 Markdown 混用
  2. 提升可維護性:純 Markdown 更容易閱讀和維護
  3. 確保相容性:不同的 Markdown 渲染器對 HTML 的支援程度不同

Hugo Shortcode 解決方案

什麼是 Shortcode?

Hugo Shortcode 是 Hugo 靜態網站生成器提供的一個強大功能,允許你:

  • 在 Markdown 中使用自定義的簡短標記
  • 封裝複雜的 HTML 結構
  • 保持 Markdown 文件的整潔

優勢分析

特性HTML 標籤Hugo Shortcode
Markdown Linter觸發警告無警告
可維護性分散在各處集中管理
可讀性較差優秀
彈性固定結構可自定義
Hugo 最佳實踐不推薦官方推薦

實現步驟

步驟 1:創建 Shortcode 檔案

在專案根目錄創建 layouts/shortcodes/details.html

點擊查看 Shortcode 程式碼
 1{{/* 
 2  Details shortcode - 用於創建可折疊的內容區塊
 3  
 4  使用方式:
 5  
 6
 7<details>
 8  <summary>點擊展開</summary>
 9  
10  內容...
11  
12</details>
13  
14  參數:
15  - summary: 摘要文字(可選,預設為 "點擊展開")
16*/}}
17<details>
18  <summary>{{ .Get "summary" | default "點擊展開" }}</summary>
19  {{ .Inner | markdownify }}
20</details>

程式碼說明

  1. 註解區塊{{/* ... */}} 用於說明 shortcode 的用途和使用方式
  2. 參數獲取.Get "summary" 獲取 summary 參數
  3. 預設值default "點擊展開" 提供預設文字
  4. 內容處理.Inner 獲取標籤內的內容
  5. Markdown 渲染markdownify 將內容中的 Markdown 語法轉換為 HTML

步驟 2:在 Markdown 中使用

舊方式(會觸發 MD033)

 1<details>
 2<summary>點擊查看程式碼</summary>
 3
 4\```toml
 5[markup]
 6  [markup.tableOfContents]
 7    startLevel = 2
 8\```
 9
10</details>

新方式(符合 Markdown 規範)

1

點擊查看程式碼 \```toml [markup] [markup.tableOfContents] startLevel = 2 \```

步驟 3:添加 CSS 樣式

layouts/partials/custom_head.html 中添加樣式:

點擊查看 CSS 樣式程式碼
 1/* 可折疊程式碼區塊樣式 */
 2details {
 3  margin: 1.5rem 0;
 4  padding: 1rem;
 5  background: rgba(0, 0, 0, 0.05);
 6  border-radius: 8px;
 7  border: 1px solid rgba(0, 0, 0, 0.1);
 8  transition: all 0.3s ease;
 9}
10
11details:hover {
12  background: rgba(0, 0, 0, 0.08);
13  border-color: rgba(0, 0, 0, 0.15);
14}
15
16details[open] {
17  background: rgba(0, 0, 0, 0.03);
18  border-color: rgba(0, 0, 0, 0.2);
19}
20
21summary {
22  cursor: pointer;
23  font-weight: 600;
24  font-size: 0.95rem;
25  padding: 0.5rem;
26  margin: -1rem -1rem 0 -1rem;
27  border-radius: 8px 8px 0 0;
28  background: rgba(0, 0, 0, 0.05);
29  transition: all 0.2s ease;
30  user-select: none;
31  list-style: none;
32}
33
34summary::-webkit-details-marker {
35  display: none;
36}
37
38summary::before {
39  content: '▶';
40  display: inline-block;
41  margin-right: 0.5rem;
42  transition: transform 0.3s ease;
43  font-size: 0.8rem;
44}
45
46details[open] summary::before {
47  transform: rotate(90deg);
48}
49
50summary:hover {
51  background: rgba(0, 0, 0, 0.1);
52}
53
54details[open] summary {
55  margin-bottom: 1rem;
56  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
57  border-radius: 8px 8px 0 0;
58}
59
60/* 確保 details 內的程式碼區塊樣式正常 */
61details pre {
62  margin: 1rem 0 0 0;
63}
64
65details > *:not(summary) {
66  animation: fadeIn 0.3s ease;
67}
68
69@keyframes fadeIn {
70  from {
71    opacity: 0;
72    transform: translateY(-10px);
73  }
74  to {
75    opacity: 1;
76    transform: translateY(0);
77  }
78}
79
80/* 響應式設計 */
81@media (max-width: 768px) {
82  details {
83    margin: 1rem 0;
84    padding: 0.8rem;
85  }
86
87  summary {
88    font-size: 0.9rem;
89    padding: 0.4rem;
90    margin: -0.8rem -0.8rem 0 -0.8rem;
91  }
92}

進階功能

自定義參數

你可以擴展 shortcode 支援更多參數:

點擊查看進階 Shortcode 程式碼
 1{{/*
 2  進階 Details shortcode
 3  
 4  參數:
 5  - summary: 摘要文字
 6  - open: 是否預設展開(true/false)
 7  - class: 自定義 CSS 類別
 8*/}}
 9<details {{ if .Get "open" }}open{{ end }} {{ with .Get "class" }}class="{{ . }}"{{ end }}>
10  <summary>{{ .Get "summary" | default "點擊展開" }}</summary>
11  {{ .Inner | markdownify }}
12</details>

使用範例

1

重要提示 這個區塊預設是展開的

巢狀使用

Shortcode 支援巢狀使用:

1

外層標題 這是外層內容
內層標題 這是內層內容

遷移指南

批量替換

如果你已經有很多使用 HTML 標籤的文章,可以使用以下步驟批量替換:

步驟 1:備份檔案

1git commit -am "備份:準備遷移到 shortcode"

步驟 2:使用 sed 批量替換(macOS)

點擊查看批量替換腳本
 1# 替換開始標籤
 2find content -name "*.md" -type f -exec sed -i '' \
 3  's/<details>$/{{&lt; details summary="點擊查看程式碼" &gt;}}/g' {} +
 4
 5# 替換帶 summary 的開始標籤
 6find content -name "*.md" -type f -exec sed -i '' \
 7  's/<details>.*<summary>\(.*\)<\/summary>/{{&lt; details summary="\1" &gt;}}/g' {} +
 8
 9# 替換結束標籤
10find content -name "*.md" -type f -exec sed -i '' \
11  's/<\/details>/{{&lt; \/details &gt;}}/g' {} +

步驟 3:驗證結果

1# 檢查是否還有 HTML 標籤
2grep -r "<details>" content/
3grep -r "</details>" content/

步驟 4:測試並提交

1hugo server -D
2# 確認無誤後提交
3git add .
4git commit -m "遷移到 shortcode:移除 HTML 標籤"

常見問題

Q1: Shortcode 不生效?

可能原因

  1. 檔案路徑錯誤:確認檔案在 layouts/shortcodes/ 目錄
  2. 檔案名稱錯誤:檔案名稱應該是 details.html
  3. Hugo 版本過舊:確認 Hugo 版本 >= 0.55

解決方案

1# 檢查 Hugo 版本
2hugo version
3
4# 重新啟動 Hugo server
5hugo server -D --disableFastRender

Q2: Markdown 內容沒有被渲染?

問題:shortcode 內的 Markdown 語法沒有被轉換為 HTML

解決方案

確認使用了 markdownify 函數:

1{{ .Inner | markdownify }}

Q3: 如何處理全域 gitignore 規則?

如果你的專案需要追蹤 .claude/settings.local.json,但被全域 gitignore 排除:

方案 1:強制添加

1git add -f .claude/settings.local.json

方案 2:在專案 .gitignore 中覆蓋

# 允許追蹤 .claude/settings.local.json
!.claude/settings.local.json

Q4: CSS 樣式沒有生效?

檢查清單

  1. 確認 CSS 是否正確載入到 custom_head.html
  2. 確認瀏覽器快取是否清除(Ctrl+Shift+R 強制重新整理)
  3. 確認 CSS 選擇器是否正確
  4. 確認是否有其他 CSS 覆蓋了樣式

效能考量

Shortcode vs HTML 標籤

項目HTML 標籤Shortcode
建置時間稍慢(需處理)
執行時效能相同相同
快取效果相同相同
維護成本