1. 背景
業務でChrome拡張機能をノーフレームワークで開発しています。
ノーフレームワークのプロジェクトでcssファイルを以下のようなtsファイルで管理していました。
export const css = `<style>
.className {
margin-top: 1rem;
float: left;
}
</style>`
これだと以下の問題があります
- cssの記法が誤っている場合にエディタで気づきづらい
- トランスパイルの対象となり、型チェック、バンドルの時間が余分にかかる
- CSSのグローバルスコープ問題
1.1. CssModulesの課題
前にCssModulesの調査をしましたが、以下の問題がありました。
typed-css-modules
毎回cssファイルが作成されるごとに型定義ファイルを作成する手間がある
viteのCssModulesの型定義
存在しないスタイルのプロパティにアクセスしてもエディタ上でエラーにならない
1.2. vanilla-extractを知った
以下のイベントでvanilla-extractの存在を知りましたので調査します。
2. そもそもcss-in-jsとは
外部ファイルでスタイルを定義するのではなく、
JavaScriptを用いて CSSを記述するために設計されたライブラリのことです。
CSS in JSを利用することで、コンポーネント内にCSSを定義することができ、
外部のCSSファイルに依存することなく、コンポーネント単体で独立させることができます。
2.1. 問題点
css-in-jsの仕組みは以下です。
JavaScriptオブジェクトで作成された動的なCSSオブジェクトをランタイムで
<style />
タグに挿入する
JavaScriptで作成したCSSオブジェクトをタグにinsert可能な形式にするために、シリアライズと呼ばれる処理を施す必要があります
これにより以下の問題点があります。
コンポーネントのレンダリングの度にランタイムでスタイルのシリアライズ処理と挿入処理を行う必要があるためにどうしてもパフォーマンスのオーバーヘッドがあります
3. vanilla-extractとは
Use TypeScript as your preprocessor. Write type‑safe, locally scoped classes, variables and themes, then generate static CSS files at build time.
型安全でローカルにスコープされたクラス、変数、テーマを記述
color
のr
を消して存在しないプロパティ名colo
にすると以下のようにエディタでエラーが表示されます。
TypeScriptをプリプロセッサとして使用し、ビルド時に静的なCSSファイルを生成
ビルド時に静的なCSSファイルを生成してくれるので従来のcss-in-jsよりもランタイムパフォーマンスが向上します。
4. vanilla-extractをVite,ノーフレームワークのChrome拡張機能で使ってみる
4.1. vanilla-extractをinstall
npm install @vanilla-extract/css
4.2. style.css.ts(styleという命名は例)ファイルを作成する
vanilla-extractでは.css.tsという拡張子でcssを管理します。
これがビルド時にcssファイルに変換されます。
import { style } from '@vanilla-extract/css';
export const button = style({
color: 'red'
});
import { button } from "../css/styles.css";
export const btnAreaElm = `<button type="button" class=${button}></button>`
これだけだと使えません。
content_scriptファイルでエラーになります。
4.3. viteのプラグインを設定
viteのプラグインを設定しないとcssファイルに変換してくれないということでした。
4.3.1 viteのプラグインをinstall
npm i --save-dev @vanilla-extract/vite-plugin
npm install --save-dev @vanilla-extract/vite-plugin
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: iimon-chrome@1.0.0
npm ERR! Found: vite@3.2.10
npm ERR! node_modules/vite
npm ERR! dev vite@"^3.2.7" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer vite@"^4.0.3 || ^5.0.0" from @vanilla-extract/vite-plugin@4.0.7
npm ERR! node_modules/@vanilla-extract/vite-plugin
npm ERR! dev @vanilla-extract/vite-plugin@"*" from the root project
npm ERR!
viteは4.0.3
以降のものでないとエラーになります。
npm i vite@4.0.3
4.3.2 vite.config.tsに設定
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
export default {
plugins: [vanillaExtractPlugin()]
};
4.3.3. viteのrollupOptions.output.assetFileNamesのファイル名を固定
rollupOptions: {
output: {
~~~~~~,
assetFileNames: "override.css"
},
}
4.3.4. cssをサイトに注入
"content_scripts": [
{"css": ["override.css"]}
]
4.3.5. ブラウザで見てみる
自動的にrandom文字列が付与されていました。
ローカルスコープになっています。
5. 応用
5.1. セレクタにprefixを設定
const makeRandomStr = () =>{/* ランダム文字列生成 */ return 'prefix_'}
vanillaExtractPlugin({
identifiers: ({ hash }) => `${makeRandomStr()}${hash}`
})
以下のようにprefix_
がセレクタについていることが確認できます
.prefix_1hmbdbt0 {
color: red;
}
6. その他メリット
こちらの記事でメリットを紹介されていましたのでご覧ください
6.1. CSS変数が使える
import { createTheme } from '@vanilla-extract/css';
export const [themeClass, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
import { vars } from "../css/theme.css";
export const button = style({
color: vars.color.brand,
fontFamily: vars.font.body,
});
7. 最後に
- 「トランスパイルの対象となり、型チェック、バンドルの時間が余分にかかる」というのは解消されませんが他2つの問題点は解消されました
- 前回調査した際のCssModulesの問題点を解消かつ、プロパティのチェックをしてくれるのはありがたいなと思いました
- Linariaについても調査したいと思います
本記事を読んで頂き、ありがとうございました。
いいねいただけると記事執筆の励みになりますので、参考になったと思われた方は是非よろしくお願い致します🙏
弊社は積極採用活動中です。
もし気になっていただけたら採用ページまでよろしくお願いします。