初めてのtailwindcss (Vue.js + PurgeCSS)

# この記事は
 [CSS Advent Calendar 2019](https://qiita.com/advent-calendar/2019/css) 21日目の記事です。
気になってた[tailwindcss](https://tailwindcss.com/)を、Vue.jsでやってみましたという記事です。
リポジトリはこちら→ https://github.com/hisako135/tailwindcss_vue_demo
<img width="1083" alt="スクリーンショット 2019-12-22 3.10.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58268/90c3a48d-b9fa-4b22-2fd4-432cf1170e85.png">

やったことは以下のような感じです。

- Vue.js での[tailwindcss](https://tailwindcss.com/)のセットアップ
- [PurgeCSS](https://www.purgecss.com/) でのファイル容量の削減

# tailwindcssってなんぞ
"Bootstrapをゴリゴリいじってカスタマイズしても良いのだが、そこに時間かけるんならCSSフレームワーク使う利点なくない...？:thinking: " などと思ったことがある人はいませんか...？
グリッドシステムとForm周りさえあればよくて、他の必要なコンポーネントは自分で作った方が楽だよな、とかゴニョゴニョ...(小声)

そこで[tailwindcss](https://tailwindcss.com/)です！


> Most CSS frameworks do too much.
> (ほとんどのCSSフレームワークは多くのことをやり過ぎている)
https://tailwindcss.com/#what-is-tailwind

"あらゆる種類のコンポーネントが用意されているCSSフレームワークって初期段階に素早く作るのには確かにとっても便利。でもカスタマイズしようとするとしんどいよね。事前に設計されたコンポーネントの代わりに、低レベルなUtilityクラスを提供することによって、HTMLを離れることなくデザインをカスタマイズできるよ。"
というのがtailwindcssです。

ちなみにCSSでのUtilityクラスとは、`.font-small {font-size: 10px;}` や `.text-left {text-align: left;}` のように、単一のプロパティを定義した汎用的なクラスを指します。Helperクラスとも呼ばれたり。
[FLOCSS](https://github.com/hiloki/flocss)ではUtilityレイヤーに、FLOCSSのベースとなっている[MCSS](https://operatino.github.io/MCSS/ja/)ではCosmeticsレイヤーあたりに記載されるやつです。


tailwindcssのUtility-firstの考え方についてはこちらの翻訳記事に詳細書いてありますので是非！
:book: [CSS Utility Classes and "Separation of Concerns" 翻訳](https://qiita.com/kojiii/items/fbd61ca8ac9cd7350475)



# セットアップ手順
何はともあれVue.jsでのtailwindcssのセットアップをやっていきましょう。
まずは[vue-cliでVue.jsプロジェクトを作成](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)します。

以降、[tailwindcss/setup-examples](https://github.com/tailwindcss/setup-examples/tree/master/examples/vue-cli) を参考に進めます。

- tailwindcssをインストール

```shell
npm install tailwindcss
```
- postcss.config.js ファイルを作成しプラグインとして設定

```postcss.config.js
const tailwindcss = require('tailwindcss');
const autoprefixer = require('autoprefixer');

module.exports = {
  plugins: [
    tailwindcss,
    autoprefixer,
  ]
}
```

- `src/assets/tailwind.css` というtailwindのスタイル用のCSSファイルを作成します

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

- `main.js`で `tailwind.css` をimportします

```src/main.js
import Vue from 'vue'
import App from './App.vue'
import '@/assets/tailwind.css' //これ

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
```

セットアップは以上です！ :tada: 

今回は[NavigationやCardの例](https://tailwindcss.com/components)を利用してサクッと画面を作っています。
あんまりtailwindcssで作り込まれてると `<transition>`コンポーネント使いづらいな...と、うっすら思いましたとさ（結果、使ってない）。

```src/components/Header.vue
<template>
  <nav class="flex items-center justify-between flex-wrap bg-teal-500 p-6">
    <div class="flex items-center flex-shrink-0 text-white mr-6">
      <svg class="fill-current h-8 w-8 mr-2" width="54" height="54" viewBox="0 0 54 54" xmlns="http://www.w3.org/2000/svg"><path d="M13.5 22.1c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05zM0 38.3c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05z"/></svg>
      <span class="font-semibold text-xl tracking-tight">Tailwind CSS</span>
    </div>
    <div @click='hideMenu=!hideMenu' class="block lg:hidden">
      <button class="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white">
        <svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
      </button>
    </div>
    <div :class='{"hidden md:hidden sm:hidden":hideMenu}' class="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
      <div class="text-sm lg:flex-grow">
        <a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white mr-4">
          Docs
        </a>
        <a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white mr-4">
          Examples
        </a>
        <a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">
          Blog
        </a>
      </div>
      <div>
        <a href="#" class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white mt-4 lg:mt-0">Download</a>
      </div>
    </div>
  </nav>
</template>

<script>
export default {
  data() {
    return {
      hideMenu: true
    }
  }
}
</script>
```


# ざっくり紹介tailwindcss

さて、tailwindcssの特徴を極一部ですがご紹介します！
めちゃくちゃ詳しく書いて下さっている記事はこちらですので是非！
:book: [TailwindCSS入門 ~ Utility First + デザインシステムの構築 ~i](https://qiita.com/zzzzz/items/68461080515ec1012980)


## `:(コロン)` でブレークポイントや擬似クラスの指定ができる

`lg:inline-block` `hover:text-white` でブレークポイントや擬似クラスへの指定をしています。1つのスタイルを使い回せるの便利だな〜。

```src/components/Header.vue
<a href="#responsive-header" class="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">Blog</a>
```



## `@apply`
組み合わせて自由に見た目を作れるのが利点とはいえ、何度も同じクラスを並べるのは苦痛ですよね。`@apply` で、utilityの組み合わせをカスタムコンポーネントのクラスとしてまとめることができます。

```src/assets/tailwind.css
@tailwind base;
@tailwind components;
/* Extracting Components */
.btn-teal {
  @apply bg-teal-500 text-white py-2 px-4 rounded w-full;
}
.btn-teal:hover {
  @apply bg-teal-700 transition-colors transition-250 transition-linear;
}
@tailwind utilities;
```

[カスタムコンポーネントのクラスは、utilitiesの前に読み込まないとダメだそうです。](https://tailwindcss.com/docs/extracting-components/#extracting-css-components-with-apply)

## カスタマイズが簡単

tailwindcssで定義されているスタイルは、 `tailwind.config.js` でカスタマイズできます。


```shell

npx tailwind init //tailwind.config.jsファイルを作成するコマンド
```

```tailwind.config.js
module.exports = {
  theme: {
    extend: {}
  },
  variants: {},
  plugins: []
}
```

作成された `tailwind.config.js` にカスタマイズしたいプロパティや追加したいプロパティを記載していきます。
[tailwindcssでは現状 `transition` のようなアニメーションに関するプロパティが定義されていません。](https://github.com/tailwindcss/tailwindcss/issues/14#issuecomment-341209139)
そういうものの追加にも便利そうですね。拡張が簡単なのは嬉しい :relieved: 
（今回はtransitionのutilityクラスを定義するのに[tailwindcss-transitions](https://github.com/benface/tailwindcss-transitions)を使用しました。）




# PurgeCSSで使ってないCSSを削除
さて、なんだかスマートに感じるtailwindcssですが、[ファイル容量は実は結構重い](https://tailwindcss.com/docs/controlling-file-size/#app)です。
<img width="694" alt="スクリーンショット 2019-12-22 2.08.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58268/11bae2d9-2f02-131a-243d-8d58ae81e568.png">

[PurgeCSS](https://www.purgecss.com/)で、使ってないCSSを削除していきましょう。

- PurgeCSSをインストール

```bash

npm install @fullhuman/postcss-purgecss --save-dev
```
- `postcss.config.js` にプラグイン設定
    - tailwindcssとVue.jsのそれぞれの特徴を踏まえ適切にパージするように `defaultExtractor` と `whitelistPatterns` を設定します。詳細はこちら → 
https://medium.com/@kyis/vue-tailwind-purgecss-the-right-way-c70d04461475

```postcss.config.js
const autoprefixer = require('autoprefixer');
const tailwindcss = require('tailwindcss');
const purgecss = require('@fullhuman/postcss-purgecss')({
  // テンプレートファイルへのパス。今回は'./src/**/*.vue'
  content: [
    './src/**/*.vue'
  ],
  // https://medium.com/@kyis/vue-tailwind-purgecss-the-right-way-c70d04461475
  defaultExtractor: (content) => {
    const contentWithoutStyleBlocks = content.replace(/<style[^]+?<\/style>/gi, '')
    return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || []
  },
  whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!cursor-move).+-move$/, /^router-link(|-exact)-active$/ ],
})

module.exports = {
  plugins: [
    autoprefixer,
    tailwindcss,
    // 開発中はビルド時間がかかってしまうので、productionの時のみ実行
    ...process.env.NODE_ENV === 'production'
    ? [purgecss]
    : []
  ]
}
```
## Before/After

`npm build` して、 `dist/css/`配下のCSS容量を見て見ます。
### Before
<img alt="スクリーンショット 2019-12-21 22.07.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58268/ddd76f4a-1db1-f192-8b80-2d51a6238592.png">
### After
<img alt="スクリーンショット 2019-12-21 22.18.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58268/f1853a50-733d-ce44-b07c-39d67015ac8e.png">


**644KB減！！**
すごいぞ！PurgeCSS！！ :tada: 

今回はサンプルのためスタイリング自体が少ないので、実際のプロジェクトではもっと大きくなるんでしょうけれども、いやぁしかし気持ち良いですね :relieved: 

# まとめ

以上、初めてのtailwindcss (Vue.js + PurgeCSS)でした！
個人的にはめちゃくちゃ好きな思想のフレームワークです。
シンプルで良い。自由 is フリーダム。必要なものは自分で作る :relieved:

緻密かつ堅牢なCSSフレームワークに疲弊している方、ぜひトライしてみては如何でしょうか？

## 参考にさせて頂いたものまとめ

- [tailwindcss](https://tailwindcss.com/)
-  [CSS Utility Classes and "Separation of Concerns" 翻訳](https://qiita.com/kojiii/items/fbd61ca8ac9cd7350475)
- [TailwindCSS入門 ~ Utility First + デザインシステムの構築 ~i](https://qiita.com/zzzzz/items/68461080515ec1012980)
- [tailwindcss/setup-examples](https://github.com/tailwindcss/setup-examples/tree/master/examples/vue-cli)
- [Setting up Tailwind CSS with Vue.js](https://markus.oberlehner.net/blog/setting-up-tailwind-css-with-vue/)
- [Vue + Tailwind + PurgeCSS — The right way](https://medium.com/@kyis/vue-tailwind-purgecss-the-right-way-c70d04461475)
- [React上でtailwindcssを使いたい！](https://qiita.com/Annoske/items/1087a1205d3783ea3d94)


tailwindcssなのかTailwindcssなのか、はたまたTailwindCSSなのか、正しい表記が未だに分かりません :relieved: