Tailwind CSSを使ってみる (with create-react-app/emotion)

CRA (create-react-app) でTailwind CSSに入門してみます。

https://tailwindcss.com/

## Tailwind CSS概要

一言でいえば、パラメータをカスタマイズ可能なCSSフレームワークという感じです。

Tailwind CSSの設定ファイル(`tailwind.config.js`)に基づいてTailwind CSSがCSSファイルを生成してくれるので、それをTailwind CSSの規約通りのクラス名を通して利用する形になります。

- 自分でゼロからUIデザインする際に邪魔しないCSS設計
    - Atomic Designをしたいし、自サイトのテイストを出したいが、そこそこCSSの定番は欲しいという要望にマッチしそう
        - たとえば、UIライブラリとしてMaterial-UIがあるわけですが、マテリアルUI強制になってしまうのが欠点だったりする
- 基本的にTailwind CSSが吐いたCSSを参照して利用する形となる。
    - Tailwind CSSが定義するクラス名をかけばOK
- ある程度定番のスタイルは既存クラスを用いればいい
    - flexボックスとか、死ぬほど書くスタイルはいちいちCSSで書きたくない
- 設定ファイルにより、カスタマイズ可能。レスポンシブの定義などを編集することができる
    - `tailwind.config.js` により何ピクセルがどの画面サイズに該当するかなど編集可能。色やテーマも可能。
    - 動的にパラメータを切り替えるサイトだと一工夫必要そう。
- PostCSSプラグインとして利用することができる
    - 要するにCSSのマクロシステム(PostCSS)に組み込める
    - 特にPostCSSがなくとも `tailwindcss build` コマンドで静的にTailwind CSSのクラス名が入ったCSSを出力可能。
- デフォルトのTailwind CSSでいいのであれば、CDN(`<link>`要素)で参照することも可能。
    - `<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">`
    - ビルドする利点はカスタマイズしたりするところにある

## 環境準備

CRA (TypeScript)　に組み込む前提で準備していきます。

```console
$ yarn create react-app --template typescript tailwindcsstryout
$ cd tailwindcsstryout

$ yarn add tailwindcss
```

Tailwind CSSの初期設定ファイル (`tailwind.config.js`) を生成します。

```console
$ yarn tailwindcss init
```

Tailwind CSSの共通CSSファイル (`src/tailwind.css`) を作成します。

```css:src/tailwind.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

`package.json` に上記のTailwind CSSのマクロ入りのCSSをビルドするタスクを組み込みます。

（PostCSSのプラグインの構成でTailwind CSSを組み込むことができ、直接マクロ入りCSSをビルド工程でトランスパイルできるのであれば特にこういうビルドタスクは不要です。）

```json:package.json
    // preXXXX は XXXX の前に勝手に実行されるタスク

    "prestart": "tailwindcss build src/tailwind.css -o src/tailwind.compiled.css",
    "start": "react-scripts start",
    "prebuild": "tailwindcss build src/tailwind.css -o src/tailwind.compiled.css",
    "build": "react-scripts build",
```

上記のスクリプトによりCRAの起動時に勝手に `src/tailwind.compiled.css` が作成されます。もちろん `src/tailwind.css` を変更した場合はこの構成だと再起動が必要になります。

コンパイル済みのCSSをルートCSSから参照します。

```css:src/index.css
@import "./tailwind.compiled.css";
```

これでアプリ内でTailwind CSSが定義するクラス名を利用することができるようになりました。

### CRAでもリアルタイムでTailwind CSSを生成したい場合

[純正のコマンドにはwatchオプションがないので、postcss-cliのwatchオプションを使う必要があります。](https://github.com/tailwindcss/tailwindcss/issues/1359) もちろんCRAじゃなく、普通にwebpack上でPostCSSを構成している人はそっちに組み込んだほうが快適だと思います。

```console
$ yarn add -D postcss-cli

# ついでに npm-run-all も入れておく
$ yarn add -D npm-run-all
```

PostCSSの設定を行います。

```js:postcss.config.js
module.exports = {
  plugins: [
    require("tailwindcss"),
    // ベンダープレフィックス自動付与も欲しい人はautoprefixerを有効にすればOKです
    // require("autoprefixer")
  ],
};
```

npm-scriptsの設定を変更します。

```json:package.json
    "start": "npm-run-all --parallel start:postcss start:cra",
    "start:cra": "react-scripts start",
    "start:postcss": "postcss src/tailwind.css -o src/tailwind.compiled.css -w",
    "build": "npm-run-all build:postcss build:cra",
    "build:cra": "react-scripts build",
    "build:postcss": "postcss src/tailwind.css -o src/tailwind.compiled.css",
```


### purge オプションでCSSを小さく

開発時に出力されるTailwind CSSのサイズがは9万行弱あるので、そのまま利用するのは厳しいです。もちろんminifyやgzip圧縮などは用いることになりますが、単純にソースコードに出てこないクラス名を削除する方法が用意されています。

たとえば、 `src` フォルダ中のTypeScriptファイルに出てこないクラス名をすべて削除するのであれば、下記のようにすれば問題ありません。

```js:tailwind.config.js
  purge: [
    './src/**/*.ts',
    './src/**/*.tsx',
  ]
