Tailwind in Hugo 是一場騙局:開發者的效率,使用者的負擔

Tailwind CSS 在現代前端開發是一個革命性的工具,它讓開發者擺脫傳統 CSS 的束縛,用簡潔的原子化類別快速建立網頁,當 Tailwind 加上以輕量快速著稱的 Hugo 靜態網站生成器時看起來更是如魚得水,天作之合。然而看似美好,實際卻隱藏嚴重的問題:開發者享受著高效的開發體驗,使用者卻要承擔沉重的技術負擔

我們先從 Hugo 的特色、優點和設計哲學開始討論,接著再說明 Tailwind 的優點,有了這些上下文會讓我們更好理解問題來源。

Hugo:無依賴的網站生成器

什麼是 Hugo?

想像一下,你只想寫文章、分享想法,不想陷入複雜的技術泥沼。Hugo 就是為了這個目標而生的。

Hugo 是一個靜態網站生成器,它的哲學超級簡單:下載一個檔案,就能建網站。不需要 Node.js,不需要處理一堆依賴包,不用擔心版本衝突。你只需要:

  1. 下載 Hugo(獨立執行檔,無依賴)
  2. hugo new site 建立專案
  3. 選一個喜歡的主題使用 git submodule add 下載
  4. 用 Markdown 寫文章
  5. hugo

完成!你的網站上線了,而且這是純二進制分發加上 HTML,無任何依賴,到哪裡都可以運行。

Hugo 主題和客製化

Hugo 系統很特別,他獨特的查找系統讓你可以輕鬆的覆蓋主題檔案,比如說用戶覺得主題的 404 頁面不好看,那覆寫方式非常簡單

  1. theme/MY_THEME/layouts/404.html 複製到根目錄的 layouts/404.html
  2. 開始客製化

一切都是這麼簡單!同樣的任務在 Docusaurus 裡面你需要翻文檔、找出 swizzle 目標、確認目標支援 swizzle、寫 JS、搞懂 React 生態系、還要搞懂各種內建組件 @theme/ThemedImage @docusaurus/Link BrowserOnly 等等等,用戶需要非常龐大的知識儲備才能完成覆寫,而 Hugo 很簡單,找到 HTML,修改 HTML,一切就結束了。

和大多數靜態網站生成器一樣,很多 Hugo 主題也會提供 custom.css 入口來覆蓋主題內建的 CSS,所以簡單的修改用戶只需要寫幾行 CSS,甚至都不需要 override 整個 HTML 文件。

CSS 簡述

接下來我們要開始說明 Hugo + Tailwind 產生的問題了,為了讓不熟悉前端的讀者知道 Tailwind 的強大之處,這裡會簡單介紹最近的 CSS 框架演進。

告別 Bootstrap 的千篇一律

還記得 2010 年代那些長得一模一樣的網站嗎?每個都用 Bootstrap,每個按鈕都是同樣的藍色,每個導航列都長得像雙胞胎。想要改個顏色?你得寫一堆複雜的覆蓋程式碼,然後祈禱不會搞壞其他地方。

Bootstrap 要客製化一個按鈕,開發者得寫:

1
2
3
4
.btn-primary {
    background-color: #007bff !important;
    border-color: #007bff !important;
}

然後戰戰兢兢地測試,確保沒有搞壞其他地方的樣式。

Tailwind:樂高積木

Tailwind CSS 就像是把 CSS 拆解成小積木,想要一個藍色背景?用 bg-blue-500,想要圓角?用 rounded,想要陰影?用 shadow-lg

把這些積木組合起來 <div class="bg-blue-500 text-white px-4 py-2 rounded shadow-lg">,你就得到了一個漂亮的按鈕。不需要取名字,不需要寫 CSS 檔案,直接在 HTML 裡就搞定了,對開發者來說輕鬆的不得了:

  • 開發速度超快,可以輕鬆做出一個卡片式佈局
  • 樣式一致,所有的尺寸、顏色都使用 Tailwind 命名標準,減少 CSS 管理的痛苦
  • 維護輕鬆,要改主題色調?修改設定檔就全部更新,不用一一檢查東缺西漏

