ノーフレームワークのChrome拡張機能にvanilla-extractを導入する

:::note
Chrome拡張機能向けの内容です。
ビルドツールはviteを使用しています。

# 要約
vanilla-extractは
- ビルド時にcssファイルを生成してくれるのでパフォーマンスが他のcss-in-jsライブラリよりも魅力的だった
- 型安全でプロパティの構文エラーをエディタで見ることができる
- CssModulesから乗り換えるメリットを感じた
:::

# 1. 背景
業務でChrome拡張機能をノーフレームワークで開発しています。

ノーフレームワークのプロジェクトでcssファイルを以下のようなtsファイルで管理していました。
```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の型定義
存在しないスタイルのプロパティにアクセスしてもエディタ上でエラーにならない

https://qiita.com/yamatai12/items/cd8ae7efb06e785a7d40

## 1.2. vanilla-extractを知った

以下のイベントでvanilla-extractの存在を知りましたので調査します。

https://findy.connpass.com/event/312625/

https://speakerdeck.com/miyahkun/gan-ixiang-riniyou-waretevanilla-extractwo1nian-jian-yun-yong-sitemita

# 2. そもそもcss-in-jsとは

https://qiita.com/degudegu2510/items/a83890442a77081e874f#css-in-js-%E3%81%A8%E3%81%AF

>外部ファイルでスタイルを定義するのではなく、
JavaScriptを用いて CSSを記述するために設計されたライブラリのことです。
CSS in JSを利用することで、コンポーネント内にCSSを定義することができ、
外部のCSSファイルに依存することなく、コンポーネント単体で独立させることができます。

## 2.1. 問題点

https://zenn.dev/poteboy/articles/e9f63b87b3cd69#%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0css-in-js%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9

css-in-jsの仕組みは以下です。
>JavaScriptオブジェクトで作成された動的なCSSオブジェクトをランタイムで`<style />`タグに挿入する
JavaScriptで作成したCSSオブジェクトを<style />タグにinsert可能な形式にするために、シリアライズと呼ばれる処理を施す必要があります

これにより以下の問題点があります。
>コンポーネントのレンダリングの度にランタイムでスタイルのシリアライズ処理と挿入処理を行う必要があるためにどうしてもパフォーマンスのオーバーヘッドがあります

# 3. vanilla-extractとは

[npm trends](https://npmtrends.com/@vanilla-extract/css)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/21feec48-0dc7-8238-deec-a7ec5d5d9354.png)

https://vanilla-extract.style/

>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`にすると以下のようにエディタでエラーが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/364c3cd9-cc54-6724-513c-f54df586b605.png)

## TypeScriptをプリプロセッサとして使用し、ビルド時に静的なCSSファイルを生成

ビルド時に静的なCSSファイルを生成してくれるので従来のcss-in-jsよりもランタイムパフォーマンスが向上します。

# 4. vanilla-extractをVite,ノーフレームワークのChrome拡張機能で使ってみる

## 4.1. vanilla-extractをinstall
```terminal
npm install @vanilla-extract/css
```

## 4.2. style.css.ts(styleという命名は例)ファイルを作成する

vanilla-extractでは.css.tsという拡張子でcssを管理します。
これがビルド時にcssファイルに変換されます。

```style.css.ts
import { style } from '@vanilla-extract/css';

export const button = style({
  color: 'red'
});
```

```call.ts
import { button } from "../css/styles.css";
export const btnAreaElm = `<button type="button" class=${button}></button>`
```

これだけだと使えません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/c7951e74-395a-3750-8f75-be6d339c86ea.png)

content_scriptファイルでエラーになります。

# 4.3. viteのプラグインを設定

viteのプラグインを設定しないとcssファイルに変換してくれないということでした。

https://chaika.hatenablog.com/entry/2023/03/18/083000

https://vanilla-extract.style/documentation/integrations/vite/

## 4.3.1 viteのプラグインをinstall

