Tailwind慎重派のTailwind CSS記述方法

## はじめに

Tailwind CSSは、シンプルで効率的なCSSフレームワークとしてWeb開発者に広く愛用されています。しかし、ReactなどのJavaScript UIフレームワークでは、CSS ModulesやCSS in JSなどのスタイル定義には多くの選択肢があり、Tailwind CSSを採用する際には慎重なアプローチが求められることもあります。

本記事では、「Tailwind慎重派のTailwind CSS記述方法」と題し、私がTailwind CSSの採用に慎重な理由と、それを解決するために作成した `tw-tag` ライブラリについて紹介します。


## Tailwind CSSの採用に慎重な理由

私がTailwind CSSの採用に慎重な理由の1つは、HTMLのクラス属性の箇所に直接記述する必要がある点です。つまり、Tailwind CSSには自身を記述する専用の場所がないため、次のような問題が発生します。

- クラス属性の記載が長くなり、文章構造（HTML）の可読性が低下する。
- Tailwind CSSを使用している箇所が明示的にわかりづらい。

前者の問題はよく知られていますが、後者の問題はTailwind CSSを剥がす際に特に重要となります。通常のクラス属性にリテラルでTailwindのクラス名を記述している場合、Tailwindの使用箇所を特定するのが難しくなります。

なので、私は `tw-tag` というライブラリを作成しました。


## tw-tagライブラリの紹介

`tw-tag` は、Tailwind CSSのクラス名を記述するための特別な場所を提供するライブラリです。このライブラリは、タグテンプレートに与えられた引数をほぼそのまま出力します（空白文字のトリムだけ行われます）。

npmからインストールできます。

```sh
npm install tw-tag
# or
yarn add tw-tag
```

以下は、`tw-tag` の基本的な使用例です。

```jsx
import { tw } from 'tw-tag'

const twStyle = tw`
  flex
  pt-4
  text-center
  rotate-90
`

console.log(twStyle) // 'flex pt-4 text-center rotate-90'

// Reactでの使用例
const FooComponent = () => <div className={twStyle} />

// インラインで直接記述してもOK
const BarComponent = () => <div className={tw`my-2 mx-auto`} />
```

このように、`tw` 関数を使用することでTailwindのクラス名を明示的に管理できます。

## tw-tagの利点

### 1. 使用箇所が明示的にわかる

`tw-tag` を使用することで、Tailwindのクラス名が使われている箇所が一目でわかるようになります。`tw` 関数で囲むことで、Tailwindのスタイル定義がどこに使用されているのかを簡単に把握できます。これはTailwind CSSを剥がす際に特に有用であり、移行作業を容易にします。TypeScriptを採用している場合であれば、`import`文を削除するだけで剥がす際の修正に必要な箇所がエラーとして表示されることでしょう。

また、`tw-tag` はEmotionに近い書き方をしています。Tailwind to CSSなツールを使用すれば、比較的簡単に `@emotion/css` への移行ができるのではないでしょうか。

### 2. インテリジェンスのサポート

`tw-tag` を使用することで、JavaScriptコード内の任意の場所でTailwindのサジェストを利用できるようになります。VS CodeのTailwind CSS IntelliSense拡張機能などを使用している場合、通常はHTMLテンプレート内のクラス属性内でしかサジェストが効きませんが、下記の設定を行うことで、`tw` 関数内（つまり、任意のソースコード内）でもサジェストが利用可能になります。

```json-doc
// settings.json
{
  // ...
  "tailwindCSS.experimental.classRegex": [
    "tw\\(?`([^`]*)" // tw`...`, tw(`...`
  ],
}
```

### 3. 自由な改行が可能

`tw-tag` を使用することで、JavaScriptコード内でスタイルを自由に記述できます。これにより、HTMLテンプレート内にスタイルを詰め込む必要がなくなり、スタイルの可読性を向上させることができます。また、擬似クラスやメディアクエリ付きのスタイルをグループ化する際にも便利です。

```js
const twStyle = tw`
  flex
  pt-4
  text-center
  rotate-90

  hover:flex
  hover:pt-4
  hover:text-center
  hover:rotate-90
  
  md:flex
  md:pt-4
  md:text-center
  md:rotate-90
`
```

ちなみに、Babel Pluginを用意しているので、これを使用すればビルド後のソースに無駄な改行が含まれることはありません。

```js
// babel.config.js
module.exports = {
  plugins: [
    'tw-tag/babel-plugin',
  ],
}
```

```js
// before
import { tw } from 'tw-tag'

const fooStyle = tw`
  flex
  pt-4
  text-center
  rotate-90
`

// after
const fooStyle = `flex pt-4 text-center rotate-90`
```

### 4. 型定義によるスタイル定義の確認（試験的機能）

`tw-tag` では試験的な機能として、型定義によるスタイル定義（Tailwindクラス名）の確認を行うことができます。タグ付きテンプレートリテラル記法を使用する代わりに、関数呼び出し形式でスタイルを定義することで、変数の型と実行結果の文字列を一致させることができます。これにより、HTMLテンプレート上で変数名をマウスでホバーするだけでスタイル定義を確認できます。

```ts
// これまで紹介していた書き方
// TypeScriptでの、tagStyle変数の型: string
const tagStyle = tw`
  flex
  pt-4
`

// 試験的な書き方
// TypeScriptでの、callStyle変数の型: 'flex pt-4'
const callStyle = tw(`
  flex
  pt-4
`)
```

タグ付きテンプレートリテラル記法（`` tw`...` ``）形式のサポートは、`TemplateStringsArray`の推論が改善[^ts#49552]されたら対応を行います。

## まとめ

Tailwind CSSは強力なCSSフレームワークですが、使用する際には慎重なアプローチが必要です。`tw-tag` を使用することで、Tailwindのクラス名を明示的に管理し、スタイルの可読性を向上させることができます。是非、慎重なTailwind CSSの採用に役立ててください。

## おまけ

Zero runtimeで軽量CSSなCSS-in-JSライブラリを作成中です。制作過程やメモをZennのスクラップに記載していますので、もしよろしければ見て言ってください。

https://zenn.dev/imasanari/scraps/d00c333a4d530a



[^ts#49552]: `Experiment` ラベル付きですが、プルリクは既に存在しています。
https://github.com/microsoft/TypeScript/pull/49552