ReactでCSS Modulesを使う

# やりたいこと


React Router v7で良さげにCSSを書きたい


# 選択肢


よく選択肢に上るのは以下のようです


・CSS Modules
・styled-components
・Zero-Runtime


・Tailwind 
・Bootstrap


どれが良いとか悪いとかはあまり知らないのですが、今回は簡単そうに見えるCSS Modulesを使う事にしてみます


# CSS Modulesとは


CSS Modulesの公式サイトは以下になります
https://github.com/css-modules/css-modules?tab=readme-ov-file


公式は仕様のみ定めていて、これに基づく実装がいくつかあります

・css-loader (Webpack用)
・Parcel
・Vite


css-loaderはCSS Modulesを非推奨としていくようですが、ParcelとViteではそういう動きはなさそうなので、Viteが使われている今どきのReact環境では引き続き使えるようです


今回はReact Router v7をViteで構築していきます


# 使い方


## 1. 基本編


React Router v7でnpx create-react-router@latest my-appによりアプリを作ると以下のようなフォルダ構成になると思います


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/417395fa-3308-4113-d3b7-bae072fe4ab3.png" width=27%>


home.tsxにCSSを適用しようと思います



```css:home.module.css
.homeContainer {
  background-color: lightblue;
  padding: 20px;
  text-align: center;
}
```


cssファイルをimportしてTSX内でclassNameに渡してやるように書くとViteがビルド時に適用してくれます。静的分析利くのでClassNameをTypoする可能性がなくなるのが良いですね


```tsx:home.tsx
import type { Route } from "./+types/home";
import { Welcome } from "../welcome/welcome";
import styles from './home.module.css';

export default function Home() {
  return <>
    <Welcome />
    <div className={styles.homeContainer}>
      <h1>hoge</h1>
    </div>
  </>
}
```


hogeの部分がスタイルを適用している部分です


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/d7ff2455-6529-b99e-aea5-0332ca4dc580.png" width=50%>


## 2. stateで表示を変える


stateを使って切り替えることもできます


```css:home.module.css
.homeContainer {
  background-color: lightblue;
  padding: 20px;
  text-align: center;
}

.homeContainer2 {
  background-color: lightcoral;
  padding: 20px;
  text-align: center;
}
```


```tsx:home.tsx
import type { Route } from "./+types/home";
import { Welcome } from "../welcome/welcome";
import styles from './home.module.scss';
import { useState } from "react";

export default function Home() {
  const [fuga, setFuga] = useState(true)

  return <>
    <Welcome />
    <div className={fuga ? styles.homeContainer : styles.homeContainer2} onClick={()=>setFuga(!fuga)}>
      <h1>hoge</h1>
    </div>
  </>
}

```


クリックすると色が変わるようにしてみました


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/bd8c8acb-9c5d-a0f5-0ff6-f0508163c31b.png" width=50%>


## 3. composes


公式だと以下に説明があります
https://github.com/css-modules/css-modules/blob/master/docs/composition.md


composes要素にclassNameを書くと、指定したclassNameを継承してCSSを適用できます
例えば以下でfugaを適用すると、color: yellow; background: red;となります


```home.css
.hoge {
  color: green;
  background: red;
}

.fuga {
  composes: hoge;
  color: yellow;
}
```


### pseudo classes


以下のように書くと、:hoverまで含めて引き継いでくれます


```home.css
.className {
  color: green;
}
.className:hover {
  color: red;
}
.otherClassName {
  composes: className;
  background: black;
}
```


つまり以下のように書いたのと同じになります


```home.css
.otherClassName {
  color: green;
  background: black;
}
.otherClassName:hover {
  color: red;
}
```

### Composing from other files 


別ファイルからcomposeすることもできます


```css:css
.otherClassName {
  composes: className from './style.css';
}
```


### Composing from global class names


グローバルスコープのCSSからcomposeすることもできます


```css:css
.otherClassName {
  composes: globalClassName from global;
}
```


## 4. 複数のCSSモジュールをimportする


複数のCSSモジュールをimportしてきて使う事もできます
https://github.com/css-modules/css-modules/blob/master/docs/import-multiple-css-modules.md


CSS Modulesでimportしたstylesはオブジェクトであるので、Object.assign()で他のstyleオブジェクトとマージすることができます。以下の場合はDemo.cssファイルとcss-fancy-buttonライブラリをstylesにマージしていて、単独の場合と同じように使うことができます


```tsx:hoge.tsx
import React from 'react';
let styles = {};
import demo from './Demo.css';
import fancyButton from 'css-fancy-button';
Object.assign(styles, fancyButton, demo);

export default function Demo() {
  return (
    <div className={styles.demo}>
      <button className={styles.fancyButton}>Press Me</button>
    </div>
  );
}
```


# まとめ


良さげに書けるし簡単です
おススメ