React・実務の道具立て

Tailwind以外のスタイリング

このコースはTailwindで書いてきましたが、実務の案件はそうとは限りません。CSS Modules・CSS-in-JS・UIライブラリのコードを読めるようにして、新しく選ぶときの判断基準まで扱います。

このコースはずっとTailwindで書いてきました。ただ、実務で入る案件がTailwindとは限りません。

この回は書けるようになるためではなく、読めるようになるためのものです。案件で開いたファイルが見慣れない形でも、手が止まらないようにします。

CSS Modules

Viteに最初から入っています。ファイル名を.module.cssにするだけです。

.card {
  display: flex;
  gap: 1rem;
  border-radius: 1rem;
  background: white;
}
.rare {
  color: #b45309;
  font-weight: bold;
}
import styles from './BirdCard.module.css'

<div className={styles.card}>
  <span className={styles.rare}>レア</span>
</div>

書き出されるHTMLでは、クラス名が重複しない名前に変わります。実際に手元で確かめると、.card_card_8uwdn_1.rare_rare_8uwdn_5になっていました。クラス名の衝突だけを解決した、ふつうのCSS——それがCSS Modulesです。

良いところつらいところ
ふつうのCSSの知識がそのまま使えるファイルを行き来する(CSSとJSXが別)
クラス名を考える負担が減る(衝突しないので)条件で切りかえると${styles.a} ${styles.b}が増える
ビルド時に解決するので実行時の負荷がない値をJSから渡すのが得意でない(CSS変数を使う)

条件つきの切りかえは、こう書きます。

<div className={`${styles.card} ${isFavorite ? styles.favorite : ''}`}>

長くなるので、clsxという小さなライブラリがほぼ必ず一緒に入っています

import clsx from 'clsx'

<div className={clsx(styles.card, isFavorite && styles.favorite)} />

CSS-in-JS(styled-components / emotion)

JavaScriptの中にCSSを書く方式です。

import styled from 'styled-components'

const Card = styled.div`
  display: flex;
  gap: 1rem;
  border-radius: 1rem;
  background: ${(props) => (props.$isFavorite ? '#fef3c7' : 'white')};
`

<Card $isFavorite={isFavorite}>…</Card>

propsで見た目を変えられるのが最大の特徴で、Cardという名前が付くので読みやすくもあります。

ただし、新規で選ばれることは減りました。理由は2つです。

  • 実行時にスタイルを組み立てるので、そのぶん遅い
  • サーバーコンポーネントで動かないNext.jsの回で扱う「既定はサーバー」と噛み合わない)

同じ書き味でビルド時に解決する方式(vanilla-extractなど)が代わりに使われています。既存の案件では現役なので、読めるようにしておく価値はあります。

UIライブラリ

部品ごと持ってくる選択肢です。名前をよく見るものだけ。

種類特徴
見た目つきMUI・Chakra UI・Ant Design入れればすぐ形になる。見た目を変えるのは大変
見た目なし(headless)Radix UI・Headless UI・React Aria動きとアクセシビリティだけ提供。見た目は自分で
コピーして使うshadcn/uiRadix+Tailwindのコードを自分のプロジェクトに書き出す。依存ではなく自分のコードになる

いま新規でTailwindを使うなら、shadcn/uiという選択がよく出てきます。npx shadcn@latest add dialogのようにして、部品のソースが自分のcomponents/に置かれる——ライブラリを入れるのではなくコードをもらう形です。

選ぶときの判断

新しく始めるとき、何を基準にするか。

状況向いているもの
新規・小〜中規模・1人〜少人数Tailwind(このコースの構成)
ふつうのCSSで書きたい・デザイナーがCSSを書くCSS Modules
デザインシステムが既にあるCSS Modules か vanilla-extract
管理画面を早く作りたいMUIなど見た目つきのUIライブラリ
見た目は独自・動きは任せたいRadix / shadcn/ui
既存の案件に入るそこで動いているものに合わせる

最後の行がいちばん大事です。既存のプロジェクトに自分の好みを持ちこまない——混在は、どちらか一方より確実に読みにくくなります。

Tailwind CSS使うと得な場面・損な場面Tailwindを使うかどうかの回。ここでの比較は、React以外でも同じ判断になります

公式ドキュメントも見てみる

vitejs外部サイト特徴Vite公式のCSS Modules。.module.cssという名前だけで有効になること、書き出される名前の形が説明されています

このレッスンのまとめ

  1. この回の目的は書けるより読めること。案件のスタイリングは選べない
  2. CSS Modules.module.cssにするだけ。クラス名の衝突だけを解決したふつうのCSS
  3. 条件つきクラスはclsx。Tailwindの現場でも出てくる
  4. CSS-in-JSはpropsで見た目を変えられるが、実行時の負荷とサーバーコンポーネントが理由で新規採用は減った
  5. $isFavorite$DOMに渡さない印
  6. UIライブラリの本命は見た目ではなくアクセシビリティ(フォーカス・キーボード・読み上げ)
  7. Tailwindならshadcn/ui(コードが自分のものになる)という選択肢
  8. 既存プロジェクトでは、そこで動いているものに合わせる

次は、Reactでのアクセシビリティです。フォーカスとキーボード操作を扱います。

やってみよう:BirdCardをCSS Modulesで書き直す

1つの部品だけ、別のやり方で書いてみます(元のファイルは残して、別名で作ってください)。

  1. src/components/BirdCardCss.module.cssを作り、カードの見た目をふつうのCSSで書く
  2. BirdCardCss.tsxからimport styles from './BirdCardCss.module.css'してclassName={styles.card}にする
  3. 一覧ページで1枚だけ差しかえて、同じ見た目になることを確認する
  4. 開発者ツールで書き出されたクラス名を見る(_card_…のような形になっている)
  5. お気に入りのときだけ色を変える指定を、clsxで足してみる
import clsx from 'clsx'
import styles from './BirdCardCss.module.css'

<div className={clsx(styles.card, isFavorite && styles.favorite)}>

4番では_card_8uwdn_1のような名前になっているはずです。これを見ておくと、なぜクラス名が衝突しないのかが一度で分かります。確認できたら元のカードに戻して構いません。

よくある質問

Tailwindを覚えたなら他は要りませんか?
新しく作るなら足ります。ただし既存の案件ではCSS Modulesやstyled-componentsが動いていることが多く、読めないと手が止まります。書けるより読めるほうが先です。
CSS ModulesとTailwindはどちらがよいですか?
優劣ではなく好みとチームの合意です。CSS ModulesはふつうのCSSがそのまま書けてクラス名の衝突だけ解決し、Tailwindはクラス名を考えずに済みます。共存もできます。
styled-componentsは今も使いますか?
既存の案件では現役ですが、新規で選ばれることは減りました。実行時にスタイルを組み立てる作りがサーバーコンポーネントと相性が悪く、ビルド時に解決する方式が主流になっています。
UIライブラリは入れるべきですか?
モーダル・タブ・コンボボックスのように、キーボード操作とフォーカス管理が難しい部品を自作しないで済むのが最大の利点です。見た目を細かく作りこみたいなら、スタイルの付いていない土台だけを使う選択もあります。