大嫌いだったユーティリティーファーストCSS（TailwindCSS）と向き合った話

最近、業務にTailwindCSSとAstroを取り入れ始めました。
ここではそれまでの苦悩、導入までの軌跡や工夫についてご紹介します。

## 育った環境

私が社会人になるころにちょうどWeb標準という言葉が浸透し、「世の中はXHTMLでマークアップはセマンティックであるべし！」という価値観で育ちました。
CSSは名前の通り、カスケードするから良いものだと思っていましたし、クラス名はデザインの都合ではなく、機能や意味からつけることが幸福につながると信じていました。

当時は大量コーディングをより効率よく行うためにコマンドラインのプログラムをつくってXPathで抽出・変換し少しでも作業を軽減できないか試行錯誤したり、複数人でのコーディングをどのように効率よくまわせるか考えたり、HTMLをXMLとして捉えて、当時のXHTMLの思想に則って業務を進めていました。

XHTMLは廃れ、それほど厳格性を求められなくなった時代でも引き続き、セマンティックなCSSネーミングを好みました。
より効率よくスタイルを定義できるようSass等のCSS拡張メタ言語を取り入れ、BootstrapのようなCSSフレームワークのお作法、変数として設定できるようにする項目を真似、なるべくフレームワークを最小限のカスタマイズだけで汎用的に導入できないかなど案件に応じて試行錯誤していました。

## TailwindCSSとの出会い

私はサーバーサイドの開発は主にPHPを利用しており、これまでPHPフレームワークはCakePHP、ZendFramework、Symfonyなどを用いていましたが、世の中はLaravelが人気を博しており、扱えるエンジニアを調達しやすいLaravelに自分自身も慣れておくべきかと学習を始めたのが、TailwindCSSを学ぶきっかけでした。

Laravel JetstreamはTailwindCSSが使われており、名前は聞いたことがあったものの、実際にTailwindCSSのコードを触ってみるのはこれが初めてでした。

当時はなんて馬鹿げたものを使っているのだろうという印象でした。

当時の自分のメモ。

> Utility FirstのCSSとか正気か？と思う。だってそれならstyle属性でガンガン書いていくのと変わらないやん。
> 共通してフォームのデザイン変更したい場合、すべてのHTMLをゴリゴリ書き換えるつもり？

TailwindCSSで嫌気がさした私は、やはりSymfonyとBootstrapで良いのじゃないかということでLaravelも業務での利用までは至りませんでした。（自分にとってはLaravel自体の機能もSymfonyと比較して見劣りしていました。）

## ユーティリティーファーストが求められる時代に

ですが、ここ数年でTailwindCSSという言葉を頻繁にみかけるようになりました。

一方で、私はセマンティックなCSSというものに限界と辛さを感じていました。
というのも、CSSは学びはじめのハードルはとても低く簡単に理解できるものの、長期的にメンテナンスできる構成、再利用性、想定しない副作用の回避など、長い経験でしか培えない独特な難しさがあります。

その打開策として様々な設計の考え方が体系化されBEM、SMACSS、FLOCSSなどが多くの現場で用いられるようになりました。

これらは複数のメンバーでCSSを管理・運用するには必要なものと認識していますが、運用負荷が大きく、スピード感を出すには重たすぎるルールのように感じています。

だからといって、「いい感じで、OOCSSである程度は自由にかいてね。」
という緩いルールの場合、レビューでコードの修正を求める側は
「なんか違うんだよねぇ、例えば、こういうケースを想定すると..」
みたいな懸念を説明するにはコストがとてもかかる。
レビューされる側はいまいち何がOKでなにがNGかの判断基準がわからなくてしんどい。
みたいな事になります。

また、コンポーネントごとにスタイルを用意したところで、開発担当者が別にいる場合にユーティリティスタイルを求められることも珍しくありません。

* 単純に上に10pxマージンをとるだけのスタイル
* フレックスボックスにするだけのスタイル

など、状況に応じて気軽につかえるスタイルの作成を依頼されることがあります。

数年前は使う価値がないと判断してしまったユーティリティーファーストCSSですが、ここまで広く普及したのは正当な理由からに違いない、現状の自身の課題も解決するかもしれないと思い、しっかり向き合うことにしました。

## 公式サイトで思想を読み解く

まずは公式サイトをしっかり読んで、どのような思想でそうなっているのか、自分が考えているモヤモヤへの回答があるのか確認してみました。

https://tailwindcss.com/docs/utility-first

### Core Concepts - コアコンセプト

> Now I know what you’re thinking, “this is an atrocity, what a horrible mess!” and you’re right, it’s kind of ugly. In fact it’s just about impossible to think this is a good idea the first time you see it — you have to actually try it.

日本語機械翻訳

