株式会社TENTIALのエンジニア、yossyです。
現在のチームには4ヶ月前にジョインした新人です。
前職・全前職では受託や自社SaasでいろんなWEBサービスをいろんな言語やライブラリを使って開発していました。そんな経験の中で、「イマドキのWEBサービスにTailwind CSSは欠かせない!」と確信していたのですが…
――なんと現職のプロダクトはnon Tailwindで動いているではありませんか!!
この状況にテコ入れすべく、Tailwind CSSの良さをチームのメンバーないし社会全体に布教しようと思いこの記事を書きました。
プロダクトの概要
- 数ヶ月前にNuxt2→3に移行したフルスクラッチECサイト
- 共通クラス、コンポーネント、.vueファイルごとに特にルールなくスタイルが実装されている
- プロジェクト内にscssファイルが250個、500個近いvueファイルにはstyleブロックがあったりなかったり…
SCSSと比較したTailwindの利点って?
エンジニアの皆さんには言葉よりもコードを見てもらったほうが早いですね。
以下は同じ振る舞いをするSCSSとTailwindのコードです。
Vue + SCSS :
<template>
<div class="container">
<div class="main-section">
<div class="inner-content">
Something
</div>
</div>
</div>
</template>
<style lang="scss">
.container {
width: 100%;
max-width: 768px;
margin: 0 auto;
.main-section {
padding: 32px 16px;
background-color: #f0f0f0;
.inner-content {
font-size: 1rem;
line-height: 1.5rem;
color: $primary-color;
@include sp {
font-size: 0.875rem;
line-height: 1.25rem;
}
}
}
}
</style>
Vue + Tailwind :
<template>
<div class="w-full max-w-3xl mx-auto">
<div class="py-8 px-4 bg-gray-200">
<div class="text-sm sm:text-base text-primary">
Something
</div>
</div>
</div>
</template>
ホラ。簡潔でしょ?
SCSSはプリミティブなクラスを毎回定義して、それに対応したスタイルをstyleブロックか別のglobal or importしたscssに記述する必要があり、コード量も参照箇所も増えますね。
SCSSで汎用的な共通クラスを作って使いまわしてしまうと、共通クラスを編集したときにどこに影響するか分からず、意図しないスタイル崩れが起きたりします。
一方、Tailwindはクラスに直接「ユーティリティクラス」を記述していきます。最初はインラインスタイルみたいで抵抗あるかもしれませんが、templateとスタイルが一元管理されることでマークアップが格段に容易になります(好みが分かれる部分なので回避策については後述します)。
基本的なスタイルはユーティリティクラスで提供されていて、自分でカスタムしたクラスも追加で定義することもできます。Tailwindはレスポンシブやアニメーションにも柔軟に対応しています。
とにかく、コード量が大幅に減らせるうえに、いちいちクラス名をBEM記法などで命名する手間が減り、文書構造とスタイルのコードが散り散りにならないようにできるんですね。
標準ユーティリティクラスとカスタムクラス
Tailwindを導入する最大のメリットとして、セマンティックなトークンやテーマを簡単に導入できることが挙げられます。つまりデザインシステムが簡単に導入できるということです。
例)
セマンティックな色 | プリミティブな色 | HEXコード |
---|---|---|
primary |
sky-700 |
#0369a1 |
warning |
yellow-300 |
#fde047 |
disabled |
gray-600 |
#4b5563 |
色はTailwindがデフォルトのクラス(プリミティブカラー)を用意してくれてあるものを基本的に使うようにして、プロジェクト用に独自のカスタムトークン(セマンティックカラー)を定義することでサイト内のトンマナを維持することができます。
tailwind.config.ts
を細かく設定することで、プロジェクトで統一性のある共通言語を使って開発できるようになります。
SCSSでも変数を使った色の定義はできますが、補完できなかったりデフォルトのカラーパレットがないので、実装者が適宜追加してカオスになりやすいです。
開発速度は上がるのか?
実装工数とレビュー工数を減らせる
エンジニアにとって、スタイルの実装と修正は大きな負担です。
デザイン通りに実装する人、既存の実装に合わせて実装する人、自己流で1から実装する人。
Tailwind Documentを一通り読めば、ほとんどのスタイル実装は網羅できるはずです。
Googleで「HTML 横並び 中央揃え」と検索してCSSをコピペすることもなくなります(慣れるまでChatGPTにも頼りすぎないようにしたいですね)。
ブラインド実装できるまではTailwind Documentとにらめっこする日々が続くかもしれませんが、ある程度慣れた頃にはTailwindだけでなく文書構造とCSSもマスターできることでしょう。
そうなれば単純にコードの記述量が減る上にマークアップ実装やデザインのパターンも理解でき、工数も減っていきます。
また、templateとスタイルが分離しないのでコードレビュー時に修正箇所が分散しない、修正漏れにコードレビューで気づけない事態を回避できるのも利点です。
VSCode拡張機能がツヨい
Tailwindにすると開発体験が非常に良くなります。
VSCodeを使っている場合、「Tailwind CSS IntelliSense」という拡張機能を導入することで強力なサジェストを手に入れられます。
中央揃えしたいけど、text-center
だっけ?justify-center
だっけ?items-center
だっけ?みたいなとき、center
と入力すると該当するすべてのユーティリティクラス(カスタムも含む)が表示されるので、その中から選ぶことができます。
サジェストにフォーカスすると実際に適用される生のCSSも表示されます。自作のユーティリティクラスではできない開発体験ですよね!
バラバラになりやすい単位の統一
人によってキリのいい数字は違うものです。1の位は0か5にしたかったり、rem派がいたりpx派がいたり。
いざデザインシステムでルール化しよう!となっても、SCSSでは実装者のルールの理解度によっては
余白や幅の単位は基本的に4の倍数(4px = 1)で定義されます。中途半端な数字では実装しにくくなるので、必然的に4の倍数に揃っていくというわけです。
padding: 1rem 32px; // => py-4 px-8
max-width: 768px; // => max-w-3xl
どうしても小数点やキリの悪い数字を使いたければ、こんなこともできます。
padding: 3.14159px; // => p-[3.14159px]
max-width: 777px; // => max-w-[777px]
実際どうなの?という問いに対する答え
移行しやすいのか?
結論:しやすくは…ない
導入初期の課題
Tailwindを導入するとなった場合、既存スタイルをTailwindクラスに置き換える作業が必要になりますが、大抵の場合めっちゃ大変です。
従来のプロジェクトにデザインシステムが特になかった場合(乱雑に色の変数が定義されて使われていない、各所でHEXコードを多用していない、余白や幅の大きさにルールがないような状態)、導入時にTailwindの基本方針や開発ルールの周知が必要です。
段階的に導入する場合、誰かが主導して推進しないと、SCSSで実装する人・Tailwindで実装する人が自由に分かれたり、既存コードの改修時にどちらで記述するか困ったりするかもしれません。
モバイルファーストへの転換
Tailwind標準のブレークポイントを使用すると、min-width指定しかなく、モバイルファーストの設計を前提としています。
もし移行前の実装がPCファーストで、 @include sp
などのミックスインを使っているような場合だと、置き換えに手間取るかもしれません。
とはいえ令和のECたるもの、スマホファーストのスタイル実装ではありたい…
移行による混乱を減らすアプローチ
実装者目線からだと、「template内のclass名が長くなって気持ち悪い」とか「セマンティックなクラス名を使いたい」という声が上がるかもしれません。
そんなときは以下のように@apply
を使って従来の記法でスタイルのみをTailwindに置き換えることも可能です。
Vue + SCSS + Tailwind(@apply
) :
<template>
<div class="container">
<div class="main-section">
<div class="inner-content">
Something
</div>
</div>
</div>
</template>
<style lang="scss">
.container {
@apply w-full;
.main-section {
@apply py-8 px-4 bg-gray-200;
.inner-content {
@apply text-base text-primary;
@include sp {
@apply text-sm;
}
}
}
}
</style>
classすべてをTailwindクラスにしてしまうと、開発者ツールからの逆引きがしにくいという意見もあります。その場合、上記のようにセマンティックなクラス名を使って今まで通りのスタイルシートにTailwindクラスを記述することも可能です。
Vue(v-bind:class) + Tailwind :
<template>
<div :class="containerClass">
<div :class="mainSectionClass">
<div :class="innerContentClass">
Something
</div>
</div>
</div>
</template>
<script setup>
const containerClass = ['w-full']
const mainSectionClass = ['py-8', 'px-4', 'bg-gray-200']
const innerContentClass = ['text-sm', 'sm:text-base', 'text-primary']
</script>
v-bindを使ってこんな書き方も。処理とレイアウトがscript内で混同してしまうので賛否両論ありそう。
やめるとなったら剥がしやすいのか?
結論:剥がしやすくは…ない(実装次第で剥がしやすくはできる)
一昔前のUIライブラリ、CSSライブラリを使ってサービスを作って、数年で古臭い印象になってしまったり、よりカスタムしたデザインを再現できず、ライブラリの移行や廃止に苦労したオッサ…熟練エンジニアの方も多いことでしょう。僕もその一人です。
実装と密結合にならないライブラリだということ
TailwindはVuetifyやBootstrapのようなUIライブラリと違い、デザインプリセットのようなものはありません。そのため、Tailwindだけを導入して管理画面をチョッ早で作る、みたいなことはできません。TailwindのデザインシステムのFigmaも公開されていますが、Atomコンポーネントの実装例などはありませんね。
つまりTailwindがデザイン設計自体やコンポーネント実装に干渉しにくいので、実装面の不満が出ない限り「やっぱり剥がそうか」という議論にもならないかもしれません。
実際、Tailwindを剥がすようなタイミングは大きなリプレースと同時だったりすると思います。そんなタイミングならデザインも変わるでしょうし、TailwindだったとしてもSCSSだったとしても、0からマークアップしたほうが早いのではないでしょうか。
一応いつでも戻しやすいように、Tailwindクラスを他クラスと分けて管理する方法を紹介されている記事もあったのでご参考までに。
まとめ
既存システムにTailwindを導入する場合、全スタイルを一斉に書き換えるとなると大きな工数と混乱を生む可能性は確かにあります。
少しずつSCSSをTailwindに置き換えていくことによって以下のようなサイクルが生まれ、少しずつ明るい未来が見えてくるのではないかと僕は思います。
-
tailwind.config.ts
の作成を契機にプロジェクトのデザインシステムについてメンバーが考え直せる - どこに影響するかわからない共通スタイルをリファクタできる
- コンポーネント設計を見つめ直せる
- 徐々に保守性の低いCSSコードが削減されていく
- スタイルの新規実装・保守コストが減っていく
要約すると、「キミ、幸せになりたいよね?最初は怪しいかもだけど、Tailwindっていうのがあってね…」というお話でした(ぜんぜん違う)