更棒的是 Tailwind 還內建了 Purge 功能,它會自動移除你網站中沒有用到的 Tailwind 類別,確保最終生成的 CSS 檔案非常小,大幅度加速網站的載入速度。

當 Tailwind 遇上 Hugo 主題交付

Hugo 很好,Tailwind 也很棒,但這就和你不會在披薩上面淋醬油一樣,這兩個組合在一起就是完全的災難。當你開發個人自用主題時 Tailwind 完全沒問題,然而當這個主題交付到下游,對於用戶就是一場災難。

我整理了五種選擇,每一種都有嚴重問題,都違背 Hugo 和 Tailwind 本身的設計理念。

tip
注意這裡說的是當 Hugo + Tailwind,並且作為主題交付給下游用戶的情境

選項一:預編譯 + Purge + 使用者自定義 CSS

主題開發者將 Tailwind 提前編譯成傳統 CSS,並 purge 移除未使用的樣式,把編譯完成後的小 CSS 分發給用戶,這是最符合 Tailwind 精神的使用方式,也是目前 Blowfishhugo-theme-tailwind 等主題的 CSS 交付方式。這個方式最終的 CSS 檔案很小,使用者也不需要額外安裝任何東西,維持 Hugo 本身的零依賴設計,使用者如果需要客製化就自己寫 custom.css 也和非 Tailwind 主題的使用方式相同,那這會有什麼問題呢?

