Moving away from Tailwind, and learning to structure my CSS

Julia Evans

遠離 Tailwind,學習如何組織我的 CSS

原文由 Julia Evans 發布,訂閱此部落格

哈囉!8 年前,我興奮地寫下發現 Tailwind 的心得

那時我真的完全不知道該怎麼組織我的 CSS 程式碼,在一片徹底的混亂和 Tailwind 之間做選擇,我非常開心地選擇了 Tailwind。它幫我完成了好多小網站!

過去一週左右,我把幾個網站從 Tailwind 遷移到更語意化的 HTML 加上原生 CSS,過程超好玩、也超有趣,所以想在這裡分享一些學到的東西!

跟往常一樣,我不是全職的前端開發者,所以這些年來我對 CSS 的學習都是斷斷續續累積起來的。

原來 Tailwind 教會了我很多

當我開始思考該怎麼組織 CSS 時,一開始有點畏縮:我本來就不太會組織 CSS!但接著我開始閱讀一些談論如何組織 CSS 的部落格文章(像是A whole cascade of layersHow I write CSS in 2024),然後我意識到幾件事:

  1. 每個 CSS 程式碼庫裡都有好多不同的東西在運作(版面配置!字型!顏色!共用元件!)
  2. 為每一種類別建立一套系統或規範來管理非常有用,不然很快就會陷入混亂
  3. Tailwind 針對其中一些類別已經有現成的系統,而我早就熟悉那些系統了!或許我可以直接模仿我喜歡的那些系統!

舉例來說,Tailwind 就有:

接下來要談的幾個系統

接下來我想聊聊我的 CSS 程式碼庫的幾個面向,以及到目前為止我對每個面向想訂立什麼樣的規則。有些是從 Tailwind 抄來的,有些則不是。

  1. 重置
  2. 元件
  3. 顏色
  4. 字級大小
  5. 工具類別
  6. 基礎樣式
  7. 間距
  8. 響應式設計
  9. 建置系統

1. 重置

我直接複製了 Tailwind 的「preflight styles」,做法是打開 tailwind.css 然後把前面大約 200 行複製過來。

我發現久而久之,我已經跟 Tailwind 的 CSS 重置培養出感情了,舉例來說,Tailwind 會對每個元素設定 box-sizing: border-box(意思是元素的寬度會包含內距):

* { box-sizing: border-box; }

我覺得如果要改成不用這些設定來寫 CSS,對我來說真的會很不習慣,而且我確定 Tailwind 的重置裡還有很多其他東西(像是 html {line-height: 1.5;}),我已經在潛意識裡習慣了,甚至沒意識到它們的存在。

2. 元件

接下來的部分就是 CSS 的大宗!

這裡的想法是把 CSS 依照「元件」來組織,在精神上跟 Vue 或 React 的元件有點類似。(雖然網站裡可能根本沒有任何 JavaScript)

基本上概念是:

  1. 每個「元件」都有一個獨一無二的 class
  2. 一個元件的 CSS 永遠不會覆蓋另一個元件的 CSS
  3. 每個元件都有自己獨立的 CSS 檔案

這樣一來,修改某個元件的 CSS 就不會莫名其妙弄壞另一個元件。而且我實際上想修改的 CSS 大概有 80% 都在各個元件檔案裡,所以如果我在編輯一個 100 行的元件,我只需要思考那 100 行就好。對我來說思考起來輕鬆多了。

舉例來說,這段 HTML 可能就是 .zine 這個「元件」。

<figure class="zine horizontal">
    <img src="whatever.jpg">
</figure>

而 CSS 用巢狀選擇器寫起來大概會像這樣:

.zine {
  ...
  &.horizontal {
    ...
  }
  &.vertical {
    ...
  }
  &:hover {
    ...
  }
}

我還沒有用任何程式化的手段(像是 web components 或@scope)來確保元件之間不會互相干擾,但光是訂下一個約定並盡力遵守,就已經感覺是很大的進步了。

接下來:建立一些規範,讓整個網站保持一致,並讓這些元件彼此協調!

