Angular 12 にシュッと導入してチャッと理解した tailwindcss をピャッとカスタマイズする

前の記事では [Angular にシュッと tailwindcss を導入](https://qiita.com/ringtail003/items/1f460643ca3a23c2a020) して [設定ファイルの内容をチャッと紹介](https://qiita.com/ringtail003/items/44b13d2bc777a68381d0) しました。今回は tailwindcss の設定ファイルを使って、カスタマイズを加えていきます。

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

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

今回は tailwindcss の設定ファイルと Angular の SCSS のファイルを変更します。

```js
// tailwind.config.js

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

```scss
// src/styles.scss

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
```

## テーマの拡張

https://tailwindcss.com/docs/theme

tailwindcss はカラーやスペースなど基本のスタイルセットを「テーマ」として定義しています。設定ファイルの `theme` の部分を変更するとカスタマイズする事ができます。

試しに `olive` というカラーパレットを追加してみましょう。

```js
// tailwind.config.js

// (1) デフォルトのテーマを読み込む
const theme = require('tailwindcss/defaultTheme');

module.exports = {
  theme: {
    // (2) デフォルトとオリジナルのカラーパレットをマージする
    colors: {
      ...theme.colors,
      olive: {
        400: "#b3b310",
        500: "#808000",
        600: "#636309",
      }
    },
    extend: {},
  },
}
```

追加したカラーパレットは背景色やテキストの色として使えるようになります。

```html
<div class="bg-olive-500">...</div>
<div class="text-olive-600">...</div>
```

デバイスサイズを示す `screens` も同様にカスタマイズできます。

```js
// tailwind.config.js

// (1) デフォルトのテーマを読み込む
const theme = require('tailwindcss/defaultTheme');

module.exports = {
  theme: {
    // (2) デフォルトとオリジナルのデバイスサイズをマージする
    screens: {
      ...theme.screens,
      tablet: "768px",
    },
    extend: {},
  },
}
```

デバイスサイズは `sm:` `md:` のようなバリアントとして使えるようになります。

```html
<!-- タブレットサイズであれば 1 / 2 の横幅にする -->
<div class="
  w-full
  tablet:w-1/2
  bg-gray-300
">...</div>
```

### defaultTheme とは

さてさて、tailwindcss のデフォルトのテーマとして `defaultTheme` を読み込みましたが、これは何でしょうか？

```js
const config = require('tailwindcss/defaultTheme');
```

`require` を追いかけるとこのファイルにたどり着きます。これが tailwindcss で定義されているデフォルトのテーマの内容です。

```js
// node_modules/tailwindcss/stubs/defaultConfig.stub.js
// （一部抜粋したもの）

const colors = require('../colors')

module.exports = {
  purge: [],
  presets: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
    screens: {
      sm: '640px',
      md: '768px',
      ...
    },
    colors: {
      transparent: 'transparent',
      current: 'currentColor',
      black: colors.black,
      ...
    },
    spacing: {
      px: '1px',
      0: '0px',
      0.5: '0.125rem',
      ...
    // （以下省略）
```

https://github.com/tailwindlabs/tailwindcss/blob/v2.2.15/stubs/defaultConfig.stub.js

このファイルを見れば元の設定がどのようになっているか分かります。カスタマイズで書き方に迷ったらこのファイルを見てみましょう。

### 値を省略できるクラス

tailwindcss のユーティリティクラスは `{ユーティリティ}-{値}` のように CSS クラス名が決定します。おなじみの `bg-gray-500` などがそうですね。

それとは別に `rounded（角丸）` のように値部分を省略できるクラスも存在します。このようなクラスは `DEFAULT` キーワードでデフォルト値が指定されています。

```js
// ==================================================
// defaultConfig.stub.js（デフォルトのテーマ）

module.exports = {
  theme: {
    borderRadius: {
      none: '0px', // rounded-none
      sm: '0.125rem', // rounded-sm
      DEFAULT: '0.25rem', // rounded
    },
```

```html
<!-- border-radius: .25rem が適用される -->
<div class="rounded">...</div>

<!-- border-radius: .5rem が適用される -->
<div class="rounded-lg">...</div>
```

### 上書きと拡張

これまで紹介したコードでは設定ファイルの `theme` に変更を加えましたが、実はカスタマイズには「上書き」と「拡張」の 2 種類のやり方があります。

デフォルトのテーマを「上書き」するには `theme` を使います。tailwindcss のカラーパレットをまるっと上書きするので、デフォルトのテーマも使いたい場合は `defaultTheme` を読み込んでマージします。

```js
// tailwind.config.js

// const theme = require('tailwindcss/defaultTheme');

module.exports = {
  theme: {
    colors: {
      // この行をコメントアウトしてみる
      // ...theme.colors,
      olive: {
        400: "#b3b310",
        500: "#808000",
        600: "#636309",
      }
    },
  },
}
```

```html
<!-- 色が表示されない -->
<p class="text-red-400">...</p>

<!-- 色が表示される -->
<p class="text-olive-400">...</p>
```

もうひとつのやり方は「拡張」です。こちらは `theme` の下の `extend` を使います。tailwindcss のカラーパレットをマージする必要はありません。

```js
// tailwind.config.js

module.exports = {
  theme: {
    // (1) extend の下にカラーパレットを定義する
    extend: {
      colors: {
        olive: {
          400: "#b3b310",
          500: "#808000",
          600: "#636309",
        }
      },
    },
  },
}
```

```html
<!-- 色が表示される -->
<p class="text-red-400">...</p>

<!-- 色が表示される -->
<p class="text-olive-400">...</p>
```

### theme にどのようなキーが指定できるのか

公式ドキュメントにキーの一覧があります。これらのキーを使って `theme` もしくは `theme > extend` でデフォルトの設定をカスタマイズする事ができます。

https://tailwindcss.com/docs/theme#configuration-reference

```js
// tailwind.config.js

const theme = require('tailwindcss/defaultTheme');

module.exports = {
  theme: {
    extend: {
      screens: { ... }, // デバイスサイズ
      colors: { ... }, // カラーパレット
      spacing: { ... }, // 要素間のスペース
      animations: { ... }, // アニメーション
      ...
    },
  },
}
```

### 同じグレーでも数種類のカラーパレットがある

`bg-gray-xxx` ユーティリティクラスを使うとグレーの背景色を指定できますが、実はこのカラーパレット、同じグレーでも数種類が用意されています。 `colors` を require するとカラーパレットを選ぶ事ができます。

```js
// tailwind.config.js
const theme = require('tailwindcss/defaultTheme');
const colors = require('tailwindcss/colors');

module.exports = {
  theme: {
    colors: {
      ...theme.colors,
      // gray: colors.coolGray, やや青みがかったグレー
      // gray: colors.blueGray, 青みがかったグレー
      gray: colors.warmGray, // 温かみのあるグレー
    },
```

https://tailwindcss.com/docs/customizing-colors#color-palette-reference

### カラーパレットに名前を付ける

カラーパレットには `primary` `secondary` などなど、任意の名前を付ける事ができます。

```js
// tailwind.config.js

const theme = require('tailwindcss/defaultTheme');
const colors = require('tailwindcss/colors');

module.exports = {
  theme: {
    colors: {
      ...theme.colors,
      primary: colors.emerald,
      secondary: colors.blueGray,
      error: colors.rose,
    },
```

```html
<div class="bg-primary-400">...</div> <!-- primary: エメラルドグリーン -->
<div class="bg-secondary-400">...</div> <!-- secondary: 青みがかったグレー -->
<div class="bg-error-400">...</div> <!-- error: ローズ -->
```

## 設定ファイルを複数に分ける

tailwindcss の設定ファイルは複数に分けてマージする事ができます。一例として、コーポレートカラーを定義した設定ファイルを追加してみましょう。

```js
// ==================================================
// tailwind.config.corp.js（任意に追加したファイル）

// コーポレートカラーの定義
module.exports = {
  theme: {
    // extend でカラーパレットを拡張
    extend: {
      colors: {
        corporate: {
          DEFAULT: "blue",
          light: "lightblue",
          ...
```

次に、プロジェクトのブランドカラーを定義した設定ファイルを追加します。

```js
// ==================================================
// tailwind.config.proj.js（任意に追加したファイル）

// プロジェクトのブランドカラーの定義
module.exports = {
  theme: {
    // extend でカラーパレットを拡張
    extend: {
      colors: {
        brand: {
          DEFAULT: "green",
          light: "lightgreen",
          ...
```

それぞれの設定ファイルを `tailwind.config.js` の `presets` でマージします。

```js
// tailwind.config.js

module.exports = {
  presets: [
    require('./tailwind.config.proj.js'),
    require('./tailwind.config.corp.js'),
  ],
  ...
}
```

マージするとコーポレートカラー・ブランドカラーの両方が使えるようになります。

```html
<!-- コーポレートカラー -->
<div class="text-corporate bg-corporate-light">...</div>

<!-- ブランドカラー -->
<div class="text-brand bg-brand-light">...</div>
```

この例のようにテーマの「拡張」を使って設定ファイルをマージすると、ファイルを読み込んだ順に次々とテーマが拡張されていきます。

```js
// tailwind.config.js

module.exports = {
  // 読み込み順：3
  theme: {
    extend: {
      colors: { ... }
    }
  },
  presets: [
    require('./tailwind.config.proj.js'), // 読み込み順：1
    require('./tailwind.config.corp.js'), // 読み込み順：2
  ],

  // 1 + 2 + 3 のテーマができあがる
  // 同じ名前の設定がある場合 3 で上書きされる
```

パージなど拡張できないものは、後から読み込まれた設定ファイルの内容で上書きされます。

```js
// tailwind.config.js

module.exports = {
  // 読み込み順：3
  purge: {
    enabled: process.env.TAILWIND_MODE === 'build',
    content: ['./src/**/*.{html,ts}'],
  },
  presets: [
    require('./tailwind.config.proj.js'), // 読み込み順：1
    require('./tailwind.config.corp.js'), // 読み込み順：2
  ],
  
  // 1, 2, 3 それぞれでパージが設定されている場合は 3 で上書きされる
```

`presets` に空配列を指定すると「何もない設定」で上書きされてしまうので、設定ファイルをマージしない場合はキーごと削除しておきましょう。

```js
// tailwind.config.js

// × これだと「何もない設定」で上書きされる
module.exports = {
  presets: [],
  ...

// ○ presets キーを削除しておく
module.exports = {
  ...
```


## スタイルセットを作る

https://tailwindcss.com/docs/adding-base-styles

tailwindcss のユーティリティクラスをまとめてオリジナルのスタイルセットを作る事ができます。設定ファイルでも定義できますが、エディタでクラス名がサジェストされるという理由で SCSS のファイルを使うのがおすすめです。

書き方は `@layer` に続けて `base` `components` `utilities` いずれかの「バケット」を指定します。

```scss
@layer base {}
@layer components {}
@layer utilities {}
```

### `@layer base` 

ビルドした CSS ファイルの先頭に出力されます。HTML の標準のタグに自動で設定したいスタイルなど、一番基本になる層のスタイルに向いています。

```scss
// src/styles.scss

@layer base {
  h1 {
    @apply text-3xl font-semibold
  }
  h2 {
    @apply text-2xl font-bold
  }
  h3 {
    @apply text-xl
  }
}
```

```html
<h1>...</h1>
<h2>...</h2>
<h3>...</h3>
```

### `@layer components` 

ビルドした CSS ファイルで base の次に出力されます。開発者が任意に追加するコンポーネント層のスタイルに向いています。

```scss
// src/styles.scss

@layer components {
  .btn {
    @apply p-2 rounded-md;

    &.fill-primary {
      @apply text-white bg-blue-500 hover:bg-blue-700;
    }
    &.fill-secondary {
      @apply text-white bg-gray-500 hover:bg-gray-700;
    }

    &:disabled {
      @apply opacity-50 pointer-events-none;
    }
  }
}
```

```html
<button class="btn fill-primary">Primary</button>
<button class="btn fill-secondary">Secondary</button>

<button class="btn fill-primary" disabled>Primary</button>
<button class="btn fill-secondary" disabled>Secondary</button>
```

### `@layer utilities` 

ビルドした CSS ファイルで components の次に出力されます。コンポーネントの外観にプラスアルファするユーティリティ層のスタイルに向いています。

```scss
// src/styles.scss

@layer utilities {
  // "hover:" で利用するユーティリティクラス
  @variants hover {
    .text-scale {
      @apply text-xl font-bold;
    }
  }

  // "md:" "lg:" などで利用するユーティリティクラス
  @variants responsive {
    .container-narrow {
      @apply m-2 p-2;
    }
    .container-wide {
      @apply m-10 p-10;
    }
  }
}
```

```html
<div class="
  text-sm
  hover:text-scale
">...</div>

<div class="
  container-narrow lg:container-wide
">...</div>
```

## どのようにカスタマイズするのが正解か

tailwindcss の設定をピャッとカスタマイズできる気分になっていただけたでしょうか。いろんな事ができすぎて、どうカスタマイズすればいいのか迷ってしまうかもしれません。私が数ヶ月間 tailwindcss を使ってみて思う事は「最初はあれこれカスタマイズせず、愚直に長いユーティリティクラスを書いたほうがいい」です。

```html
<div class="
  m-3 p-2
  text-gray-700
  bg-blue-100 hover:bg-blue-200
  border border-red-300
  rounded-md
  shadow-sm
">...</div>
```

HTML がユーティリティクラスだらけになるのでウワッと思うかもしれません。ただこれは tailwindcss に限った事ではなく、SCSS でスタイルを書くのも同じ事だと思っています。コンポーネントを 2 つ 3 つ作った段階で BEM のような設計思想を取り入れようとするとハードルが高くなります。何度も同じコードを書くうちに「あれ、これって共通化・抽象化できるのでは？」と湧き上がる疑問をベースに整理していけば良いのではないかなと思っています。

今回の記事では紹介していませんが、オープンソースのプラグインもいろいろ公開されています。それを吟味して使うのも良いですね。

tailwindcss は多機能さを使いこなすゴールではなく、ひとつの手段です。プロジェクトの向かう表現方法のゴールが見えてきたら、あれこれ模索しながらカスタマイズを検討してみましょう。

## おわりに

最後まで読んでいただきありがとうございました。この記事がみなさまの開発の一助になれば幸いです。