<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Theme on Tarragon</title><link>https://tarrragon.github.io/blog/tags/theme/</link><description>Recent content in Theme on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Sat, 25 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/theme/index.xml" rel="self" type="application/rss+xml"/><item><title>baseof.html override 範圍最小化</title><link>https://tarrragon.github.io/blog/report/minimize-baseof-override/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/report/minimize-baseof-override/</guid><description>&lt;h2 id="核心原則">核心原則&lt;/h2>
&lt;p>&lt;strong>Override theme 檔案的範圍越小、theme 升級時越容易 sync。&lt;/strong> 整檔 copy + 改 1-2 行的 override 在 theme 改了 baseof 時、本地必須手動 merge；只 override 必要的部分（用 partial 或最小檔案）讓變更面積小、merge 容易。&lt;/p>
&lt;hr>
&lt;h2 id="為什麼-override-要小">為什麼 override 要小&lt;/h2>
&lt;h3 id="商業邏輯">商業邏輯&lt;/h3>
&lt;p>Hugo theme 的 lookup order：本地 &lt;code>layouts/&lt;/code> 優先於 &lt;code>themes/&amp;lt;name&amp;gt;/layouts/&lt;/code>。本地有同名檔案、本地的整個內容生效、theme 的版本完全被忽略。&lt;/p>
&lt;p>當本地 override 整個 baseof.html、只改 1-2 行：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Theme 升級時的代價&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>本地 override 不會自動更新 — 永遠是當初 copy 的版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Theme 的新功能（新 partial、改進的 SEO meta）不會生效&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>要手動 diff &lt;code>themes/&amp;lt;name&amp;gt;/layouts/baseof.html&lt;/code> 與本地、合併變更&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>容易忘記、theme 的修正在本地永遠沒套到&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Override 範圍小 = merge 面積小 = 升級時手動 sync 的工作量小。&lt;/p>
&lt;hr>
&lt;h2 id="這次任務的-override">這次任務的 override&lt;/h2>
&lt;h3 id="觀察">觀察&lt;/h3>
&lt;p>&lt;code>layouts/_default/baseof.html&lt;/code> 是整個 theme 的 baseof 複製、只改了：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-diff" data-lang="diff">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="gd">- &amp;lt;body&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="gd">&lt;/span>&lt;span class="gi">+ &amp;lt;body{{ if eq .Layout &amp;#34;search&amp;#34; }} class=&amp;#34;page-search&amp;#34;{{ end }}&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="gi">+ {{- partial &amp;#34;pagefind_meta.html&amp;#34; . -}}
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>兩個改動：&lt;/p>
&lt;ol>
&lt;li>&lt;code>&amp;lt;body&amp;gt;&lt;/code> 加條件 class（搜尋頁需要的 hook）&lt;/li>
&lt;li>&lt;code>&amp;lt;main&amp;gt;&lt;/code> 內加 &lt;code>pagefind_meta.html&lt;/code> partial 引用&lt;/li>
&lt;/ol>
&lt;p>整個 baseof（44 行）完全 copy、只為了這兩處 5 行改動。&lt;/p>
&lt;h3 id="判讀">判讀&lt;/h3>
&lt;p>兩個改動都有更小的 override 方式：&lt;/p>
&lt;h4 id="改動-1body-class">改動 1：body class&lt;/h4>
&lt;p>Hugo 的 &lt;code>block&lt;/code> 機制讓 child template override &lt;code>block&lt;/code> 內容。如果 theme baseof 預先定義了 &lt;code>body-class&lt;/code> block：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c">&amp;lt;!-- theme baseof.html --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">body&lt;/span> &lt;span class="na">class&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ block &amp;#34;&lt;/span>&lt;span class="na">body-class&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">.&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>那本地搜尋頁 layout 可以：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c">&amp;lt;!-- layouts/_default/search.html --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">{{ define &amp;#34;body-class&amp;#34; }}page-search{{ end }}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不需要 override 整個 baseof。&lt;/p>
&lt;p>但這次的 theme 沒有 &lt;code>body-class&lt;/code> block — 所以這條路不通、必須 override。&lt;/p>
&lt;p>替代：用 &lt;code>custom_body.html&lt;/code> 之類已有的 partial hook。Theme 可能在 body 結束前 inject &lt;code>custom_body.html&lt;/code>、但那發生在 body 開始之後、無法影響 body 的 attribute。&lt;/p>
&lt;p>結論：第一個改動需要 override baseof、無更小的方式。&lt;/p>
&lt;h4 id="改動-2pagefind_metahtml-partial">改動 2：pagefind_meta.html partial&lt;/h4>
&lt;p>這個 partial 注入在 &lt;code>&amp;lt;main&amp;gt;&lt;/code> 開頭、加 hidden filter spans。可以放到 &lt;code>custom_head.html&lt;/code>（theme 已有的 hook） — 但 head 內的元素不會被 pagefind 索引、所以那條路不通。&lt;/p></description><content:encoded><![CDATA[<h2 id="核心原則">核心原則</h2>
<p><strong>Override theme 檔案的範圍越小、theme 升級時越容易 sync。</strong> 整檔 copy + 改 1-2 行的 override 在 theme 改了 baseof 時、本地必須手動 merge；只 override 必要的部分（用 partial 或最小檔案）讓變更面積小、merge 容易。</p>
<hr>
<h2 id="為什麼-override-要小">為什麼 override 要小</h2>
<h3 id="商業邏輯">商業邏輯</h3>
<p>Hugo theme 的 lookup order：本地 <code>layouts/</code> 優先於 <code>themes/&lt;name&gt;/layouts/</code>。本地有同名檔案、本地的整個內容生效、theme 的版本完全被忽略。</p>
<p>當本地 override 整個 baseof.html、只改 1-2 行：</p>
<table>
  <thead>
      <tr>
          <th>Theme 升級時的代價</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>本地 override 不會自動更新 — 永遠是當初 copy 的版本</td>
      </tr>
      <tr>
          <td>Theme 的新功能（新 partial、改進的 SEO meta）不會生效</td>
      </tr>
      <tr>
          <td>要手動 diff <code>themes/&lt;name&gt;/layouts/baseof.html</code> 與本地、合併變更</td>
      </tr>
      <tr>
          <td>容易忘記、theme 的修正在本地永遠沒套到</td>
      </tr>
  </tbody>
</table>
<p>Override 範圍小 = merge 面積小 = 升級時手動 sync 的工作量小。</p>
<hr>
<h2 id="這次任務的-override">這次任務的 override</h2>
<h3 id="觀察">觀察</h3>
<p><code>layouts/_default/baseof.html</code> 是整個 theme 的 baseof 複製、只改了：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="ln">1</span><span class="cl"><span class="gd">- &lt;body&gt;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gd"></span><span class="gi">+ &lt;body{{ if eq .Layout &#34;search&#34; }} class=&#34;page-search&#34;{{ end }}&gt;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gi">+   {{- partial &#34;pagefind_meta.html&#34; . -}}
</span></span></span></code></pre></div><p>兩個改動：</p>
<ol>
<li><code>&lt;body&gt;</code> 加條件 class（搜尋頁需要的 hook）</li>
<li><code>&lt;main&gt;</code> 內加 <code>pagefind_meta.html</code> partial 引用</li>
</ol>
<p>整個 baseof（44 行）完全 copy、只為了這兩處 5 行改動。</p>
<h3 id="判讀">判讀</h3>
<p>兩個改動都有更小的 override 方式：</p>
<h4 id="改動-1body-class">改動 1：body class</h4>
<p>Hugo 的 <code>block</code> 機制讓 child template override <code>block</code> 內容。如果 theme baseof 預先定義了 <code>body-class</code> block：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="c">&lt;!-- theme baseof.html --&gt;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">&lt;</span><span class="nt">body</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;{{ block &#34;</span><span class="na">body-class</span><span class="err">&#34;</span> <span class="err">.</span> <span class="err">}}{{</span> <span class="na">end</span> <span class="err">}}&#34;</span><span class="p">&gt;</span></span></span></code></pre></div><p>那本地搜尋頁 layout 可以：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="c">&lt;!-- layouts/_default/search.html --&gt;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">{{ define &#34;body-class&#34; }}page-search{{ end }}</span></span></code></pre></div><p>不需要 override 整個 baseof。</p>
<p>但這次的 theme 沒有 <code>body-class</code> block — 所以這條路不通、必須 override。</p>
<p>替代：用 <code>custom_body.html</code> 之類已有的 partial hook。Theme 可能在 body 結束前 inject <code>custom_body.html</code>、但那發生在 body 開始之後、無法影響 body 的 attribute。</p>
<p>結論：第一個改動需要 override baseof、無更小的方式。</p>
<h4 id="改動-2pagefind_metahtml-partial">改動 2：pagefind_meta.html partial</h4>
<p>這個 partial 注入在 <code>&lt;main&gt;</code> 開頭、加 hidden filter spans。可以放到 <code>custom_head.html</code>（theme 已有的 hook） — 但 head 內的元素不會被 pagefind 索引、所以那條路不通。</p>
<p>也可以從每個 layout 內手動引入：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl"><span class="c">&lt;!-- layouts/_default/single.html --&gt;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">{{ define &#34;main&#34; }}
</span></span><span class="line"><span class="ln">3</span><span class="cl">{{- partial &#34;pagefind_meta.html&#34; . -}}
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>{{ .Title }}<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">...
</span></span><span class="line"><span class="ln">6</span><span class="cl">{{ end }}</span></span></code></pre></div><p>但這樣每個 layout（single、list、search、taxonomy）都要重複引用 — 維護成本不一定更低。</p>
<p>結論：第二個改動放在 baseof 比放在每個 layout 更乾淨。</p>
<h3 id="執行當前-override-已是最小">執行：當前 override 已是最小</h3>
<p>兩個改動都是 baseof override 較合理。但可以做的精簡是 <strong>註解標明跟 theme 版本的差異</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln"> 1</span><span class="cl">{{- /*
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  本地 override theme baseof.html。
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  跟 themes/hugo-bearcub/layouts/_default/baseof.html 的差異：
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    1. <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> 加條件 class=&#34;page-search&#34;（給搜尋頁的 CSS / JS hook 用）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    2. <span class="p">&lt;</span><span class="nt">main</span><span class="p">&gt;</span> 內加 partial &#34;pagefind_meta.html&#34;（注入 pagefind filter metadata）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  Theme 升級時、把上面兩個改動套到新版 baseof 即可。
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">*/ -}}
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;...&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">...</span></span></code></pre></div><p>註解告訴未來的維護者「這檔案 override 了什麼、為什麼、升級時要看哪些 diff」。</p>
<hr>
<h2 id="內在屬性比較四種-override-策略">內在屬性比較：四種 override 策略</h2>
<table>
  <thead>
      <tr>
          <th>策略</th>
          <th>改動面積</th>
          <th>升級成本</th>
          <th>適用情境</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>整檔 copy + 修改</td>
          <td>大</td>
          <td>高 — 手動 merge 整檔</td>
          <td>Theme 沒提供 hook、必要</td>
      </tr>
      <tr>
          <td>Override 加註解標明 diff</td>
          <td>大</td>
          <td>中 — 註解告訴升級者改了什麼</td>
          <td>整檔 override 的最佳實踐</td>
      </tr>
      <tr>
          <td>用 theme 提供的 partial / block hook</td>
          <td>小</td>
          <td>低 — theme 升級不影響</td>
          <td>Theme 設計時預留了 hook</td>
      </tr>
      <tr>
          <td>Fork theme 並維護</td>
          <td>整個 theme</td>
          <td>最高 — 整個 theme 都要 sync</td>
          <td>客製極深、theme 沒 hook</td>
      </tr>
  </tbody>
</table>
<p>優先選「用 theme 提供的 hook」、次選「override 加註解」、最後才考慮 fork。</p>
<hr>
<h2 id="override-的具體最佳實踐">Override 的具體最佳實踐</h2>
<h3 id="1-註解標明-diff">1. 註解標明 diff</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl">{{- /*
</span></span><span class="line"><span class="ln">2</span><span class="cl">  Override theme/.../baseof.html。
</span></span><span class="line"><span class="ln">3</span><span class="cl">  改了：
</span></span><span class="line"><span class="ln">4</span><span class="cl">    - <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> 加 class hook
</span></span><span class="line"><span class="ln">5</span><span class="cl">    - <span class="p">&lt;</span><span class="nt">main</span><span class="p">&gt;</span> 內加 partial
</span></span><span class="line"><span class="ln">6</span><span class="cl">*/ -}}</span></span></code></pre></div><p>註解讓未來維護者一眼知道改了什麼。</p>
<h3 id="2-override-檔案內容對齊-theme-版本">2. Override 檔案內容對齊 theme 版本</h3>
<p>當 theme 升級時：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">diff themes/hugo-bearcub/layouts/_default/baseof.html <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>     layouts/_default/baseof.html</span></span></code></pre></div><p>差異應該只有註解內標明的那幾處。如果差異更多 — 表示 theme 有變更我們沒套到。</p>
<h3 id="3-標明-theme-版本">3. 標明 theme 版本</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="ln">1</span><span class="cl">{{- /*
</span></span><span class="line"><span class="ln">2</span><span class="cl">  Override based on themes/hugo-bearcub@v1.2.3.
</span></span><span class="line"><span class="ln">3</span><span class="cl">  跟該版本的 baseof.html 差異：...
</span></span><span class="line"><span class="ln">4</span><span class="cl">*/ -}}</span></span></code></pre></div><p>知道是基於哪個版本 override、升級到 v1.3.0 時知道要 diff 哪兩個版本。</p>
<h3 id="4-主動建議-theme-加-hook">4. 主動建議 theme 加 hook</h3>
<p>如果常需要 override theme 同樣的位置、考慮給 theme 提 PR 加 <code>block</code> 或 <code>partial</code> hook — 這樣升級後 hook 自動有、不需要繼續 override。</p>
<hr>
<h2 id="設計取捨theme-客製的策略">設計取捨：Theme 客製的策略</h2>
<p>四種做法、各自機會成本不同。優先選 A（用 theme hook）— 不夠用才退到 B / C / D。</p>
<h3 id="a用-theme-提供的-partial--block--template-hook最佳">A：用 theme 提供的 partial / block / template hook（最佳）</h3>
<ul>
<li><strong>機制</strong>：theme 預留 <code>block</code>、<code>custom_head.html</code>、<code>custom_body.html</code> 等 hook、本地只填 hook</li>
<li><strong>選 A 的理由</strong>：theme 升級不影響本地客製、hook 是公開介面</li>
<li><strong>適合</strong>：theme 設計時預留了對應 hook 的客製需求</li>
<li><strong>代價</strong>：需要 theme 預先支援、若不支援考慮給 theme 提 PR 加 hook</li>
</ul>
<h3 id="boverride-加-diff-註解這個專案的預設">B：Override 加 diff 註解（這個專案的預設）</h3>
<ul>
<li><strong>機制</strong>：複製 theme 檔案到本地、改必要的部分、註解標明跟 theme 版本的差異</li>
<li><strong>跟 A 的取捨</strong>：B 不需要 theme 預留 hook、A 需要；B 升級時要手動 sync</li>
<li><strong>適合</strong>：theme 沒對應 hook、必須 override</li>
<li><strong>代價</strong>：升級時要 diff theme 新版手動 merge、註解可降低 merge 成本</li>
</ul>
<h3 id="coverride-不加註解">C：Override 不加註解</h3>
<ul>
<li><strong>機制</strong>：複製 theme 檔案、改必要部分、不註解</li>
<li><strong>跟 B 的取捨</strong>：C 寫法簡單、B 額外註解；但 C 未來維護者不知為什麼這檔案在本地、漏 sync 風險高</li>
<li><strong>C 才合理的情境</strong>：純探索性 override、之後會還原 — production 不該如此</li>
</ul>
<h3 id="dfork-theme-維護自己版本">D：Fork theme 維護自己版本</h3>
<ul>
<li><strong>機制</strong>：fork theme 整個 repo、所有客製改在 fork 內</li>
<li><strong>成本特別高的原因</strong>：每次原 theme 升級都要 merge upstream、長期維護負擔重</li>
<li><strong>D 才合理的情境</strong>：客製極深（多檔案 override + 改 internal logic）、且願意承擔 fork 維護成本</li>
</ul>
<hr>
<h2 id="判讀徵兆">判讀徵兆</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>Refactor 動作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>整檔 override theme 檔案、只改 1-2 行</td>
          <td>加註解標明 diff、未來容易升級</td>
      </tr>
      <tr>
          <td>Override 不知道是基於哪個 theme 版本</td>
          <td>加版本註解</td>
      </tr>
      <tr>
          <td>Theme 升級後本地客製失效 / 出怪事</td>
          <td>Diff theme 新版與本地 override、手動 sync</td>
      </tr>
      <tr>
          <td>多個 override 檔案、不知道為什麼存在</td>
          <td>每個 override 加用途註解</td>
      </tr>
      <tr>
          <td>同樣的客製需求要 override 多個檔案</td>
          <td>評估給 theme 提 PR 加 hook</td>
      </tr>
  </tbody>
</table>
<p><strong>核心原則</strong>：Override 是雙面刃 — 短期解決客製、長期增加升級成本。把 override 範圍與 diff 範圍維持最小、註解說明來由 — 是長期可維護的妥協。</p>
]]></content:encoded></item></channel></rss>