CSS-in-JS とは
CSS のスタイル定義を JavaScript(TypeScript)のコードとして書き、JavaScript に CSS を生成・管理させる仕組みです。
以下の記事で CSS-in-JS について紹介していますので、併せてご覧ください。
ゼロランタイム CSS-in-JS とは
styled-components や Emotion などの「ランタイム CSS-in-JS」は、コンポーネントがレンダリングされるたびに、JavaScript が CSS を生成してブラウザに挿入します。これは書きやすい反面、レンダリング時に JS の実行コストがかかるというデメリットがありました。
対して、「ゼロランタイム CSS-in-JS」は、このレンダリング時に JS が CSS を生成するという処理そのものをなくします。代わりに、ビルド時にコード内のスタイル定義を静的な .css ファイルへ変換してしまいます。ブラウザが受け取るのは普通の CSS ファイルなので、実行時には CSS-in-JS のための処理が一切走りません。
代表的なライブラリに vanilla-extract、Panda CSS、StyleX などがあります。
本記事では vanilla-extract を例として紹介します。
vanilla-extract の導入方法
@vanilla-extract/css と @vanilla-extract/vite-plugin をインストールします。
npm install --save-dev @vanilla-extract/css @vanilla-extract/vite-plugin
Vite のプラグイン設定に vanillaExtractPlugin を追加します。
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'
export default defineConfig({
plugins: [react(), vanillaExtractPlugin()],
})
vanilla-extract の使い方
■ スタイルを定義する
vanilla-extract では、スタイルを .css.ts という専用の拡張子のファイルに書きます。
// Button.css.ts
import { style } from "@vanilla-extract/css";
export const button = style({
padding: "8px 16px",
backgroundColor: "#2563eb",
color: "#fff",
});
作成したスタイルをコンポーネントから呼び出すには、
import { button } from "./Button.css";
export default function Button() {
return <button className={button}>Click me!</button>;
}
ビルドを実行してみると、dist/assets/index-68BF59UU.css のような、.css.ts ファイルを元に作られた静的な CSS ファイルが作られます。これを dist/index.html から呼び出しています。
これがビルド時に静的な CSS ファイルを生成する「ゼロランタイム CSS-in-JS」の挙動です。
■ 動的スタイルを定義する
ビルド時に静的 CSS へ変換される都合上、以下の Emotion(ランタイム CSS-in-JS)の例のように「実行時にしか分からない値」をスタイル定義の中に直接書くことができません。
// Emotion
const StyledText = styled.p`
color: ${(props) => props.color};
`;
vanilla-extract(ゼロランタイム CSS-in-JS)では、CSS 変数を経由して値を渡すのが基本パターンです。
まず .css.ts 側で、createVar を使って値を後から差し込むための CSS 変数を宣言します。
// Button.css.ts
import { createVar, style } from "@vanilla-extract/css";
export const colorVar = createVar();
export const button = style({
color: colorVar,
});
コンポーネント側では @vanilla-extract/dynamic パッケージの assignInlineVars を使って、実行時の値を CSS 変数へ割り当てます。
npm install @vanilla-extract/dynamic
import { assignInlineVars } from "@vanilla-extract/dynamic";
import { button, colorVar } from "./Button.css";
export default function Button({ color }: { color: string }) {
return (
<button
className={button}
style={assignInlineVars({
[colorVar]: color,
})}
>
Click me!
</button>
);
}