TailwindCSSの重複したクラス名を上書き出来るようにする
結論
以下のような関数を作成して、クラス名を作成する際に使うと良いです 🏄♀️🏄♂️
import clsx, { ClassValue } from "clsx";
// tailwindで使える色配列
const colors = ["red","gray","yellow","green","blue","indigo","purple","pink"];
function twcx(...classes: ClassValue[]) {
// 全体のクラス名を作成
const classlist = clsx(...classes).split(" ");
// 重複しているクラス名を削除する
const result = classlist.reduce((result, c) => {
const prefix = c
.substring(0, c.lastIndexOf("-") + 1 || c.length) // 可変部分を削除
.replace(new RegExp(`${colors.join("|")}`), "color"); // "color"に置き換える
return { ...result, [prefix]: c };
}, {});
// クラス名を再生成
return Object.values(result).join(" ");
}
上記のtwcx()
の使用方法は、以下の通りです 👇
// デフォルトで青文字を表示し、上書きできるようにする
const ColorText: React.FC<{ className?: string }> = ({ className = "" }) => (
<h1 className={twcx("text-blue-400", className)}>これはサンプルです</h1>
);
また優先されるクラス名は、後に渡された引数・後に書かれたモノほど優先的に残します ⏬
twcx("h-1 h-2 h-3"); // -> "h-3"
twcx("h-1", "h-2", "h-3"); // -> "h-3"
twcx("w-1 h-1", "h-2"); // -> "w-1 h-2"
twcx("w-1 h-1", "w-2 h-2"); // -> "w-2 h-2"
// colors
twcx("bg-blue-100 bg-blue-200"); // -> "text-blue-200"
twcx("bg-blue-100", "bg-red-100"); // -> "text-red-100"
具体的な解説
Next.jsとTailwind CSSを使っている環境で、以下のソースコードを実行してみました。
import React from "react";
/**
* @description デフォルトで青い文字として表示させる
*/
const ColorText: React.FC<{ className?: string }> = ({ className = "" }) => (
<h1 className={`text-blue-400 ${className}`}>これはサンプルです</h1>
);
/**
* @description <ColorText /> を赤文字で表示します
*/
export default function IndexPage() {
return (
<div className="body">
<div className="board">
<ColorText className="text-red-400" />
</div>
<style jsx>{`
.body {
top: 0;
width: 100%;
height: 100vh;
background: #ededed;
position: absolute;
}
.board {
text-align: center;
padding: 4rem 0;
margin: 12rem auto;
max-width: 200px;
border-radius: 28px;
background: #ededed;
box-shadow: 5px 5px 10px #c5c5c5, -5px -5px 10px #ffffff;
}
`}</style>
</div>
);
}
そして、上記の表示結果を確認してみた結果が以下の画像です 👇
見て分かる通り、本来は赤い文字を表示したかったのに青い文字が表示されています。
「 クラス名が要素に当てられてないのかなぁ 」と思って Chrome のコンソール画面で確認してみると、、、
<h1 class="text-blue-400 text-red-400">これはサンプルです</h1>
ちゃんとクラス名が付いています。
しかし、よく見てみるとtext-blue-400
とtext-red-400
が競合してしまっています。
「 じゃあ、順番の問題かな? 」と思って、<ColorText />
を以下のように修正しました。
const ColorText: React.FC<{ className?:string }> = ({ className = "" }) => (
- <h1 className={`text-blue-400 ${className}`}>これはサンプルです</h1>
+ <h1 className={`${className} text-blue-400`}>これはサンプルです</h1>
);
しかし、結果は変わらず。。。🤦♂️🤦♀️
解決方法
要は、同じ役割を持ったクラス名が重複して定義されているのが問題なので、クラス名の重複を削除する関数を定義して、その関数を使うようにします。
まずは、関数を作成します 🎢
import clsx, { ClassValue } from "clsx";
// tailwindで使える色配列
const colors = ["red","gray","yellow","green","blue","indigo","purple","pink"];
function twcx(...classes: ClassValue[]) {
// 全体のクラス名を作成
const classlist = clsx(...classes).split(" ");
// 重複しているクラス名を削除する
const result = classlist.reduce((result, c) => {
const prefix = c
.substring(0, c.lastIndexOf("-") + 1 || c.length) // 可変部分を削除
.replace(new RegExp(`${colors.join("|")}`), "color"); // "color"に置き換える
return { ...result, [prefix]: c };
}, {});
// クラス名を再生成
return Object.values(result).join(" ");
}
そして次に、twcx()
を上記で定義していた<ColorText />
内で使うように修正します。
const ColorText: React.FC<{ className?: string }> = ({ className = "" }) => (
- <h1 className={`text-blue-400 ${className}`}>これはサンプルです</h1>
+ <h1 className={twcx("text-blue-400", className)}>これはサンプルです</h1>
);
上記の修正を適用して再度実行すると、
無事、赤い文字にする事が出来ました。
めでたしめでたし 👏
参考
この記事は以下の Discussions を参考にさせて頂きました 💁♀️💁♂️
またtwcx()
の実装のほとんどは、Discussions 内のコメントより拝借しました 👒
画像のデザインは、以下のサイトより作成したモノを使っています 🎨
あとがき
ここまで読んでくれてありがとうございます 🙏
記事に間違いなどがあれば、コメントなどで教えて頂けると嬉しいです。
これが誰かの参考になれば幸いです。
それではまた 👋
Discussion
twMergeライブラリを使ったアプローチでもいいかもと思いました。
定義側
使用側
簡単ですが、以上です。
ありがとうございます!