💡

CSSモジュールはこう書きたい

2022/09/07に公開

React.jsとCSSモジュール(CSS Modules)を採用するとき、こういう指針でコーディングできたらという所感です。

CSSよりSass

CSS1本で抽象化できるメンバーが揃うと言い切れる場合を除き、基本的にはスタイル抽象化のためにSass(scss)を導入するのが無難です。

理想とは違い、現実的に全てのスタイル定義がコンポーネントの区切りに適合する訳ではないと感じます。例えばcreate-react-app公式に書いてあるような「コンポーネントがあればプリプロセッサは不要(要約)」[1]という世界はまだ遠いです。

コンポーネントとscssは1対1

これが一番の原則で、Some.tsxSome.module.scssを1対1で関連付けることを守って欲しいです。 そうでないと途端に治安が悪化します。

# 例
src/path/Todo.module.scss
src/path/Todo.tsx
src/another-path/Header.module.scss
src/another-path/Header.tsx
...

冒頭の原則がプロジェククトに浸透するかはコンポーネント志向CSSがどれだけ共有できるかに依ります。SMACSSやFLOCSSなどの多機能なCSSクラスを統制して実装されたWebサイトやアプリケーションも普通に存在し、その選択肢も含めて方向性を認識する必要があります。

ネストは最小限に抑える

3重以上の親子・子孫セレクタを駆使するよりは粒度の小さいセレクタが好みです。言い換えれば、こちらの記事では「CSSがDOMを知っている」より「DOMがCSSを知っている」タイプ。

https://zenn.dev/takepepe/articles/css-always-breaks#「詳細度・dom-概念は無視した方が楽」という選択

単にコーディング量を削減するためではなく、次のように「個別のスタイルよりも2つ揃って完成する」度合いが強いときに数段階のネストを適用します。

  • ulに対するli、dlに対するdiv
  • パーツ単位のUIに被せるための親スタイル
  • その他、フクロウセレクタ等...

汎用クラスについて

CSSモジュールは「utility first」ではないですが、第2の選択肢として適度に取り入れると楽になります。ただし感覚的にはmixinで共通化し、汎用クラスは最小限にするのがベストな気がします。

composesは使わない

composesは共通クラスをセレクタに挿入して自動適用してくれる機能で、スタイルシートのファイルがDRYになります。プロセスは次のように、HTMLに共通クラスが当てられる結果になります。[2]

Foo.module.scss
.common { /* ... */}
.primary {composes: common; /* ... */}
.danger {composes: common; /* ... */}
Foo.tsx
return <div className={styles.primary} />
// output: class="Foo_common_hash Foo_primary_hash"
return <div className={styles.danger} />
// output: class="Foo_common_hash Foo_danger_hash"

この隠蔽はさほど旨味を感じないので、混乱を避けるために使わなくていいと思っています。

CSS-in-JSをできるだけ入れない

動的なスタイルのpropsを受け取るコンポーネントを作りたくEmotion等を入れたくなるときが来ますが、スタイルの優先順位から一貫性が消えて狂う(※)ので可能な限り他のCSSライブラリを入れないことを推奨します。代わりにインラインCSS変数等を使うのがおすすめです。

※例えばビルドしてから初めてスタイルが崩れたりします

脚注
  1. https://create-react-app.dev/docs/adding-a-sass-stylesheet/ ↩︎

  2. https://github.com/css-modules/css-modules#composition ↩︎

Discussion