見出し画像

Tailwind CSSで動的なカラー設定をする方法

株式会社カンリーエンジニア部の角谷( @motsuo373 )です。

最近Tailwind CSSの流れがきてると思いきや、Zero runtime CSS in JSという新しい流行りがあると先輩に教えられて、フロントエンドにおけるCSSフレームワークは何が一番ベストなんだろう。
そんなことを考えたり考えなかったりしているフロントエンドエンジニアです。

今回は弊社のプロダクトで使用している、Tailwind CSSの記事になります。
業務で要件に見合う方法を模索していたのですが、その方法がまとまっている記事が少なかったため、今回実装した内容を共有できればと思い執筆いたしました。


要件&デモ

達成したい要件として、

  • 複数クライアントに同一プロジェクトの機能を利用してもらう

  • APIで取得した値を参照し、クライアントの任意カラーに設定する。

  • JITモードでクライアントごとにカラーを付与させるのはなるべく避ける

まずは、実際の動作している環境を見ていただきたいと思います。
(動作環境はNext.js + typescript + Tailwind CSSの環境です。)


続いて、要件に至る背景を簡略に説明してきたいと思います。

複数クライアントに同一プロジェクトの機能を利用してもらう

Tailwind CSSにはデフォルトでスタイルの設定がされており、tailwind.config.js のextend部分にカラーを追加することで、特定のカラーを付与することが可能です。

しかしカンリーホームページでは、クライアントごとにカスタマイズしたカラーリングを設定できる仕様になっております。

同一機能でクライアント単位でカスタムカラーを行う。

APIで取得した値を参照し、クライアントの任意カラーに設定する

APIでデータを取得し、クライアントのカスタマイズされた情報を読み取り、データを元に適切にカラーを割り当てる機能を作成します。

今回作成したデモでは、擬似的にAPIからデータを参照できたファイルをmock.jsonとしてJSONで用意しております。

{
  "companyId": 0,
  "sampleCompanyData": [
    {
      "id": 0,
      "name": "カンリーHP",
      "primaryColor": "318BF7",
      "textColor": "000000",
      "buttonColor": "E6F1FE",
      "backgroundColor": "FFFFFF"
    },
    {
      "id": 1,
      "name": "カンリー居酒屋",
      "primaryColor": "DD5A0F",
      "textColor": "000000",
      "buttonColor": "EFA54B",
      "backgroundColor": "FFFFFF"
    },
    {
      "//": "省略"
    }
  ]
}

JITモードでクライアントごとにカラーを付与させるのはなるべく避ける

一番初めにカラーを個別に付与する方法としてJITモード(Just-in-Time Mode)があげられます。
この機能は、指定したカラーを生成して利用することができるようになる素晴らしい機能ですが、
複数クライアントにテーマだけカスタマイズした状態で提供したい。
という要件があり、都度クライアントごとにマークアップの変更をすることは、クライアント数が増えていくごとに作業もその工数分かかり膨らんでいくため、

// 文字をprimaryで指定したカラーに変更する。
<p className="text-primary">The quick brown fox...</p>

tailwind.config.jsで設定をすることで、上記のコードのように指定したカラーを任意に設定できないかと考えました。
こうすることで、style部分の冗長を防ぐこともでき、クライアントの意向で急遽カラーに変更が入ってもカラーが切り替えられるようになりました。

また、不透明度については、結果としてJITモードを利用して対応している例も 実際に使用してみる にて紹介しております。

どのように対応したか

今回私たちのソリューションとして、styleタグにCSSカスタムプロパティを利用してカラーを埋め込み、tailwind.config.jsで読み取れるようにして対応しました。
また、hexToRgb というようにCSSのrgbの記法で使用できるように置換をしてくれるヘルパー関数を用意しておきました。

今回は、tailwind.config.jsのextend下で4つのカラー設定を追加しました。

// pages/index.tsx

// 省略