3. 顏色

colours.css 裡有一堆像這樣的變數,需要時就可以拿來用。顏色真的很難處理,這次重構我不想重新檢視顏色的用法,所以就先維持原樣。

我在這裡想執行的唯一規範是:網站上使用的所有顏色都要列在這個檔案裡。

:root {
  --pink: #fea0c2;
  --pink-light: #F9B9B9;
  --red: #f91a55;
  --orange: rgb(222, 117, 31);
  ...
}

4. 字級大小

我很喜歡 Tailwind 的一點是,如果我想設定字級大小,只要想「嗯,我想要文字大一點」,寫個 text-lg 就搞定了!如果覺得還不夠大,再改成 xl2xl 就好。完全不用去記到底要用 em 還是 px 還是 rem

所以我參考 Tailwind 定義了一堆變數,像這樣:

  --size-xs: 0.75rem;
  --line-height-xs: 1rem;

  --size-sm: 0.875rem;
  --line-height-sm: 1.25rem;

然後如果我想設定字級大小,就可以這樣寫。雖然比 Tailwind 囉唆一點,但目前我還算滿意。

h3 {
  font-size: var(--size-lg);
  line-height: var(--line-height-lg);
}

5. 工具類別

有些東西像是按鈕,會出現在許多不同的元件裡。我把這些稱為「工具類別」。

我從 Tailwind 複製了一些工具類別(像是 .sr-only,用來處理只想讓螢幕閱讀器使用者看到的內容)。

這個區塊很小,我會盡量小心,不要隨意更動這裡。

6. 基礎樣式

「基礎」樣式是我自己挑選、套用到整個網站的樣式。我必須讓這個區塊保持非常小,因為我還沒自信到能強制在全站套用很多樣式。目前我只對這兩個覺得還 OK,而且 <section> 那個我可能還會再改:

/* put a 950px column in the middle of each <section> */
section {
  --inner-width: 950px;
  padding: 3rem max(1rem, (100% - var(--inner-width))/2);
}

a {
  color: var(--orange);
}

我覺得對基礎樣式來說,對我而言最簡單的做法是自下而上——一開始基礎樣式裡幾乎什麼都不放,然後隨著我慢慢發現共通的需求,再把一些樣式從元件中移到基礎樣式裡。

7. 間距

我還沒完全想出一套管理內距和外距的方法。不過我確實想比以前用 Tailwind 時更有原則一點——以前我就是隨意地到處加內距和外距,直到看起來順眼為止。

目前我的方向是盡量讓外層的版面配置元件來負責間距。舉例來說,如果我有一個 <section> 裡面有一堆子元素,我想讓它們之間有間隔,我可能會這樣讓子元素平均分佈間距:

section > *+* {
  margin-top: 1rem;
}

一些啟發我的部落格文章:

8. 響應式設計:多用 grid!

我以前在 Tailwind 裡做響應式設計的方式是用很多 media query。Tailwind 有這種 md:text-xl 語法,意思是「在 md 或更大的尺寸上套用 text-xl 樣式」。

現在我嘗試了相當不同的方法,就是做出更靈活、不需要那麼多斷點的 CSS grid 版面配置。這很難,但學習 grid 能做到什麼真的很有趣,而且這正是個我覺得用 Tailwind 做不到的好例子。

舉例來說,我最近在學怎麼用 auto-fit 來自動在大螢幕上用兩欄、小螢幕上用一欄,像這樣:

  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), max-content));
  justify-content: center;

我也大量使用了grid-template-areas,這是一個超棒的功能,我覺得用 Tailwind 應該是沒辦法用的。

一些靈感來源:

9. 建置系統:esbuild

在開發時,我不需要建置系統:現在的 CSS 已經內建匯入語句,像這樣:

@import "reset.css";
@import "typography.css";
@import "colors.css";

也有內建的巢狀選擇器,像這樣:

.page {
  h2 { ...}
}

如果需要的話,我可以用 esbuild 來打包正式環境用的 CSS 檔案。大概會像這樣。

