React で Tailwind CSS v3.x を使っていて変数で動的に Tailwind CSS のクラス名を作成したらスタイルが適応されなかったのでメモ
環境
react@18.1.0
typescript@4.6.4
tailwindcss@3.1.2
動的にサイズを指定できる Spinner を作りかった。
<svg role="status" class="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M100 … 50.5908Z" fill="currentColor"/> <path d="M93.9676 … 39.0409Z" fill="currentFill"/> </svg>
このコンポーネントのサイズは <svg>
タグの w-{n} h-{n}
クラスを変更すれば良いっぽい
🙅 動作しない例
import { FC } from 'react'; type SizeType = 'xs' | 'sm' | 'md' | 'lg'; type SpinnerProps = { size?: SizeType; }; const getSize = (size: SizeType): number => { switch (size) { case 'xs': { return 4; } case 'sm': { return 6; } case 'md': { return 8; } case 'lg': { return 10; } default: { return 8; } } }; const Spinner: FC<SpinnerProps> = ({ size = 'md' }) => { const n = getSize(size); return ( <svg role='status' className={`inline w-${n} h-${n} mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600`} viewBox='0 0 100 101' fill='none' xmlns='http://www.w3.org/2000/svg' > <path d='M100 ... 50.5908Z' fill='currentColor' /> <path d='M93.9676 … 39.0409Z' fill='currentFill' /> </svg> ); }; export const App: FC = () => { return ( <Spinner size="md" /> ); };
<Spinner />
コンポーネントに付けられる w-6
, h-6
のスタイルがそもそも存在しない状態でした…
🙆 問題ない例
type SizeType = 'xs' | 'sm' | 'md' | 'lg'; type SpinnerProps = { size?: SizeType; }; const getSizeClass = (size: SizeType): string => { switch (size) { case 'xs': { return 'w-4 h-4'; } case 'sm': { return 'w-6 h-6'; } case 'md': { return 'w-8 h-8'; } case 'lg': { return 'w-10 h-10'; } default: { return 'w-8 h-8'; } } }; const Spinner: FC<SpinnerProps> = ({ size = 'md' }) => { const sizeClass = getSizeClass(size); return ( <svg role='status' className={`inline ${sizeClass} mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600`} viewBox='0 0 100 101' fill='none' xmlns='http://www.w3.org/2000/svg' > <path d='M100 ... 50.5908Z' fill='currentColor' /> <path d='M93.9676 … 39.0409Z' fill='currentFill' /> </svg> ); };
Tailwind CSS で定義されているクラス名そのものを動的に作るのではなく、存在しているクラス名の文字列が変数で渡される場合は問題なくスタイルが適応された
Tailwind CSS の賢い Migrating to the JIT engine との相性の問題
The new Just-in-Time engine we announced in March has replaced the classic engine in Tailwind CSS v3.0.
The new engine generates the styles you need for your project on-demand, and might necessitate some small changes to your project depending on how you have Tailwind configured.
cf. Upgrade Guide - Tailwind CSS
Tailwind CSS v3.0 からデフォルトで Just-in-Time engine が搭載されており、プロジェクトに必要なスタイルだけを動的に生成するようになっている
In tailwind you can't use dynamic class naming like
bg-${color}
, though we can mock it to be that, but it is not preffered. Because Tailwind compiles its CSS, it looks up over all of your code and checks if a class name matches.
cf. reactjs - Tailwind not working when using variables (React.js) - Stack Overflow
つまり、Tailwind CSS v3.0 から不要なクラスをバインドしないように、プロジェクトで使われているクラスに該当するスタイルだけを生成するようになった。スタイルの生成方法が恐らくコードを静的解析し Tailwind CSS のクラス名とマッチするものがあれば、そのスタイルを出力する仕組みになっている。
なので、Tailwind CSS のクラス名そのものを変数を使って動的に作成すると解析時にマッチせずスタイルが出力されない。ということっぽい!
- 🙅 NGだった例
w-{n}
は Tailwind CSS のクラス名とマッチしないのでw-4
,w-6
といったスタイルが一切生成されない - 🙆 OK だった例 はコード中に
w-4
,w-6
,w-8
という文字列があるので、実際にコンポーネントで使っているのがw-6
だけだったとしても、使ってないw-4
,w-8
のスタイルも出力される
という挙動になっていたのが理由だったようです。
utility ファーストな設計なので、使ってないクラスも全て読み込むと巨大な CSS を読み込むことになるのでいい感じにしてくれる仕組みのお作法を理解してなかったのが原因でした。
ドキュメントがうまく探せずなんでや〜ってなってました (ᐡ o̴̶̷̤ ﻌ o̴̶̷̤ ᐡ)
おわり
[参考]
- reactjs - Tailwind not working when using variables (React.js) - Stack Overflow
- Tailwind CSSでのjitモードではクラスに変数を使えないのでインラインスタイルを使う話
- Upgrade Guide - Tailwind CSS
- Just-In-Time: The Next Generation of Tailwind CSS – Tailwind CSS
- ViteでReact + TypeScript + TailwindCSSの環境構築をする