// 16進数から10進数に変換
const hexToRgb = (hex: string) => {
  const result = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result
    ? `${parseInt(result[1], 16)},
      ${parseInt(result[2], 16)},
      ${parseInt(result[3], 16)}`
    : null;
};

const IndexPage: React.FC<Props> = (props) => {
  const systemSettings = props.systemSetting;
  const [companyId, setCompanyId] = useState(props.companySetting);
  // serverSideからデータを取得し、指定された会社のカラーを取得する
  const pageColor = props.systemSetting[companyId];

  return (
    <Layout>
      <Head>
        <style>
          :root
          {`{--primary: ${hexToRgb(pageColor.primaryColor)};
          --text: ${hexToRgb(pageColor.textColor)};
          --button: ${hexToRgb(pageColor.buttonColor)};
          --background: ${hexToRgb(pageColor.backgroundColor)};
          }`}
        </style>
      </Head>
      <Card />
      <div className="flex flex-col items-center w-full gap-2 mt-12">
        <a className="text-text">デザイン変更</a>
        <Dropdown systemSettings={systemSettings} setCompanyId={setCompanyId} />
      </div>
    </Layout>
  );
};
// tailwind.config.js

module.exports = {
  mode: "jit",
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  darkMode: "media",
  theme: {
    extend: {
      colors: {
        primary: "rgb(var(--primary))",
        text: "rgb(var(--text))",
        button: "rgb(var(--button))",
        background: "rgb(var(--background))",
      },
    },
  },
  plugins: [],
};

実際に使用してみる

テキストのカラーを変更するためには、下記のようにコードを書きます。
他のテキストカラー同様、カスタマイズしたカラーを変更させることができるようになりました。

一番下の例のようにtailwind.config.jsでのカスタムを利用していませんが、JITモードとstyleタグにcssの変数を展開する手法を用いて透過もすることも可能です。
もちろん、tailwind.config.js側でrgbではなく、rgbaを用いることでも実装可能です。

// テキストカラーをprimaryカラーに変更
<h5 className="text-primary"></h5>
// 作成したセレクトボックスをbuttonカラーに変更、テキストカラーをtextカラーに変更
<select
  className="text-text bg-button"
>
// バックグラウンドをprimaryカラーに変更
<header className="bg-primary">
// バックグラウンドをprimaryカラーの透過20%のカラーに変更
<footer className="text-text bg-[color:rgba(var(--primary),0.2)]">


//tailwind.config.jsに下記を追加
primary20: "rgba(var(--primary),0.2)"
//primary20を使用する
<footer className="text-text bg-primary20">

[おまけ]styleタグにcssの変数を展開する手法以外はなかったのか。

今回、styleタグにcssの変数を展開する手法を利用した方法をご紹介しました。
しかし、他に方法はなかったのでしょうか。
当初、tailwind.config.js自体に直接変数を渡す方法を試してみたのですが、カラーは適応されず困っていました。

Tailwind CSSはスタイルを適用するためのクラスを事前に生成をしています。そのためクラスはビルド時に生成されるため、CSSは静的なファイルとなります。Tailwind CSSはビルド時に存在するすべてのクラスのみを生成するようになっています。

最初に実装した方法では、CSS自体に変数を使用して動的に色を設定しようとしていました。この方法では、ページがロードされたときにJavaScriptによって動的に設定されます。しかし、Tailwind CSSのクラスはビルド時に生成されるため、この動的な値を直接Tailwindのクラスに反映することができませんでした。
そのため今回ご紹介した、styleタグにcssの変数を展開する手法で対応する流れになりました。

さいごに

いかがでしたでしょうか。
今回は、動的にTailwind CSSのカスタムカラーを実装する方法についてご紹介いたしました。
今後も、開発の中で見つけたtips等があればご紹介していきたいと思います。

株式会社カンリーでは一緒に働く仲間を募集しています!カンリーのバリューに共感できる方、ちょっと話を聞いてみたいという方、ぜひご応募ください!

参考


いいなと思ったら応援しよう!