技術
Tailwind v4 の @theme inline で、1つのサイトに複数の世界を置く
1つのサイトの中に、明るい紙面と、アプリごとに色の違う暗いページが同居しています。CSS は1本のままです。Tailwind v4 の @theme inline で、トークンの名前は共通にして値だけを差し替えています。
前は、レイアウトごと分けていました
--background のような同じ名前の変数が衝突するので、最初はページの種類ごとに CSS とルートレイアウトを分けていました。動きはします。ただ、代償がありました。
- 世界をまたぐ移動が毎回フルリロードになる
- アプリが増えるほど、ルート要素とフォント読み込みがN 重になる
アプリが3つになった時点で、この方式は続けられないと分かりました。
@theme inline は「値」ではなく「参照」を宣言する
@theme に値を書くと、Tailwind はその値をユーティリティに焼き込みます。inline を付けると、素の CSS 変数への参照として宣言されます。
@theme inline {
/* 値を焼き込まず、素の CSS 変数への「参照」として宣言する */
--color-background: var(--background);
--color-foreground: var(--foreground);
}
:root { --background: #ffffff; } /* 既定の世界 */
[data-theme='resuru'] { --background: #060e20; } /* この中だけ別の世界 */これで bg-background は background-color: var(--background) になります。どこで宣言された値でも、後から効きます。 器に data-theme を立てるだけで、その内側だけ世界が変わります。
CSS が1本のままなので、レイアウトを分ける必要がなくなり、世界をまたいでもソフト遷移で切り替わるようになりました。
角丸だけは、各段を独立した変数にする
ここで一度崩しました。角丸を1つの基準値から計算する形にしていたのですが、アプリごとに各段の値が違います(アプリ側の実装からそのまま写しているため)。
/* 角丸は各段を独立した変数にする。計算式を焼き込むと崩れる */
--radius-sm: var(--r-sm);
--radius-md: var(--r-md);
--radius-lg: var(--r-lg);計算式を @theme に焼き込むと、差し替えられるのは基準値だけになり、段ごとの違いが再現できません。各段を独立した変数にしておく必要があります。
書体だけは、器の側でも指定し直す
もう1つ、器を作ったときに気づいたことがあります。フォントは「継承された計算済みの値」として降りてきます。ルート要素の時点で var(--font-body) が解決されてしまうので、内側で変数を差し替えても書体は変わりません。
器の側でもう一度 font-sans を当てて、そこで解決し直しています。
OS の設定では見た目を変えない
明るさは「いまどの世界にいるか」だけで決まります。紹介ページは常に暗く、それ以外は常に明るい。OS のダークモードには反応させません。
既定のままだと、外部から持ってきたコンポーネントが持つ dark: 付きの指定が OS 設定に反応して勝手に効いてしまうので、その条件を決して一致しないものに縛ってあります。