[CSS in JS] Emotion の選定理由👩🎤
CSS in JS ライブラリに求めている機能
CSS in JS のライブラリとして必要とした機能は以下。
- タグ付きテンプレートリテラル記法が使えること
- CSS の記法が使えるため既存実装(CSS ファイル)をそのまま生かしやすい
オブジェクトスタイル記法だとプロパティ名をキャメルケースにする必要がある(e.g.:font-size
→fontSize
) - CSS in JS に明るくないメンバーでもシンタックスが異なるという障壁がなく参入しやすい
- CSS の記法が使えるため既存実装(CSS ファイル)をそのまま生かしやすい
- グローバルスタイルが定義できること
CSS in JS ライブラリを選定するための条件
選定条件としては以下。
- ドキュメントが多いこと
- 他社事例や著名な UI ライブラリが Emotion を採用している事例が多く観測できた
- メンテナンスされていること
- Next.js 上で動作させる予定だったため、styled-components、Emotion などであれば、SWC コンパイラに切り替える選択もできる
Zero-runtime
パフォーマンスの観点で Zero-runtime CSS in JS ライブラリも選択肢のひとつとして挙がった。
API が styled-components や Emotion などの先発ライブラリと同様の構文を有しているlinariaを確認してみた。
小規模なアプリケーションの場合は良さそうだが、以下の点で linaria は見送った。
動的なスタイルを多用した場合に CSS Custom Properties の出力が多くなり、HTML が肥大化してしまう
動的なスタイルの値が
undefined
な場合に不要なプロパティが残るconst Heading = styled.h1` background-color: ${({ bg }) => bg}; `; const Example = () => { return ( <> <Heading bg="red">Red Heading</Heading> <Heading>Heading</Heading> </> ); };
↓
<h1 class="t1vg9j5w" style="--t1vg9j5w-0:red;">Red Heading</h1> <h1 class="t1vg9j5w" style="--t1vg9j5w-0:undefined;">Heading</h1> <style> .t1vg9j5w { background-color: var(--t1vg9j5w-0); } </style>
値が
undefined
の場合でもプロパティが残る同じコンポーネントをネストすると再利用された CSS Custom Properties が上書きされていまい意図した動作をしない
const Stack = styled.div` & > * + * { margin-top: ${({ spacing }) => spacing || "0"}; } `; const Example = () => { return ( <Stack spacing="1rem"> <Stack></Stack> <Stack></Stack> {/* 👈 */} </Stack> ); };
ネストされた 2 番目の
Stack
のmargin-top
は1rem
となることを期待するが、CSS Custom Properties の上書きによって0
になってしまう採用事例・ドキュメントが少ない
styled-components vs Emotion
検討結果、候補は styled-components もしくは Emotion の 2 つになった。
- 以前は後発の Emotion は利用できる API が多かった
- 現状、styled-components の同様の機能も有している
ライブラリ タグ付きテンプレート オブジェクトスタイル グローバルスタイル ネストセレクタ TypeScript サポート Server Side Rendering styled-components ✅ ✅ ✅ ✅ ✅ ✅ Emotion ✅ ✅ ✅ ✅ ✅ ✅
- 現状、styled-components の同様の機能も有している
- ベンチマーク結果を見ると Emotion の方が styled-components よりパフォーマンスは良い
https://stitches.dev/docs/benchmarks
トレンドの比較
npm パッケージのダウンロード数だけで見ると emotion が styled-components よりも多くなっている(emotion はパッケージが分かれているため単純比較は難しい)。
ちなみに GitHub のスター数は styled-components の方が多い。
next build
の結果比較
Next.js 上で styled-components と Emotion のそれぞれを利用した場合のビルド結果を比較した。
ビルド後のファイルサイズを比較すると Emotion の方がファイルサイズは小さいようだった。
styled-components
対象パッケージは以下の通り。
- next: v12.2.4
- styled-components: v5.3.5
next build
の実行結果は以下の通り。
Page Size First Load JS
┌ ○ / 3.27 kB 90.6 kB
├ /_app 0 B 87.3 kB
├ ○ /404 2.67 kB 90 kB
└ ○ /other 1.06 kB 88.4 kB
+ First Load JS shared by all 87.3 kB
├ chunks/framework-5f4595e5518b5600.js 42 kB
├ chunks/main-13980ce3a4ae3dcf.js 29 kB
├ chunks/pages/_app-5392701c868a5bd6.js 14.7 kB
└ chunks/webpack-7b5c3cf34b918637.js 1.58 kB
https://github.com/hiro0218/css-in-js-analysis/tree/styled-components
Emotion
対象パッケージは以下の通り。
- @emotion/react: v11.10.0
- @emotion/styled: v11.10.0
- next: v12.2.4
next build
の実行結果は以下の通り。
Page Size First Load JS
┌ ○ / 3.21 kB 84.1 kB
├ /_app 0 B 80.8 kB
├ ○ /404 2.67 kB 83.5 kB
└ ○ /other 1.06 kB 81.9 kB
+ First Load JS shared by all 80.8 kB
├ chunks/framework-5f4595e5518b5600.js 42 kB
├ chunks/main-13980ce3a4ae3dcf.js 29 kB
├ chunks/pages/_app-0710a7201ba41fb0.js 8.07 kB
└ chunks/webpack-3bd2f374eaae16d4.js 1.71 kB
https://github.com/hiro0218/css-in-js-analysis/tree/emotion
まとめ
- パフォーマンス観点だけでみれば Zero-runtime は魅力的だったが、要件を満たせなかった
- styled-components や Emotion のようなユーザー数が多いライブラリを選択する方がナレッジを拾っていきやすいので良さそう
好みの問題もあるが、パフォーマンス観点で言えば Emotion が良さそうという結論に至った