🐭

【React / TypeScript】CSS modulesの型を生成する | Offers Tech Blog

2022/10/24に公開
5

概要

こんにちは、Offers を運営している株式会社 overflow の Software Engineer(主戦場はフロントエンド)の Kazuya です。今回は、CSS modules の型を生成する方法について紹介します。
デフォルトの状態では、tsx(React)内で import した CSS は型が付与されないため、やや保守性に欠けるところがあります。今回は CSS を TypeScript の世界に取り入れて型に守られた世界を目指していきたいと考えていますので、ぜひ最後までご付き合いください。

関連記事

https://zenn.dev/offers/articles/20220804-css_design_with_css_modules

はじめに

本記事では、CSS modules の型を生成する方法を React ベースで紹介します。基本的に他のフレームワークや言語でも活用できますが、チームメンバーのスキルアセット、要件定義など様々な要因で紹介する内容とマッチしない場合があります。今回は設計の一例であることをご理解の上、参考にしていただけると幸いです。

そもそも CSS Modules とは?

CSS Modules とは、「コンポーネント毎に CSS をローカルスコープ化する技術のこと」です。基本的に JavaScript 内で CSS をインポートしてクラス名を付与します。React のフレームワーク Next.js で推奨されていることもあり、再度注目をされ始めています。

実現したいこと

tsx ファイル内で import した Scss ファイルに対して型を付与するというものです。方法は様々ありますが、型ファイルが生成されておいたほうが、デバックしやすく環境依存が減ると考えたので、以下のプラグインを使用することにしました。

使用するプラグイン

今回は「typed-scss-modules」を使用します。CSS modules におけるクラス名を型ファイルに書き出してくれるため、実在するクラス名で補完されます。また、アウトプットのフォーマットやファイル監視モードなど細かく設定できるため、使用する環境に応じて柔軟に導入できます。導入方法や詳しい使い方は以下のページをご参照ください。

https://github.com/skovy/typed-scss-modules

構築する環境

  • Node: 16.13.0
  • Next.js: 12.3.1
  • CSS: CSS Modules(SCSS)

ディレクトリ構成

src
L styles
  L variables
    L palettes.scss
  L variables.scss
L components
  L Hoge
    L Hoge.tsx
    L style.module.scss
package.json
typed-scss-modules.config.ts
ファイル詳細
src/styles/variables/palettes.scss
$palettes: (
  primary: blue,
  black: black,
  white: white,
  alert: red,
  caution: yellow
);
src/styles/variables.scss
@forward 'variables/palettes';
src/components/Hoge/Hoge.tsx
import React from 'react'
import styles from './style.module.scss'

interface Props {
  color: string
}

export const Hoge: React.FC<Props> = ({ color }) => (
  <span className={styles[color]}>テスト</span>
)
tsx
@each $key, $value in $palettes {
  #{'.' + $key } {
     color: $value;
  }
}

実装方法

プラグインをインストールする

まずは、以下のプラグインのコマンドを実行します。開発環境でのみ動作する必要のあるプラグインのため、devDependencies にインストールしています。

$ npm i typed-scss-modules -D

設定ファイルを作成する

次にルートディレクトリに設定ファイル「typed-scss-modules.config.ts」を追加します。このファイルに定義することで、アウトプットのフォーマットや対象外にするファイルなどを設定できます。今回は以下のような設定にしていますが、必要に応じて変更してみてもらえればと思います。

typed-scss-modules.config.ts
export const config = {
  exportType: 'default',
  nameFormat: 'none',
  implementation: 'sass'
}

グローバルで定義した SCSS の変数や関数を扱う場合

すべての CSS ファイルで呼び出す変数や関数をファイル毎に呼び出すのは手間がかかるため、グローバルとして扱うケースが多いと思います。(Next.js における additionalData)
これをプラグインに適用させるには、以下の設定項目を追加します。

typed-scss-modules.config.ts
export const config = {
  exportType: 'default',
  nameFormat: 'none',
  implementation: 'sass',
+ additionalData: `@use "src/styles/variables.scss" as *;`,
+ ignore: ['**/variables.scss', '**/variables/**']
}

この時に重要なのが、ignore にグローバルで定義するファイルを除外を記載することです。これを記載しないと Module loop: this module is already being loaded. のエラーが出てしまい、正常に動作しません。

コマンドを設定する

最後に型ファイルを生成するコマンドを npmscripts に登録します。
ルートディレクトリに typed-scss-modules.config.ts が存在していると、記載した設定が反映されます。

package.json
"scripts": {
  "dev": "next dev",
  "typegen:scss": "typed-scss-modules src",
}

開発環境のホットリロード時に生成させるようにする

Next.js や Nuxt.js では、サーバーも起動させて開発することが多いですが、基本的にホットリロードに対応しています。typed-scss-modules もホットリロードに対応しているため、こちらも同時に起動させたいところですが、ターミナルを複数開いて起動させるのは手間がかかります。そこで npmscripts を並列実行できるようにするプラグインを導入します。

$ npm i npm-run-all -D

上記のプラグインを導入後、npmscripts も更新します。

package.json
"scripts": {
- "dev": "next dev",
+ "dev": "run-p dev:next dev:scss",
+ "dev:next": "next dev",
+  "dev:scss": "typed-scss-modules src --watch",
  "typegen:scss": "typed-scss-modules src",
}

npm run dev を実行することで「Next.js のサーバー起動」と「scss の型をファイル更新時に生成」を並列で起動できます。1 コマンドの実行で済むため、ターミナルを複数開くという煩わしさを無くせます。

実行結果

npm run dev もしくは npm run typegen:scss を実行することで以下の型ファイルが生成されると思います。

src/components/Hoge/style.module.scss.d.ts
export type Styles = {
  alert: string
  black: string
  caution: string
  primary: string
  white: string
}

export type ClassNames = keyof Styles

declare const styles: Styles

export default styles

まとめ

今回は、CSS modules の型を生成する方法について紹介しました。CSS modules にも型を付与されることで、保守性をより高めることができました。導入コストが低め且つ比較的どんな環境でも適用させやすいと思いますので、ぜひお試しください!

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

関連記事

https://zenn.dev/offers/articles/20220804-css_design_with_css_modules
https://zenn.dev/offers/articles/20220523-component-design-best-practice
https://zenn.dev/offers/articles/20220418-what-is-bff-architecture

Offers Tech Blog

Discussion

まっきんとっしゅまっきんとっしゅ

CSSに型の概念が存在することを初めて知ったのですが、どういったメリットがあるのでしょうか??

Yuki TerashimaYuki Terashima

class などの呼び出し時にその class が存在することが保証されているという安心感があったり、class を消した際に HTML 側に呼び出しが残っていると型エラーになってくれたりするといったメリットがありますね。

まっきんとっしゅまっきんとっしゅ

返信ありがとうございます。

class などの呼び出し時にその class が存在することが保証されているという安心感があったり

というのはエディタで補完が効くようになるという意味ですか??