🌗

TailwindCSSのカラーテーマでlight-dark()を使う

2024/04/01に公開

はじめに

ダークモードのスタイルを指定する方法はいくつかありますが、そのうちのひとつに light-dark() 関数というものがあります。
この関数で、ひとつのクラスにライトモード、ダークモードそれぞれの色を同時に指定できるので、カスタムプロパティーで切り替える方法の代替として使えそうな、そんな予感がします。

そこでこの light-dark() 関数をTailwindCSSで使う方法をいくつか書いてみたいとおもいます。

https://developer.mozilla.org/ja/docs/Web/CSS/color_value/light-dark

その1:べたがき

書いたそのまま反映されます。簡単です。

tailwind.config.js
{
  // ...
  theme: {
    extend: {
      colors: {
        background: "light-dark(#FFE500, #1C1A00)",
        foreground: "light-dark(#1C1A00, #FFE500)",
      },
    },
  },
  // ...
}
global.css
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  color-scheme: light dark;
}

.dark {
  color-scheme: dark;
}

.light {
  color-scheme: light;
}

index.tsx
<p className="bg-background text-foreground py-20 text-4xl font-bold text-center">
  light-dark();
</p>


ライトモード

ダークモード

書くまでもないので飛ばそうかとも思いましたが、この書き方には一つ問題点があり、モディファイアーで不透明度を指定することができません。

index.tsx
<p className="bg-background/50 text-foreground/50 py-20 text-4xl font-bold text-center">
  light-dark();
</p>

カスタムプロパティーで色指定する場合に使用する <alpha-value> を使うパターンも一つしか置換してくれないようで、だめそうでした。

tailwind.config.js
{
  // ...
  theme: {
    extend: {
      colors: {
        background: "light-dark(rgb(242 228 22 / <alpha-value>), rgb(48 45 2 / <alpha-value>))",
        foreground: "light-dark(rgb(48 45 2 / <alpha-value>), rgb(242 228 22 / <alpha-value>))",
      },
    },
  },
  // ...
}

その2:プラグインを書く

こまったときのプラグインです。プラグインを書いてしまえば大抵のことは叶います。

light-dark.js
import plugin from "tailwindcss/plugin";
import { withAlphaValue } from "tailwindcss/lib/util/withAlphaVariable";

export default function LightDark() {
  return plugin(({ matchUtilities, theme }) => {
    matchUtilities(
      {
        bg: (value, { modifier }) => {
          return {
            backgroundColor: generateLightDark(value, modifier ?? 1),
          };
        },
        text: (value, { modifier }) => {
          return {
            color: generateLightDark(value, modifier ?? 1),
          };
        },
      },
      {
        values: {
          // ...theme("lightDark")
          background: ["#FFE500", "#1C1A00"],
          foreground: ["#1C1A00", "#FFE500"],
        },
        type: ["color", "any"],
        modifiers: theme("opacity"),
      }
    );
  });
}

function generateLightDark([light, dark], alpha) {
  return `light-dark(${withAlphaValue(light, alpha)}, ${withAlphaValue(
    dark,
    alpha
  )})`;
}

withAlphaValue といういろんなフォーマットの色の値を上手にrgba形式に変えてくれる関数があるので比較的簡単に実装できます。


ライトモード(不透明度50%)

このやりかたの問題点は、bg, text, border, border-t, border-l, ...と定義していくのが少し、いやとてもめんどくさいところです。

その3:関数を指定する

最後に関数を指定するやりかたです。
内容はプラグインと似ています。

カラーテーマに関数を指定すると引数に不透明度 {opacityValue: string} を指定して呼び出してくれるので、プラグインのときに実装した generateLightDark で値を生成して返すようにします。

light-dark.js
import { withAlphaValue } from "tailwindcss/lib/util/withAlphaVariable";

export function lightDark(light, dark) {
  return ({ opacityValue }) => generateLightDark([light, dark], opacityValue);
}

function generateLightDark([light, dark], alpha) {
  return `light-dark(${withAlphaValue(light, alpha)}, ${withAlphaValue(
    dark,
    alpha
  )})`;
}

この関数を使って以下のように指定します。
べたがきと似てシンプルに書けます。

tailwind.config.js
{
  // ...
  theme: {
    extend: {
      colors: {
        background: lightDark("#FFE500", "#1C1A00"),
        foreground: lightDark("#1C1A00", "#FFE500"),
      },
    },
  },
  // ...
}

おしまい

そもそも不透明度を使うのかという疑問を残しつつスパッと終わりたいと思います。

(なんだか知らないうちにいろんなCSSが追加されている🫠)

Discussion