理解困難:使用者面對的是一個巨大的的 CSS 檔案。想要覆蓋一個樣式?首先你要:

  • 了解 Tailwind 的命名規則
  • 找到對應的 CSS 選擇器(可能是 .bg-blue-500 對應 background-color: #3b82f6
  • 或者具備足夠的 Tailwind 知識從 main.css 中理解結構
  • 或者把編譯後的純 CSS 當作一般的 CSS 理解,但是那至少會有兩千行 CSS,Blowfish 主題甚至膨脹到五千行 CSS

這就是給人一本汽車維修手冊,然後說「你想要的零件都在這裡,自己找吧。」

不只覆寫 CSS 非常困難,就算想要客製化不需要覆寫的元素也很麻煩。由於 Tailwind 的特色,所有 HTML 元素都沒有語意化的類別名稱,幾乎不會看到 class="card__item" 這種東西,用戶看到的只有前面說的樂高積木。這導致 CSS 選擇器變成一串難以理解的 Tailwind class,於是 CSS 檔案每個 class 都要加上註解否則根本無法理解,不只如此,使用者自定義 CSS 還有更大問題,上游很難修改 HTML class,否則下游 custom CSS 全數失效

選項二:預編譯 + Purge + 使用者 override Hugo 模板

既然 CSS 覆蓋有問題,那麼想要直接使用 Hugo override 系統覆寫檔案呢?這也有很大的問題,Tailwind 的核心價值之一的動態擴展性,上游 purge 後完全消失了。使用者想要添加一個 text-red-500 類別?抱歉,這個類別在編譯時被移除了。

於是用戶完全失去了 Tailwind 的動態擴展性,連 custom CSS 都困難重重。

選項三:預編譯 + 不 Purge

前面遵照一般的 Tailwind 使用方式預編譯 + purge 造成了擴展能力低落問題,那麼為了保留擴展性,開發者選擇不移除任何 Tailwind 類別又會產生什麼問題?

  1. 龐大的 CSS 檔案:完整的 Tailwind CSS 可能達到數 MB,這對網站效能是災難性的。
  2. 不可能預測:開發者必須猜測使用者可能需要哪些類別。需要 bg-pink-300?需要 text-7xl?需要顏色的透明度?不好意思沒人知道,這些需求不可能被預測。

此外,就算不 purge 也還是逃不過原有的問題,custom CSS 選擇器仍舊難以指定,override HTML 檔案依然沒辦法隨心所欲的指定所有 class。

選項四:不編譯

既然預先編譯產生這麼多問題,那麼我們乾脆不編譯了吧,開發者直接提供 Tailwind 的原始檔案,要求使用者自己進行編譯。

技術門檻大幅上升:使用者現在需要:

  • 安裝 Node.js 和 npm
  • 理解 package.json 和依賴管理
  • 學會使用 Tailwind CLI 或 PostCSS
  • 處理版本相容性問題

對於開發者而言這些都不是問題,然而對於主題用戶這些問題就大了,大多數人要他們開命令行就已經很痛苦,怎麼可能還要求他們安裝這麼多依賴和前端知識?這也造成邏輯矛盾,如果使用者需要這些 JavaScript 生態系統的知識,為什麼不直接使用 Next.js、Nuxt.js 或 Astro?這嚴重違背 Hugo 哲學,Hugo 的核心競爭優勢——零依賴、開箱即用完全消失了

除此之外還有一個顯而易見的問題,主題安裝變的非常繁瑣,原本只需要使用 submodule 簡單安裝,現在需要安裝 Node.jsnpm,把 tailwind.config.js + 所有 CSS 檔案 + package.json 從主題目錄複製到用戶根目錄。

對於開發者來說可能非常簡單,但是一個產品這麼繁瑣的安裝是災難性的,你的用戶群體是一般用戶而不是開發者,就算是開發者作為用戶,他們也會想要簡單的方式。

選項五:Hugo 整合編譯

也許有人會說「你沒有看文檔,Hugo 內建整合 Tailwind!」沒錯,Hugoplate 就是用這種方式分發的,以 Hugo 內建的 TailwindCSS 完美地結合了兩個世界,從此再也沒有需不需要預先編譯的問題,使用者雖然還是需要安裝 Node.js 和 npm,但是不需要理解複雜的構建系統。開發者可以享受 Tailwind 的便利,使用者也可以即時看到樣式變化。

看似完美,實際上還是引起了同樣的災難,這個方案總結了整篇文章的問題:「Hugo 主題系統和 Tailwind 的根本衝突」:

傳統 CSS 的客製化流程

  1. 使用者想要修改按鈕顏色
  2. static/css/custom.css 中寫入 .btn { background: red; }
  3. 完成

Tailwind 的客製化流程

  1. 使用者想要修改按鈕顏色
  2. 發現需要修改 HTML 模板中的 class 屬性
  3. 找到按鈕定義在 themes/mytheme/layouts/partials/button.html
  4. 把檔案複製到 layouts/partials/button.html
  5. 修改 bg-blue-500bg-red-500
  6. 以後每次主題更新,都要手動合併這個檔案的變更

維護地獄:想像一個使用者客製化了 10 個 partial 檔案,然後主題發布了新版本。他們需要:

  • 比較每個檔案的差異
  • 手動合併變更
  • 測試確保沒有破壞任何功能
  • 處理可能的衝突

這比起簡單的 CSS 覆蓋,複雜度提升了一個數量級。

到這裡我們發現,無論 Tailwind 有沒有預先編譯,在 Hugo 架構中他就永遠不可能好用,當用戶試圖調整一個小細節時卻發現無從下手,或是被迫成為一個前端開發者。

根本問題:架構哲學的衝突

Hugo 的「分離」哲學

Hugo 大致上遵守 Unix 哲學:專注做好一件事,它的設計將複雜的網站建設分解為獨立可控的部分:

  • 內容與展示分離:作者只需專注於 Markdown 寫作,完全不需要了解 HTML、CSS 或 JavaScript。
  • 主題與內容分離:主題開發者負責外觀設計,內容創作者負責文字創作,兩者可以獨立工作而互不干擾。當你想要換一個主題時,只需要修改設定檔中的一行,然後修改自己的 submodule,所有內容自動套用新的網站外觀。
  • 技術與使用者分離:Hugo 的設計讓非技術使用者也能輕鬆建站。他們不需要理解 webpack、npm、Node.js 或任何前端工程的複雜概念。
  • 結構與樣式分離:如果主題使用 Tailwind 以外的技術搭建,就可以享受的這個特性,想要修改結構才 override layouts,想調整樣式就只需要修改 CSS。

Tailwind 的「融合」哲學

Tailwind 是開發者端的工具,所以走的是完全不同的道路,他的的核心理念是將樣式直接寫在 HTML 中,將樣式與結構緊密融合,實現 CSS 的原子性,追求開發效率的最大化。

  • 樣式直接寫在 HTML 標籤中
  • 每個類別都有明確的單一職責
  • 視覺效果與代碼結構一一對應

這種設計消除了 CSS 文件與 HTML 文件之間的往返查找,讓開發者能夠在單一位置完成所有工作。

矛盾

兩者都是以原子獨立為基礎,但是合併起來卻變成醬油披薩,同時套用這兩種工具時就會產生嚴重的邏輯衝突問題,尤其是做為上游主題進行發佈時:

  • Hugo override 系統要求樣式和結構分離,Tailwind 要求它們耦合
  • Hugo 主題假設樣式可以通過 CSS 覆蓋,Tailwind 需要修改 HTML
  • Hugo 的價值在於簡化,Tailwind 的價值在於動態性

所以我不認為這種根本性的設計思維可以讓這兩個工具能各自發揮最大效率,同時使用兩者就一定會犧牲其中一方的核心價值,最終導致專案的四不像。

騙子和受害者

誰是騙子?沒有做好市場研究的主題開發者,他們既是騙子也是受騙者,不過有些人可能是從自用變成公開主題,那就沒辦法怪他們。最大的受害者則是那些只想要一個簡單網站的使用者。他們被迫在以下選項中做選擇:

  1. 接受客製化能力低落的主題(選項一、二)
  2. 忍受極慢的網站載入速度(選項三)
  3. 學習複雜的前端知識才能進行簡單的樣式修改(選項四、五)

這些使用者原本選擇 Hugo 就是為了逃避前端工程的複雜性,結果卻被拖入了更深的技術泥沼,甚至他們在選擇前根本也不太可能意識到這些問題,只有發現自己的網站越來越難以維護之後才會找到這篇文章。

結論

說開發者是騙子可能嚴重了,不過沒有釣魚標題誰要點進來看?但是這剛好和騙子和受害者的關係一樣——一方獲益,另一方承擔代價。

那麼未來開發者對於全新主題開發,以及使用者未來該如何選擇主題?對於開發者我最大的建議就是放棄在 Hugo 使用 Tailwind,這兩個工具有設計邏輯上的根本衝突,並且強烈建議提前調查 Docusaurus/Vitepress/Next.js 在「用戶」方面是如何設計的,比如在 Docusaurus 基於 Infima 系統可以指定元素的顏色,甚至提供調色盤讓用戶輕鬆的自定義,Vitepress 一樣也是使用類似的概念完成;並且建議使用 Hugo 內建的 PostCSS/Sass 功能,這些都可以無縫整合現代的 CSS 功能,並且不會有 HTML 耦合 CSS 的問題。

對於用戶,講好聽一點是可以基於自身條件簡單檢查

  1. 重要:主題是否提供 CSS 自訂屬性來調整顏色和間距?
  2. 主題是否需要 Node.js 環境?
  3. 客製化是否需要大量修改 HTML 模板?(此問題基本上只會出現在使用 Tailwind 的主題)

不過誰選主題會想這麼多,還是哪個主題好看選哪個,就算維護負擔增加,至少那個主題好看,不喜歡的主題再好維護也沒用。

我們能從這件事學到最大的教訓就是「永遠提前做好調查」,技術棧選好開始開發專案,頭洗下去之後想反悔已經來不及了。

載入評論