2024-03-24|閱讀時間 ‧ 約 28 分鐘

善用 data-attributes 為 Tailwind CSS 設定動態樣式

Tailwind CSS 為什麼受歡迎

Tailwind CSS 是熱門的CSS框架,最大的賣點在於「原子化」,也就是「一個class 對應到一個css 樣式」。使用 Tailwind CSS 後,開發者再也不用花時間思考class的名稱,只要在元件中的 markup 寫好構成樣式所需的 class 名稱,Tailwind 就會在 build 的時候生成對應的css樣式,像這樣:

<p className="text-3xl font-bold text-blue-500">Hello World</p>;

搭配 Webpack 或 Vite 等打包工具,執行 build 命令後, Tailwind 就會幫我們在 build 之後的靜態檔案裡新增樣式:

.text-blue-500 {
--tw-text-opacity: 1;
color: rgb(59 130 246 / var(--tw-text-opacity));
}

Tailwind CSS 會根據 class 名稱,生成對應樣式。

此外,Tailwind CSS 在生成樣式時,有寫出的class才會生成對應的 CSS,因此不用擔心有像 BootStrap 等框架在網頁打包時,套件的css會讓檔案變得臃腫的情況。Tailwind 的好處還有避免同名的class之間樣式衝突等等,但這非本文重點,故不贅述。筆者希望把重點放在 Tailwind 難以解決的問題,和其解決方案上。


不易設定動態樣式

Tailwind 在 build 時生成樣式,因此要因應 client-side 的 state 或 prop 變化動態產生樣式會比較麻煩。面對這個問題,其中一種做法是使用如 clsx 的第三方套件,透過 JavaScript 在 runtime 時根據元件的狀態將 class 名稱組合成對應的樣式。


clsx 套件

clsx 是一個專門處理 CSS class 名稱的套件,他可以將物件、陣列甚至條件判斷轉換成字串,並計算出最終的 class 名稱。搭配 Tailwind 使用,可以讓動態樣式設定的開發者體驗更好:

const buttonClass = clsx("py-2 px-4 font-semibold rounded-lg text-white", {
"bg-blue-500": status === "Primary",
"bg-green-500": status === "Success",
"bg-red-500": status === "Danger",
});

return (
<div>
<button className={buttonClass}>{status}</button>
</div>
);

雖然 clsx 和 Tailwind 搭配已經十分好用,即使套件本身非常輕量化,畢竟仍會在打包時產生一些 JavaScript。那麼,有沒有不用第三方套件產生動態樣式的解決方案呢?


用data-* attribute 動態生成樣式

所幸 Tailwind CSS 在 3.2 版推出用 data-* attributes 動態生成樣式的方法。以下舉 React component 為例,說明如何透過 data 屬性的綁定,生成動態的 Tailwind classes:

import { useState, useEffect, useRef } from "react";

import Checkbox from "./Checkbox";

export default function App() {
const [status, setStatus] = useState("Primary");

const intervalRef = useRef(null);
useEffect(() => {
function switchBtnBackground(color) {
switch (color) {
case "Primary": {
return "Success";
}
case "Success": {
return "Danger";
}
case "Danger": {
return "Primary";
}
}
}

intervalRef.current = setInterval(() => {
setStatus((t) => switchBtnBackground(t));
}, 1500);

return () => clearInterval(intervalRef.current);
}, []);

return (
<div className="text-2xl border w-64">
<button
data-status={status}
className="w-full data-[status=Primary]:bg-[#007bff] text-white data-[status=Success]:bg-[#28a745] data-[status=Danger]:bg-[#dc3545] px-4 py-2 border-gray-500 rounded-sm text-center"
>
{btnText}
</button>
</div>
);
}


data attribute 是 HTML 內建的元素屬性,可以自定義元素的屬性,將網頁的狀態綁定在元素上。如上面的例子中,筆者將元件的狀態 status 綁定在按鈕上,當元件的狀態改變時,data-status 就會跟著改變。而 Tailwind 讓 data attribute 成為一種狀態,就像 media query 的 md/xl 等一樣,後面可以加上隨著data attribute值變化所套用的class 名稱,進而達到動態產生樣式的目的。

按鈕根據元件狀態變化,會有不同背景顏色。



總結

data-attribute 讓使用Tailwind CSS 制定動態樣式變得更容易,且不需要依賴第三方套件,看起來十分美好。但 Tailwind 官方有提到:data attribute 只接受 arbitrary values,也就是開發者自定義的值,這也就代表會在 build 的時候額外生成classes。因此,如果元件內有大量的互動,造成樣式多變,使用 data attribute 也許不一定能減少打包檔案的大小。筆者認為,像clsx這樣處理多變樣式的套件,仍有其優勢,而data-* 屬性則適合在元件的動態較不複雜時使用。

arbitrary value 會在build 時生成額外的class,因此過度使用會讓css打包檔案變得很肥。



參考資料

CLEANER Tailwind classes

Tailwind CSS v3.2: Dynamic breakpoints, multi-config, and container queries, oh my!

https://tailwindcss.com/blog/tailwindcss-v3-2#data-attribute-variants


分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.