> 「これは残虐行為だ、なんとひどい混乱だ！」とあなたが考えていることはわかります。おっしゃるとおり、それはちょっと醜いです。実際、これを初めて見たときにこれが良いアイデアだと考えるのはほぼ不可能であり、実際に試してみなければなりません。

確かに良いアイデアだと思っていませんが、そこまで言うなら、実際に試してみましょう。
ただ、公式サイトで記載されているようなコードはとても作業効率や保守性に問題があるように感じます。
どのようにすれば良いのでしょうか？

これに対する回答は「Maintainability concerns」の中で記載されています。

### Maintainability concerns - 保守性に関する懸念

「Maintainability concerns」で書かれていることは、
「ReactとかVueみたいにコンポーネント指向のコーディング手法が増えたので、コンポーネントに分割したらいいやん、あとはマルチカーソルとか使ったら？」
というような内容でした。

マルチカーソルで解決っていうのは正直どうかと思いますが、たしかにReactやVueを用いてなるべくDRY原則にしたがった構成にすれば長いclass名でも保守できるかもしれません。

## ここでAstroの出番ですね

ReactやVueを用いた案件であれば、前述のとおり、コンポーネントをしっかり分割すれば大丈夫でしょう。
ただ、コーディング業務はモダンな案件だけでなく、昔ながらで単純なHTMLのお仕事も多くあります。

ここでTailwindCSSと同様に最近急速に普及しているAstroの出番です。
JavaScriptに依存しないピュアなHTML案件をReactやVueのようなモダンな書き方、コンポーネント指向で制作できそうです。

AstroとTailwindCSSの組み合わせで通常のHTML制作業務を進めてみました。

https://astro.build/

## Astroでの課題

Astroを使う中でいくつか課題が出てきました。

### 相対パスでのビルド

単純なLP（ランディングページ）制作などのコーディング案件では、どこに設置しても動くシンプルな動作が求められることが多いです。
すなわち、何らかのサブディレクトリ配下に設置しても動作するように各種リンクはドキュメントからの相対パスにしなければなりません。

```html
<!-- ▼ こういうのは避けたい -->
<a href=”http://example.com/sample_dir/”>リンク</a>
<a href=”//example.com/sample_dir/”>リンク</a>
<a href=”/sample_dir/”>リンク</a>

<!-- ▼ こういうことにしたい -->
<a href=”./”>リンク</a>
```

Astroはデフォルトではドキュメントからの相対パスにはなりませんが、この問題を解決してくれている人がいました。素晴らしい。

https://www.npmjs.com/package/astro-relative-links

こちらを導入することによってCSSファイル、JavaScriptファイル、画像ファイル、各種リンクのパス指定問題が解決しました。

### やっぱりclassまみれで、たまらんな

いくらコンポーネントに分割したからといって、class属性の値が大量に並び、見通しが悪く編集しにくい問題は残ります。
そこで、下記の対応策をとりました。

#### 無理に疑似要素使わず空div等で代用

TailwindCSSでも疑似要素の指定は可能ですが、無理に要素を削るメリットもないため、入れ子のdiv構造や空divを用いることを許容する方針としました。
これにより単一の要素にとんでもない量のclassがつけられるのを分割し、少し可読性を上げることが出来ました。

#### 無理にデザイン要素をCSSで実現しない

簡単なアイコンや矩形構造をCSSを用いて実装するのは楽しいですが、単純化できる場合、CSSを用いる特別な理由がない場合はSVGなどを積極的に利用する方針としました。

#### 複雑なスタイルは独自コンポーネントで実装

複数のステップがあるような複雑なグラーデーションなどは独自コンポーネントクラスを定義する方針としました。

#### Simpleな構造を意識する

慣れや感覚を掴むまで時間がかかりましたが下記のあたりを考慮するとコード全体の単純化が図れました。
　
* TailwindCSSの基本実装方針にならって、root要素以外はrem指定で統一する（borderなど一部は除く）
* 指定はモバイルファースト、breakpointは増やさず、プレフィックスなしと `md:` プレフィクスに留める。
* なるべくスマホとPC共通のクラスで実装できるよう意識して `md:` プレフィックスも必要最小限とする。

#### Prettierでクラス名ソート

クラス名の並びを自動ソートしたいので prettier-plugin-tailwindcss を導入しました。

https://github.com/tailwindlabs/prettier-plugin-tailwindcss

#### ときにはTailwindCSSを部分的に諦める

例えば、チェックボックスの選択状態に応じてデザインを変更したい場合、 `:checked` に隣接セレクタを設定したい場合があります。
しかし、これがTailwindCSSでは実現が難しい。
TailwindCSSを使うことにより、極端に複雑性が増す箇所においては、諦めてAstro側のscopedなCSSとして定義しました。


## 脱セマンティックCSSクラスの弊害

TailwindCSSを本格的に取り入れると、HTML上に要素を特定するセマンティックなclassがなくなり、JavaScriptからDOMを制御するときにどういう目印で掴むかが悩ましくなります。
一意であればidで良いのですが。

