Angular 12 にシュッと導入した tailwindcss の設定ファイルをチャッと理解する

前の記事では、[Angular にシュッと tailwindcss を導入する](https://qiita.com/ringtail003/items/1f460643ca3a23c2a020) 方法を紹介しました。今回は tailwindcss の設定ファイルに何が書いてあるのかを見ていきます。

記事執筆の環境は下記の通りです。

- @angular/cli v12.2
- node v16.8 (npm v7.21)
- tailwindcss v2.2


`$ npx tailwindcss init` で生成される設定ファイルは tailwindcss のビルドのためのメタデータです。よく使うであろう項目がブランクで用意されていますが、それ以外にもいろいろな設定ができます。

```js
// tailwind.config.js

module.exports = {
  purge: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```

この記事はとても長いので、項目ごとに個人的な重要度（★〜★★★）をつけてみました。また★ひとつの項目でも、重要な説明部分を太字にしています。ナナメ読みする時の参考にしてください。

時間が足りなくて `Variant` `Plugins` については言及していません...。

## ★ Prefix

https://tailwindcss.com/docs/configuration#important

tailwindcss では、用意されたユーティリティクラスを使用してスタイルをあてていきます。

```html
<div class="bg-gray-100">...</div>
```

ユーティリティクラス名が自前の CSS クラスとコンフリクトするような場合、設定ファイルでプレフィクスを指定できます。

```js
// tailwind.config.js

module.exports = {
  prefix: "tw-",
};
```

プレフィクス指定後のユーティリティクラスの記述はこのように変わります。

```html
<!-- 先頭に "tw-" を記述 -->
<div class="tw-bg-gray-100"></div>
```

## ★ Important

https://tailwindcss.com/docs/configuration#important

自前のスタイルと tailwindcss がコンフリクトする場合、自前のスタイル記述のほうが優先されます。

```html
<!-- 青（tailwindcss） <<< 赤（自前のスタイル）  -->
<div class="bg-blue-500 red">...</div>

<style>
  .red { background: red; }
</style>
```

tailwindcss を優先したい場合には設定ファイルで `important` を指定します。

```js
// tailwind.config.js
module.exports = {
  important: true
};
```

```html
<!-- 青（tailwindcss） >>> 赤（自前のスタイル）  -->
<div class="bg-blue-500 red">...</div>

<style>
  .red { background: red; }
</style>
```

また設定ファイルの `important` にセレクタを指定すると、そのセレクタのみで tailwindcss が優先されるようになります。

```js
// tailwind.config.js

module.exports = {
  important: "#container"
};
```

```html
<div id="container">
  <!-- #container では tailwindcss が優先される：青 -->
  <div class="bg-blue-500 red">...</div>
</div>

<div>
  <!-- #container の外では自前のスタイルが優先される：赤 -->
  <div class="bg-blue-500 red">...</div>
</div>

<style>
  .red { background: red; }
</style>
```

important に関わらずインライン CSS は常に最優先です。

```html
<!-- 青（tailwindcss） <<< 赤（自前のスタイル） <<< 黄色（インライン） -->
<div 
  class="bg-blue-500 red" 
  style="background: yellow;"
>...</div>
```

## ★ Separator

https://tailwindcss.com/docs/configuration#separator

**tailwindcss では「マウスホバーした時に背景色を変更する」や「デバイスサイズに応じて背景色を変更する」というように変数のように値を変化させる仕組みを「バリアント」と呼びます。シンタックスは `{バリアント}:{ユーティリティクラス名}` です。**

```html
<!-- マウスホバーした時に背景色を赤にする -->
<div class="bg-blue-500 hover:bg-blue-red">...</div>
```

HTML テンプレートライブラリなどの事情で CSS クラス名に `:` を含める事ができない場合、設定ファイルでセパレータを変更する事ができます。

```js
// tailwind.config.js

module.exports = {
  // アンダースコア 2 個に変更する
  separator: "__"
};
```

```html
<!-- マウスホバーした時に背景色を赤にする -->
<div class="bg-blue-500 hover__bg-blue-red">...</div>
```

## ★ Variant Order

https://tailwindcss.com/docs/configuration#variant-order

バリアントを複数指定した場合、`first（先頭）` `odd（出現順位が奇数）` のどちらにも該当する、という事が起こります。その場合はデフォルトの優先順位が適用されます。

```html
<table>
  <!-- first（先頭）：グレー <<< odd（奇数）：黄色 -->
  <tr class="first:bg-gray-500 odd:bg-yellow-500">
    <td></td>
  </tr>
</table>
```

優先順位を変更するには、設定ファイルで `variantOrder` を指定します。

```js
// tailwind.config.js

module.exports = {
  // 後ろのほうが優先順位が高い
  variantOrder: ['odd', 'first'],
};
```

設定ファイルの変更によって `odd` より `first` が優先されるようになります。

```html
<table>
  <!-- first（先頭）：グレー >>> odd（奇数）：黄色 -->
  <tr class="first:bg-gray-500 odd:bg-yellow-500">
    <td></td>
  </tr>
</table>
```

## ★★★ Jit

https://tailwindcss.com/docs/just-in-time-mode#enabling-jit-mode

設定ファイルで JIT モードを有効にするといろいろな機能が使えるようになります。

```js
// tailwind.config.js

module.exports = {
  mode: 'jit',
};
```

※ 有効にしてもうまく動かない時は `npm start` で起動しているビルトインサーバーを一旦止めて再度立ち上げ直してみてください。

16 進表記の色が指定できます。

```html
<button class="text-[#bcbcbc]">...</button>
```

幅や高さなどのピクセル指定ができます。

```html
<button class="w-[500px]">...</button>
```

`calc()` が使えるようになります。

```html
<div class="w-[calc(500px-4rem)]">...</div>
```

カスタムプロパティが使えるようになります。カスタムプロパティは単なる変数のため tailwindcss に色である事を伝えるには `color:` バリアントと組み合わせて使います。

```scss
// src/styles.scss

:root {
  --foo: hotpink;
  --bar: skyblue;
}
```

```html
<div class="bg-[color:var(--foo)] text-[color:var(--bar)]">...</div>
```

`before::` `after::` などの疑似要素を指定できます。

```html
<!-- 前後に "*" を挿入する -->
<button class="before:content-['*']">...</button>
<button class="after:content-['*']">...</button>
```

`:disabled` などの擬似クラスを指定できます。

```html
<button class="
  disabled:opacity-25
  disabled:pointer-events-none
">...</button>
```

`:invalid` `:valid` などのバリデーションに関する擬似クラスを指定できます。

```html
<!-- 値が入力されていなければ赤枠で表示する -->
<input 
  type="text"
  required
  class="border
    invalid:border-red-500
    valid:border-gray-300"
/>

<!-- 範囲外の値であれば赤枠で表示する -->
<input
  type="number"
  min="1"
  max="3"
  class="border
    out-of-range:border-red-700
    valid:border-gray-300"
/>
```

variant はつなげて複数書くと AND 条件になります。

```html
<!-- focus（フォーカスされた）かつ odd（先頭要素）であれば背景を赤にする -->
<button class="focus:odd:bg-red-300">...</button>
```

ピンポイントで `!important` 指定ができます。

```html
<button class="!font-medium">...</button>
```

`/` に続けて不透明度の指定ができます。

```html
<div class="bg-red-500/10">...</div> <!-- 背景を不透明度 10 にする -->
<div class="bg-red-500/20">...</div> <!-- 背景を不透明度 20 にする -->
<div class="bg-red-500/30">...</div> <!-- 背景を不透明度 30 にする -->
```

`/[]` のようにすると不透明度を小数指定できます。

```html
<div class="mt-4 bg-green-500/[.3]">...</div> <!-- 背景を不透明度 0.3 にする -->
<div class="mt-4 bg-green-500/[.4]">...</div> <!-- 背景を不透明度 0.4 にする -->
<div class="mt-4 bg-green-500/[.5]">...</div> <!-- 背景を不透明度 0.5 にする -->
```

線を上下左右で個別指定できます。

```html
<button class="
  border-4
  border-l-blue-500
  border-r-red-500
  border-t-green-500
  border-b-yellow-500
">...</button>
```

テキスト選択のハイライト色を指定できます。

```html
<p class="selection:bg-blue-100">...</p>
```

リストマーカー（箇条書きの記号や数値の部分）の色を指定できます。

```html
<ul class="list-decimal list-inside marker:text-red-500">
  <li>ハム太郎</li> <!-- 1. ハム太郎 -->
  <li>リボンちゃん</li> <!-- 2. リボンちゃん -->
</ul>

<!-- "1." "2." の部分が赤で表示される -->
```

キャレット（テキストカーソル）の色を指定できます。

```html
<input type="text" class="caret-green-500">
```

`peer` クラスを指定した要素の `:checked` `:focus` などの疑似クラスをキャッチして、別の要素の外観を変更する事ができます。

```html
<ul>
  <li>
    <!-- チェックされた時 span の背景色が青になる -->
    <input type="checkbox" class="peer" />
    <span class="peer-checked:bg-blue-300">猿谷猿丸</span>
  </li>

  <li>
    <!-- チェックされた時 span の背景色が緑になる -->
    <input type="checkbox" class="peer" />
    <span class="peer-checked:bg-green-300">紅蜂</span>
  </li>

  <li>
    <!-- チェックされた時 span の背景色が赤になる -->
    <input type="checkbox" class="peer" />
    <span class="peer-checked:bg-red-300">ミスターX</span>
  </li>
</ul>
```

## ★★ Dark Mode

### media

設定ファイルで `darkMode` を `"media"` にセットすると OS の設定に応じてダークモード・ライトモードが切り替わるようになります。

```js
// tailwind.config.js

module.exports = {
  darkMode: 'media',
};
```

HTML では `bg-white dark:bg-black` のように、`dark:` バリアントありなしの 2 種類のユーティリティクラスを書いておきます。

```html
<!-- src/app/app.component.html -->

<main class="bg-white dark:bg-gray-800">
  <h1 class="text-gray-900 dark:text-white">Dark mode | Light mode</h1>
  <p class="text-gray-600 dark:text-gray-300">
    ダークモードはしっとりした色です
  </p>
</main>
```

macOS ではシステム環境設定の「外観モード」で「ライト」または「ダーク」を切り替えます。

<img src="https://user-images.githubusercontent.com/15980747/135270194-2044dcff-f7c5-46bb-a81c-cfbeea854004.png" width="400px">

ダークモードでは `dark:` バリアントのユーティリティクラスが使われます。

<img src="https://user-images.githubusercontent.com/15980747/135270187-37f08bf4-390b-4834-8013-ad11757d2e8d.png" width="400px">

ライトモードでは `dark:` バリアントなしのユーティリティクラスが使われます。

<img src="https://user-images.githubusercontent.com/15980747/135270192-5163df55-2498-459f-b796-219fda12e589.png" width="400px">

### class 

`darkMode` には `"class"` という値もセットできます。

こちらはコンテナの CSS クラスに応じてダークモード・ライトモードの切り替えを行うための機能です。`"class"` を使うと、ユーザーがスライダなどを使って任意にダークモード・ライトモードをスイッチする事ができます。

```js
// tailwind.config.js

module.exports = {
  darkMode: 'class',
};
```

```html
<!-- src/app/app.component.html -->

<!-- "dark" という CSS クラスを持ったコンテナ配下で "dark:" バリアントが有効になる -->
<main [class.dark]="enabled">
  <div class="bg-white dark:bg-black">
    <input type="checkbox" [(ngModel)]="enabled" /> 🌛 | 🌞
    <p class="text-black dark:text-white">Dark mode | Light mode</p>
  </div>
</main>
```

## ★★★ Purge

**tailwindcss では未使用のユーティリティクラスは「パージ」という仕組みによってビルドから除外されます。** 

tailwindcss は Node がプロダクションモードの時にパージを実行します。Angular のビルドはプロダクションモードとは関連しないので tailwindcss の環境変数などを使って `enabled` を切り替える必要があります。

```js
// tailwind.config.js

module.exports = {
  purge: {
    // `npm run build` が実行された時 tailwindcss の環境変数が変わるのでそれを利用する
    enabled: process.env.TAILWIND_MODE === 'build',
    content: [...],
  },
```

`enabled` でオンオフを切り替える事もできます。

```js
// tailwind.config.js

module.exports = {
  purge: {
    // プロダクションモードでなくても毎回パージを実行する
    // `npm start` でもパージが走る
    // 未使用のユーティリティクラスはブラウザの開発者ツールでサジェストされなくなる
    enabled: true,
    content: [...],
  },
```

ユーティリティクラスが未使用かどうかは `purge > content` に該当するファイルの中にクラスの記述が存在するかどうかで決まります。

```js
// tailwind.config.js

module.exports = {
  purge: {
    content: [
      './src/**/*.{html,ts}',
      './src/**/*.js',
    ],
  },
```

ユーティリティクラスの存在は単純な正規表現を使って探索されます。

<code>/[^<>"'`\s]*[^<>"'`\s:]/g</code>

そのため `.js` に書いた変数の値もヒットします。

```js
// src/foo.js

const className = "text-gray-100";
```

文字列を連結してユーティリティクラスを組み立てているような場合は、正規表現にヒットしないためビルド済みファイルに出力されません。

```js
// src/foo.js

const className1 = "text-" + "gray-200"; // 出力されない
const className2 = `bg-${color}-red`; // 出力されない
```

## ★ Safelist

ビルド済みの CSS を Angular 外から参照する場合は、`safelist` を使う事でパージ対象外のユーティリティクラスを指定する事ができます。

```js
// tailwind.config.js
module.exports = {
  mode: 'jit',
  purge: {
    content: ['src/**/*.{html,ts}'],
    safelist: [
      // このクラスは未使用であってもパージしない
      'bg-blue-300'
    ],
  },
```

```sh
npm run build
```

```css
/* styles.{hash}.css */

.bg-blue-300{--tw-bg-opacity:1;background-color:rgba(147,197,253,var(--tw-bg-opacity))}
```

## ★ Core Plugins

使用しない CSS は `corePlugins` で false を設定します。

```js
// tailwind.config.js

module.exports = {
  corePlugins: {
    backgroundColor: false,
    display: false,
  },
};
```

このようにすると `background-color` と `display` を変更するユーティリティクラスはビルド済みファイルに含まれなくなります。そのため `bg-yellow-500` `inline` は見た目上の意味をなさなくなります。

```html
<!-- ユーティリティクラスを指定しても見た目が何も変わらない -->
<div class="bg-yellow-500 inline"></div>
```

## ★ Core Plugins > preflight

https://tailwindcss.com/docs/preflight

**tailwindcss ではユーティリティクラスでスタイルをあてる前準備として、ブラウザのデフォルトのスタイルを打ち消すノーマライズの処理を行っています。いわゆる「CSS リセット」ですが tailwindcss ではこれを「プリフライト」と呼びます。**

tailwindcss をシュッと導入した Angular では `styles.scss` に `base` というインポート文があるはずです。この部分が「プリフライト」です。

```scss
// styles.scss

@import 'tailwindcss/base'; // <=== プリフライト
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
```

プリフライトの CSS の中身は GitHub で見る事ができます。

https://unpkg.com/tailwindcss@2.2.15/dist/base.css

ユーティリティクラスを何も指定しないと、プリフライトされただけの状態になります。ブラウザのデフォルトのスタイルは打ち消されているため、枠線も背景色も何も表示されません。

```html
<!-- 枠線も背景色もないボタン -->
<button class="">...</button>
```

プリフライトが困るという場合は `preflight` で無効にできます。

```js
module.exports = {
  corePlugins: {
    preflight: false,
  },
```

特に理由がない限りデフォルトのまま有効にしておいたほうが良いでしょう。プリフライトは、ブラウザ間のレンダリングの差異をリセットしてユーティリティクラスをスムーズに適用するためのものだからです。

## おわりに

tailwindcss がなんとなく凄そうって雰囲気が伝わりましたでしょうか。これだけ設定ファイルをいじくりまわす事はそうそうないと思いますが、この記事を「そういえばあんな事ができたっけ...」とチャッと開ける引き出しとして使っていただけたらと思います。

次の記事では tailwindcss のカスタマイズについて紹介します。
[Angular 12 にシュッと導入してチャッと理解した tailwindcss をピャッとカスタマイズする](https://qiita.com/ringtail003/items/55fa0f681a1b73c7f2b7)