背景
新規で CSS in JS を利用するにあたり、Emotionを選定した。
個人的に選定理由をまとめておきたいので簡潔に書いていく。
CSS in JS ライブラリに求めている機能
CSS in JS のライブラリとして必要とした機能は以下。
- タグ付きテンプレートリテラル記法が使えること
- CSS の記法が使えるため既存実装(CSS ファイル)をそのまま生かしやすい
オブジェクトスタイル記法だとプロパティ名をキャメルケースにする必要がある(e.g.: font-size → fontSize) - CSS in JS に明るくないメンバーでもシンタックスが異なるという障壁がなく参入しやすい
- グローバルスタイルが定義できること
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 つになった。
トレンドの比較
npm パッケージのダウンロード数だけで見ると emotion が styled-components よりも多くなっている(emotion はパッケージが分かれているため単純比較は難しい)。
npm trendsによる @emotion/styled と styled-components の比較
@emotion/styled-vs-styled-componentsちなみに GitHub のスター数は styled-components の方が多い。
next build の結果比較
Next.js 上で styled-components と Emotion のそれぞれを利用した場合のビルド結果を比較した。
ビルド後のファイルサイズを比較すると Emotion の方がファイルサイズは小さいようだった。
styled-components
対象パッケージは以下の通り。
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
GitHub - hiro0218/css-in-js-analysis at styled-componentsA thorough analysis of all the current CSS-in-JS sgithub.com
Emotion
対象パッケージは以下の通り。
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
GitHub - hiro0218/css-in-js-analysis at emotionA thorough analysis of all the current CSS-in-JS sgithub.com
まとめ
- パフォーマンス観点だけでみれば Zero-runtime は魅力的だったが、要件を満たせなかった
- styled-components や Emotion のようなユーザー数が多いライブラリを選択する方がナレッジを拾っていきやすいので良さそう
好みの問題もあるが、パフォーマンス観点で言えば Emotion が良さそうという結論に至った