Tailwind CSS @apply 自訂元件樣式教學:從 Utility 到可複用元件的最佳實踐
用 Tailwind CSS 寫了一陣子之後,你一定會遇到一個問題:同樣的 utility class 組合在好幾個地方重複出現,改一個地方就要改好幾處。這時候 @apply 就派上用場了。不過老實說,@apply 的使用時機和方式一直是社群裡很有爭議的話題,今天我就來分享我自己在實戰中摸索出來的最佳實踐。
@apply 到底是什麼
簡單來說,@apply 是 Tailwind 提供的一個 CSS 指令,讓你在傳統 CSS 檔案裡使用 Tailwind 的 utility class。舉個例子:
.btn-primary {
@apply px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors;
}這樣你就把一堆 utility class 封裝成一個語意化的 class 名稱了。看起來很方便對吧?但先別急著到處用——什麼時候該用、什麼時候不該用,這裡面有很多眉角。如果你剛從 v3 升上來,建議先看看Tailwind CSS v4 的新功能與遷移指南,因為 v4 對 @apply 的處理有一些變化。
什麼時候該用 @apply
高度重複的樣式模式
當你發現同一組 class 在超過三個地方出現時,就是考慮用 @apply 的時候了。特別是那些不太會變動的基礎元件,像是按鈕、輸入框、卡片容器等。我的原則是:如果這個樣式組合已經穩定下來,不太需要針對個別使用場景做調整,那就封裝它。
第三方元件的樣式覆寫
這是我覺得 @apply 最適合的場景。當你用了第三方套件(比如日期選擇器、富文字編輯器),需要覆寫它們的預設樣式時,用 @apply 比寫一堆原始 CSS 要方便得多,而且風格能和你的 Tailwind 主題保持一致。
什麼時候不該用 @apply
Tailwind 的作者 Adam Wathon 本人都說過,@apply 基本上是一個逃生出口,不應該成為你的主要寫法。以下幾種情況我建議避免使用:
- 元件框架已經提供了封裝機制——React、Vue、Svelte 的元件本身就是最好的樣式封裝方式,直接在 JSX/template 上寫 utility class 就好。
- 每個使用場景都需要微調——如果你封裝了
.card但每次用都要再加一堆額外的 class,那封裝的意義就不大了。 - 只是為了讓 HTML 看起來乾淨——「class 太長很醜」不是使用
@apply的好理由。
實戰元件封裝模式
按鈕變體系統
按鈕是最常被封裝的元件。我的做法是建立一個基礎樣式,然後用修飾詞 class 來處理變體:
.btn {
@apply inline-flex items-center justify-center rounded-lg font-medium
transition-all duration-200 focus:outline-none focus:ring-2
focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-primary {
@apply btn bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500;
}
.btn-secondary {
@apply btn bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-500;
}
.btn-sm { @apply btn text-sm px-3 py-1.5; }
.btn-md { @apply btn text-base px-4 py-2; }
.btn-lg { @apply btn text-lg px-6 py-3; }但說實話,現在我更推薦用 CVA(Class Variance Authority)這個套件來管理按鈕變體。程式碼更清楚,TypeScript 支援也更好。
表單元素統一
表單元素的一致性非常重要,特別是當你的專案需要支援深色模式的時候。我會建立一組基礎表單樣式:
.input-base {
@apply w-full rounded-lg border border-gray-300 px-4 py-2.5
text-gray-900 placeholder:text-gray-400
focus:border-blue-500 focus:ring-1 focus:ring-blue-500
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100;
}這樣所有表單元素——輸入框、選擇器、文字區域——都有統一的視覺風格,維護起來也很方便。
CVA:@apply 的現代替代方案
如果你的專案是用 React 或 Vue 這類元件框架,我強烈建議用 CVA 來取代大部分的 @apply 用法。CVA 讓你在 JavaScript 層面管理樣式變體,搭配 tailwind-merge 還能智慧地處理 class 衝突。這也是目前像 shadcn/ui 等元件庫採用的主流做法。
import { cva } from 'class-variance-authority';
const button = cva('inline-flex items-center rounded-lg font-medium', {
variants: {
intent: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200',
},
size: {
sm: 'text-sm px-3 py-1.5',
md: 'text-base px-4 py-2',
},
},
defaultVariants: { intent: 'primary', size: 'md' },
});Tailwind v4 中的 @apply 變化
Tailwind v4 做了一些底層架構的調整,@apply 的行為也有些不同。最大的變化是 v4 採用了新的引擎,CSS 的處理速度快了很多。另外,v4 引入了Container Queries 等新功能,搭配 @apply 可以做出更靈活的響應式元件。不過要注意的是,某些 v3 的 @apply 寫法在 v4 可能需要調整語法。
我的最佳實踐總結
經過幾十個專案的摸索,我整理出這些原則:
- 框架元件優先——能用 React/Vue 元件封裝的,就不用
@apply。 - 全域基礎樣式用 @apply——typography、form reset、base components 這些適合放在全域 CSS 裡用
@apply。 - 變體管理用 CVA——需要多種變體的元件,CVA 比
@apply更好維護。 - 保持扁平——不要讓
@apply的 class 裡又引用另一個@apply的 class,嵌套會讓除錯變得很痛苦。 - 文件化你的設計 token——無論用哪種方式封裝,都要確保團隊知道有哪些可複用的樣式。
總之,@apply 是個好工具,但它最好的定位是「安全網」而不是「主力武器」。搞清楚它的適用場景,你的 Tailwind 專案會更好維護,開發效率也會更高。
繼續閱讀
Tailwind CSS 元件庫推薦:daisyUI、Headless UI、shadcn/ui 完整比較
Tailwind CSS 元件庫太多不知道怎麼選?這篇幫你比較 daisyUI、shadcn/ui、Headless UI 三大方案的差異。
相關文章
你可能也喜歡
探索其他領域的精選好文