```

上記を指定しつつ、 `NODE_ENV=production` の状態でコンパイルすれば、ソースコードに出現しないクラス名はすべて削除されます。（クラス名関係なく要素に直接付与されるスタイルは残ります。）

```console
$ NODE_ENV=production yarn build
```

[もっと細かいホワイトリスト指定もできるようです。](https://tailwindcss.com/docs/controlling-file-size)

## 基本的に適用されるスタイル

[Preflight](https://tailwindcss.com/docs/preflight/)というものが基本的なスタイルとして用意されています。

```css
@tailwind base;
```

normalize.cssをベースにしているらしいですが、基本的には下記のようなスタイルになるようです。

- いろんな要素の空白がゼロ
- 見出しは完全通常テキストと同様
- リストスタイル消去
- `img`, `object` などメディア系のタグがブロック指定かつ `vertical-align: middle`
    - この動作が不要な時は `class="inline"` を指定
- borderをすべて0幅、`solid`、テーマのデフォルト色に変更

preflightは下記を設定に入れれば無効にできるそうです。

```js
  corePlugins: {
    preflight: false,
  }
```

## 基本的な概念

### クラス名

- 基本的にTailwind CSSのスタイリングは要素にクラス名を指定することで行われます。
    - 要素に直接CSSが指定されている場合（例：前述のPreflight)もあります

### [ブレイクポイント](https://tailwindcss.com/docs/responsive-design/)

- レスポンシブデザインの見た目の切れ目になる画面横幅のこと
    - デフォルトは、 sm: 640px 〜 md: 768px 〜 lg: 1024px 〜 xl: 1280px
- 特定画面幅の場合のみ有効にしたいスタイルがある場合は、 `ブレイクポイント名:クラス名` のようにつければOK
- デフォルト設定だと、 sm に適用されているものは、 md 以上でも適用されることに注意。
    - この動作を変えたい場合は設定の変更が必要。

### [擬似クラス変種](https://tailwindcss.com/docs/pseudo-class-variants/) (Pseudo Class Variants)

- たとえば、ホバー時だけにあてたいスタイルは `hover:クラス名` というように書くことができる
- 上記のブレイクポイントとあわせて、 `xl:hover:bg-blue-500` (画面サイズ特大時でホバーした時のみ青い背景をつける) という指定も可能。
    - ただ、もちろん上記のクラス名にあわせたCSSクラスの膨大な全組み合わせ定義をTailwind CSSが生成しなければならないので、デフォルトの設定では `hover` / `focus` のみサポート。それ以外は設定を変えれば対応可能。

### [関数・ディレクティブ](https://tailwindcss.com/docs/functions-and-directives)

- Tailwind CSSでトランスパイルされるCSSファイル(本稿だと `src/tailwind.css`) ではいくつかディレクティブ（マクロ）を使えます
- [`@apply`](https://tailwindcss.com/docs/extracting-components)
    - `@apply tailwindcssのクラス名1 tailwindcssのクラス名2 tailwindcssのクラス名3 ...` というようにCSSのスタイル指定部分に書くことによりスタイル指定をインライン化することができます
    - ブレイクポイントの指定や、擬似クラス変種のような条件指定はこれではできないことに注意。普通に擬似クラスを指定したり、 `@screen` を使う必要がある。
- [`@responsive`, `@variants`](https://tailwindcss.com/docs/adding-new-utilities)
    - `@responsive { ... }` で囲ったCSS定義内のクラスについて、レスポンシブ分クラス名のコンビネーション(e.g. `.sm:myClassName`)を生成することができます
    - `@variants { ... }` も同じで疑似クラス分のコンビネーション(e.g. `.hover:myClassName`)を生成することができます
- [`@screen`](https://tailwindcss.com/docs/functions-and-directives)
    - `@screen sm { ... }` とすれば `sm` のレスポンシブ定義 (`@media (min-width: 640px){ ... }`) で囲ったのと同じことにできます
- `theme(...)` 関数
    - テーマとして設定ファイルで定義している変数の値にアクセスすることができます

## emotion と同時利用する (twin.macro)

CSS-in-JS ライブラリと同時併用もできるのかやってみましょう。ここではCSS-in-JSライブラリとして [emotion](https://github.com/emotion-js/emotion) を用います。

```console
$ yarn add @emotion/core @emotion/styled
```

くわえて、 styled-components に直接Tailwind CSSのクラス名を記述するために、[twin.macro](https://github.com/ben-rogerson/twin.macro) というものを使います。

```console
$ yarn add twin.macro
```

あとは、 `tw` マクロを使って直接Tailwind CSSのクラス名を書くことができます。

```tsx
import styled from "@emotion/styled";

const Component = styled.div`
  ${tw`bg-blue-500`}
`
```

別件ですが、なんか `tailwind.config.js` が `src/` フォルダの中に無いと[色々不具合](https://qiita.com/hourglasshoro/items/a1dc20b3d8fe42730859)が起こるらしいし、公式的なCRAのセットアップとしても `src` に置いてほしいらしいので、これを行う際は `src/tailwind.config.js` に設定ファイルを移すようにしたほうがいいかもしれません。

```js:postcss.config.js
const tailwindcss = require("tailwindcss");

module.exports = {
  plugins: [tailwindcss("src/tailwind.config.js")],
};
```

```js:package.json
  "babelMacros": {
    "twin": {
      "config": "src/tailwind.config.js"
    }
  },
```

## まとめ

結構既存のスタイルを書きまくる作業などが軽減されそうな感じがします。

ただ、やっぱりCSS-in-JSと共存させるあたりは多少面倒で罠がありそうに感じました。

styled-component内に直接外部クラス名を書かせてくれれば特に問題ないんだけどなあ〜