現時点での解決策としてはdata属性で任意の印をつけるか、WAI-ARIAな目印をつけるようにしています。

https://developer.mozilla.org/ja/docs/Learn/Accessibility/WAI-ARIA_basics

## 現時点での所感など

### 初期工数は増える

まだ、慣れきっていない部分もありますが、単純なLPを作成する場合の作業工数はこれまでのセマンティックなクラス名を用いながらSassなどでコーディングしてく手法とくらべ、3倍以上かかっているなという感覚です。おそらく慣れていくと2倍くらい。

運用が発生しない期間限定のLPをサクッとつくるには、これまで通りの手法のほうが適しているかもしれません。

### しかし、取り組むべき

プロジェクトメンバーの手に馴染んだ技術や手法は何か、技術選定に影響する変数（要因）はなにかによってかわりますが、基本的には今後もユーティリティーファーストCSSは積極的に取り入れていこうと考えています。
その理由について、いくつかの観点で具体的に挙げていきます。

#### 品質管理がしやすい

これまでのHTML・CSSコーディングは経験に則ったセンスのようなものが必要でした。
どこまでカスケードさせるか、逆にカスケードさせないか。
クラス名のネーミングセンス、CSS設計技法をどのレベルまで導入するか、方針に一貫性があるかなどは一朝一夕では身につきません。

外部パートナーと協業して進める場合は、ある程度は誰が書いても同様のコードになる技術選定が管理しやすいでしょう。

#### 歴史を重ねても破綻しにくい

CSSの一番の問題点は、歴史を重ね、たくさんの人間を経由するとメンテナンスが難しい秘伝のタレ状態になりやすいことです。
それを回避するには、日々の運用負荷が上がる厳しく分厚いルールを導入するしかありませんでした。

TailwindCSSの公式サイトでもメリットとして謳っていますが、ユーティリティーファーストはCSSがどんどんと成長していき手がつけられなくなるという状態を回避できます。

#### 自動化を見据える

クラス名を考えずに単純にFigmaなどのデザインツールから値を拾って、REMを算出し、ユーティリティークラスを指定していく。

この作業をコーディングの最中に繰り返し作業をしていると、とても単調でまったく頭を使っていないことに気づきます。

従来のコーディングでは、クラス名を検討するときにどのような名前が汎用的で見た人がわかりやすく、仕様変更に強いか、どの粒度で分割するかなどを考えて作業を行いますが、ユーティリティファーストの場合はそれらの思考がまったく不要になります。

TailwindCSSの公式サイトの中でも謳っていた利点と重なります。

クラス名を考えなくても良くなるというメリットは初め聞いたときには、そんなことメリットにならないだろうと考えていましたが、たしかに作業をすすめる中で思考すべきことが多く削減できました。

デザインデータからユーティリティークラスを算出してコードに落としていく。
この作業は話題の生成AIなどのような高度な技術ではなく、もっと低レベルなレイヤーで自動化と相性が良いだろうという気づきがあります。


### デザイン設計からTailwindCSSを意識する必要あり

やってみてわかったことは、TailwindCSSのメリットを教授するにはTailwindCSSの設計思想によりそってデザイン設計フェーズから考える必要があるということです。

そうしないと多くの箇所でTailwindCSSがデフォルトで用意したクラス郡ではなく、 `mt-[4.107rem]` のような値を指定することになります。

例えば、[Typography Font Size](https://tailwindcss.com/docs/font-size)で定義されている係数でフォントサイズ定義ができて、figmaでこのテキストは text-lg 相当ですとかがすぐにわかるようになればコーディング速度の改善が見込めそうです。

Figma Tokensもうまく使いこなせば、TailwindCSSと抜群に相性が良さそうです。

## これからの未来予想

前述したとおり、クラス名を考えなくて済むコーディングというのは、おそらくFigmaでうまくデザインデータだけ作成すれば、HTMLコーディングという業務はほとんど無くなるのではないかと考えています。
実際に、既存サイトからFigmaデザインにするプラグイン、FigmaデザインからReactのコードを生成するプラグインなども存在します。

今、HTML・CSSコーダーという仕事が消失する転換期だろうと考えています。

ただ、悲観すべきことではありません。
どのような仕組みにすれば、生成AIと相性が良いだろうか、自動化できるだろうかという観点、サーバーサイドとの連携部分も考えれるフロントエンドエンジニアという立場の人はこれからも重宝されるでしょう。

WebデザイナーにとってはユーティリティーファーストCSSとの接続方法だけ習得すれば、コーダーへの依頼なしで一瞬で動くコードを取得する未来がきっとやってくるでしょう。

便利なものは上手に取り入れて、よりよい価値を高速に提供できるよう、これからも模索していきたいと思います。