```terminal
npm i --save-dev @vanilla-extract/vite-plugin
```

```terminal
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!
```
:::note
viteは```4.0.3```以降のものでないとエラーになります。
:::

```terminal
npm i vite@4.0.3
```

## 4.3.2 vite.config.tsに設定

```vite.config.ts
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';

export default {
  plugins: [vanillaExtractPlugin()]
};
```

## 4.3.3. viteのrollupOptions.output.assetFileNamesのファイル名を固定
```vite.config.ts
rollupOptions: {
    output: {
        ~~~~~~,
        assetFileNames: "override.css"
    },
}
```

## 4.3.4. cssをサイトに注入

```manifest.json
"content_scripts": [
        {"css": ["override.css"]}
]
```

## 4.3.5.　ブラウザで見てみる

自動的にrandom文字列が付与されていました。
ローカルスコープになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/d5eb918c-bc23-df88-d1c2-e5ec0f2ffc3f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/c8f5b873-c130-f7f1-4cd6-d48ff8bf304c.png)

# 5. 応用
## 5.1. セレクタにprefixを設定

```vite.config.ts

const makeRandomStr = () =>{/* ランダム文字列生成 */ return 'prefix_'}

vanillaExtractPlugin({
      identifiers: ({ hash }) => `${makeRandomStr()}${hash}`
    })
```

以下のように```prefix_```がセレクタについていることが確認できます
```css
.prefix_1hmbdbt0 {
  color: red;
}
```

# 6. その他メリット
こちらの記事でメリットを紹介されていましたのでご覧ください

https://zenn.dev/tonkotsuboy_com/articles/vanilla-extract

## 6.1. CSS変数が使える

https://vanilla-extract.style/documentation/theming/
```theme.css.ts
import { createTheme } from '@vanilla-extract/css';

export const [themeClass, vars] = createTheme({
  color: {
    brand: 'blue'
  },
  font: {
    body: 'arial'
  }
});
```

```button.css.ts
import { vars } from "../css/theme.css";

export const button = style({
    color: vars.color.brand,
    fontFamily: vars.font.body,
});
```

# 7. 最後に
- 「トランスパイルの対象となり、型チェック、バンドルの時間が余分にかかる」というのは解消されませんが他2つの問題点は解消されました
- 前回調査した際のCssModulesの問題点を解消かつ、プロパティのチェックをしてくれるのはありがたいなと思いました
- Linariaについても調査したいと思います

本記事を読んで頂き、ありがとうございました。
いいねいただけると記事執筆の励みになりますので、参考になったと思われた方は是非よろしくお願い致します🙏

:::note
弊社は積極採用活動中です。
もし気になっていただけたら[採用ページ](https://recruit.iimon.co.jp/)までよろしくお願いします。
:::

# 8. 参考
- [ノーフレームワークのChrome拡張機能にCssModulesを導入する](https://qiita.com/yamatai12/items/cd8ae7efb06e785a7d40)
- [フロントエンド技術選定 ~ゼロランタイムCSS in JSとTailwind CSS編~](https://findy.connpass.com/event/312625/)
- [甘い香りに誘われてVanilla Extractを1年間運用してみた](https://speakerdeck.com/miyahkun/gan-ixiang-riniyou-waretevanilla-extractwo1nian-jian-yun-yong-sitemita)
- [CSS-in-JSのパラダイムシフト](https://zenn.dev/poteboy/articles/e9f63b87b3cd69#%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0css-in-js%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9)
- [React TypeScript vanilla-extract を vite で使う時の Tips](https://chaika.hatenablog.com/entry/2023/03/18/083000)
- [【2022年最新】オススメの CSS in JS まとめ](https://qiita.com/degudegu2510/items/a83890442a77081e874f#css-in-js-%E3%81%A8%E3%81%AF)
- [CSS・TypeScriptの相性が抜群。vanilla-extractが最高のCSS開発体験をくれた](https://zenn.dev/tonkotsuboy_com/articles/vanilla-extract)