🍃

TailwindCSSの重複したクラス名を上書き出来るようにする

2021/04/05に公開
2

結論

以下のような関数を作成して、クラス名を作成する際に使うと良いです 🏄‍♀️🏄‍♂️

twcx()を定義
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()の使用方法は、以下の通りです 👇

twcx()を使用する
// デフォルトで青文字を表示し、上書きできるようにする
const ColorText: React.FC<{ className?: string }> = ({ className = "" }) => (
  <h1 className={twcx("text-blue-400", className)}>これはサンプルです</h1>
);

また優先されるクラス名は、後に渡された引数・後に書かれたモノほど優先的に残します ⏬

twcx()の実行例
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"

https://github.com/lukeed/clsx

具体的な解説

Next.jsTailwind CSSを使っている環境で、以下のソースコードを実行してみました。

./pages/index.tsx
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 のコンソール画面で確認してみると、、、

Chromeのコンソール画面の表示内容
<h1 class="text-blue-400 text-red-400">これはサンプルです</h1>

ちゃんとクラス名が付いています。

しかし、よく見てみるとtext-blue-400text-red-400が競合してしまっています。
「 じゃあ、順番の問題かな? 」と思って、<ColorText />を以下のように修正しました。

classNameの順番を変更
const ColorText: React.FC<{ className?:string }> = ({ className = "" }) => (
- <h1 className={`text-blue-400 ${className}`}>これはサンプルです</h1>
+ <h1 className={`${className} text-blue-400`}>これはサンプルです</h1>
);

しかし、結果は変わらず。。。🤦‍♂️🤦‍♀️

解決方法

要は、同じ役割を持ったクラス名が重複して定義されているのが問題なので、クラス名の重複を削除する関数を定義して、その関数を使うようにします。

まずは、関数を作成します 🎢

twcx()を定義
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 />内で使うように修正します。

twcx()を使うように修正
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 内のコメントより拝借しました 👒

https://github.com/tailwindlabs/tailwindcss/discussions/1446


画像のデザインは、以下のサイトより作成したモノを使っています 🎨

https://neumorphism.io/#e0e0e0

あとがき

ここまで読んでくれてありがとうございます 🙏
記事に間違いなどがあれば、コメントなどで教えて頂けると嬉しいです。

これが誰かの参考になれば幸いです。
それではまた 👋

GitHubで編集を提案

Discussion