esbuild style.css --bundle --loader:.svg=dataurl  --loader:.woff2=file --outfile=/tmp/out.css

雖然我平常會盡量避免使用 CSS 和 JS 的建置系統,但我不排斥使用 esbuild(我在2021 年在這裡寫過相關文章),因為它是基於網頁標準,而且是一個靜態的 Go 執行檔。

為什麼要從 Tailwind 遷移出來?

有幾個人問我為什麼要從 Tailwind 遷移出來。幾個促使我這麼做的原因包括:

  • Tailwind 從 2018 年以來變得越來越依賴建置系統,我覺得現在不用建置系統,好像不可能(?)使用新版的 Tailwind。所以我這些年來一直都還在用 Tailwind v2。(聽說還有litewind
  • 本來就一直都是建議搭配建置系統使用 Tailwind,但我從來沒有真的那麼做,所以在很多專案裡我都有 2.8MB 的 tailwind.min.css 檔案(gzip 後 270K),感覺有點蠢。
  • 我現在的 CSS 能力比剛開始用 Tailwind 時好多了
  • 說到底 Tailwind 是有侷限的:如果你想在 CSS 裡搞些奇怪的花樣,用 Tailwind 不一定做得到。那些限制可能非常有用(這篇文章有很大一部分就是在講我如何重新實作 Tailwind 的一些限制!),但到這個階段,我會比較想自己選擇要保留哪些限制。
  • 最後我的網站變成在同一個專案裡混用原生 CSS 和 Tailwind,維護起來一點也不好玩
  • 我也開始好奇,寫更語意化的 HTML 會是什麼感覺。

我感到好奇的 CSS 功能

在做這件事的過程中,我認識了很多還沒用到、但以後想找機會學學看的 CSS 功能:

最後一個我離開 Tailwind 的原因

在這篇文章裡我講了很多從使用 Tailwind 中學到的東西,那些都是真的。

但我在 3 年前讀過一篇叫做Tailwind and the Femininity of CSS 的文章,一直讓我印象深刻。老實說,我一開始對 CSS 的態度可能就跟那篇文章描述的有點像:

他們聽說它很簡單,所以就以為它很容易。但當他們試著使用時,卻行不通。這一定是語言本身的錯,因為他們知道自己很聰明,而這東西本來就應該很簡單。

但在過去 10 年裡,我真的學會了熱愛並尊重 CSS 這項技術。

所以多年前我就決定,與其貶低 CSS,不如用「把 CSS 學好、認真把它當作一門技術」來回應「CSS 很難」這句話。這麼做徹底改變了我:我發現許多曾讓我挫折的問題(像是「置中根本不可能」)其實早就在 CSS 裡被解決了,也理解到「置中」到底是什麼意思本來就不總是那麼直觀,有很多種做法是很合理的。CSS 會很難,是因為它在解決一個很難的問題!

過去 10 到 15 年來建立的那些新 CSS 功能(其中一些我在這篇文章裡有提到!)以及它們如何讓 CSS 變得更好用,真的讓我非常驚艷,而花時間提升自己的 CSS 能力也是一段非常酷的體驗。

而那篇文章讓我覺得,Tailwind 助長了對 CSS 專業的貶低,而那不是我想參與的事,即使 Tailwind 對我個人來說曾是個有用的工具。尤其在這個 LLM 的時代,我覺得重視人類的專業比以往任何時候都更重要。

另一篇影響我的、批評 Tailwind 的部落格文章:

就先說到這裡!

感謝Melody Starling,她最初為wizardzines.com 設計並撰寫了 CSS,網站上所有酷炫有趣的地方都要歸功於 Melody。

另外,在進行這次遷移的過程中,我讀了好多超棒的 CSS 部落格文章(來自CSS TricksSmashing Magazine 等等),我在文章中試著附上了其中一些連結,也非常感謝 CSS 社群的朋友們如此慷慨地分享他們的實務經驗。

本文章由 muse-spark-1.2-contributor 進行翻譯

留言