使用 shadcn/ui 前該補的 TailwindCSS 基礎知識(四) - 深入 TailwindCSS v4 的進階配置

本文是「使用 shadcn/ui 前該補的 TailwindCSS 基礎知識」系列文章的第四篇
系列文章:
- 從 MUI 到 TailwindCSS 設計哲學的轉變
- 理解 TailwindCSS 的運作原理
- TailwindCSS v4 基礎語法速查
- 深入 TailwindCSS v4 的進階配置(本篇)
- shadcn/ui 生態系工具鏈
在前三篇文章中,我們已經理解了從 MUI 到 TailwindCSS 的設計哲學轉變、TailwindCSS 的運作原理,以及基礎 語法的使用方式。現在,是時候深入探討 TailwindCSS v4 的進階配置了。
本篇文章將以 shadcn/ui 範例程式碼的 index.css 或 globals.css 為例,解析 TailwindCSS v4 的進階語法,如:@theme inline、@layer、@custom-variant,並介紹亮暗模式的實現原理。
@theme:定義設計 Token
什麼是 Design Token?
在 shadcn/ui 範例程式碼的 index.css 或 globals.css 中,經常會看到類似這樣的程式碼:
@theme inline {
--color-primary: var(--primary);
--radius-lg: var(--radius);
}
:root {
--primary: oklch(0.6171 0.1375 39.0427);
--radius: 0.5rem;
}
.dark {
--primary: oklch(0.8 0.15 250);
}
剛開始用 shadcn/ui 時,我大概能猜得到 :root 和 .dark 是在定義主題變數,但還是有幾個疑問:
@theme inline是什麼?看起來也挺像主題變數的,跟:root有什麼差別?- 這些變數如何變成 TailwindCSS 的 utility class?
這些變數其實就是 Design Token,它就像是設計系統的「變數庫」。在設計一個網站時,會需要定義:
- 主色調、次要色調
- 不同大小的間距
- 統一的圓角大小
- 陰影效果
這些都可以定義成 Token,然後在整個專案中重複使用。@theme 就是 TailwindCSS v4 提供的工具,用來將這些 Design Token 轉換成可用的 utility class。
@theme 的基本語法與使用方式
以下是一段範例程式碼:
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-primary: var(--primary);
--color-secondary: var(--secondary);
--color-destructive: var(--destructive);
}
:root {
--radius: 0.5rem;
--primary: oklch(0.6171 0.1375 39.0427);
--secondary: oklch(0.9245 0.0138 92.9892);
}
這段程式碼做了什麼?
Token 轉換成 Utility Class 的規則
從上面的例子我們可以看出,定義了 --color-primary 後,在 React 或 HTML 中就可以使用 bg-primary。這背後有一套明確的命名轉換規則。
關於各種 Token 類型的詳細用法,可以參考 TailwindCSS v4 官方文件 - Theme 以及本系列的第三篇文章:TailwindCSS v4 基礎語法速查。
規則 1:移除類型前綴
Token 名稱的類型前綴(如 --color-、--spacing-、--radius- 等)在轉換成 utility class 時會被移除,只保留語意名稱。
規則 2:配合屬性前綴使用
不同的 CSS 屬性有不同的 TailwindCSS 前綴,需要搭配使用:
| Token 類型 | 定義範例 | 使用方式 |
|---|---|---|
| 顏色 | --color-brand | bg-brand、text-brand、border-brand |
| 間距 | --spacing-lg | p-lg、m-lg、gap-lg |
| 圓角 | --radius-card | rounded-card |
| 寬度 | --width-sidebar | w-sidebar、min-w-sidebar |
| 高度 | --height-header | h-header、max-h-header |
| 字型 | --font-family-heading | font-heading |
| 字體大小 | --font-size-xl | text-xl |
| 字重 | --font-weight-bold | font-bold |
| 陰影 | --shadow-card | shadow-card |
| 透明度 | --opacity-soft | opacity-soft |
實際範例:
/* 在 index.css 定義 */
@theme inline {
--color-brand: #ff6b6b;
--spacing-card: 1.5rem;
--radius-button: 0.375rem;
--font-family-heading: "Inter", sans-serif;
--font-size-xl: 1.25rem;
--shadow-elevated: 0 4px 6px rgba(0, 0, 0, 0.1);
--width-sidebar: 16rem;
}
// 在 React/HTML 中使用
function Card() {
return (
<div
className="
bg-brand {/* --color-brand */}
p-card {/* --spacing-card */}
rounded-button {/* --radius-button */}
shadow-elevated {/* --shadow-elevated */}
w-sidebar {/* --width-sidebar */}
"
>
<h1 className="font-heading text-xl">
{" "}
{/* --font-family-heading, --font-size-xl */}
標題
</h1>
</div>
);
}
圓角的轉換規則有一個特別之處:Token 定義時使用 --radius-,但對應的 utility class 前綴是 rounded-:
--radius-lg→rounded-lg(Token 用radius,utility 用rounded)--radius-button→rounded-button
這與其他 Token 不同,例如顏 色:
--color-primary→bg-primary(都是color相關)
@theme inline:inline 關鍵字的作用
在前面的範例中,我們看到的都是 @theme inline,而不是單純的 @theme。這兩者之間的差異在於是否保留 CSS 變數的動態特性,而這個差異將會直接影響了暗色模式等主題切換功能的實現(會在後面的「方法二:使用語意化顏色」章節中詳細介紹)。
inline 關鍵字的作用:保留動態特性
inline 這個關鍵字非常重要,它告訴 Tailwind:「這些主題變數的值是『動態的』,它們的值會在瀏覽器執行時 (runtime) 透過引用其他 CSS 變數來決定,而不是在編譯時 (build time) 就固定下來。」
讓我們用實際範例來理解:
不加 inline 的情況:
/* 定義 */
@theme {
--color-primary: var(--primary);
}
:root {
--primary: oklch(0.6171 0.1375 39.0427);
}
.dark {
--primary: oklch(0.8 0.15 250);
}