12周年記念パーティ開催! 2024/5/10(金) 19:00

Tailwind CSSで始めるユーティリティファーストCSS 第1回 Tailwind CSSの特徴と使い方

CSSフレームワークのTailwind CSSを紹介します。まずはどのような特徴を持っているのか、通常のHTMLとCSSで記述した場合との違いはどのような点があるかを見てみましょう。

発行

著者 中野 祐人 Jamstackエンジニア
Tailwind CSSで始めるユーティリティファーストCSS シリーズの記事一覧

基本的な仕様は変わっていませんが、バージョン3では設定ファイルの書き方やPurgeCSSの利用がなくなるという変更があります。詳しくは「Tailwind CSSバージョン3のポイント」記事も併せて参照ください。(2022年2月現在)

Tailwind CSSの特徴

本シリーズでは数回に分けて、CSSフレームワークのTailwind CSSの解説をしていきます。1回目の今回は、その特徴、解決できること、注意点を紹介します。

Tailwind CSSの主な特徴は次の3つが挙げられます。

UIコンポーネントを持たないCSSフレームワーク

もともと独自デザインのUIコンポーネントを持っているBulmaBootstrapのようなCSSフレームワークは、たとえばbuttonというクラス名を指定すれば即座にボタンを実装できるという手軽さがあります。しかし、「独自のデザインをあらかじめ持つ」という特徴は、自分たちのデザインを実装する場合はかえってコストがかかります。なぜなら、一からデザインする場合には必要のない、既存のデザインを打ち消すという作業が発生するからです。

それに対してTailwind CSSは独自のUIコンポーネントを持ちません。CSSのプロパティ単位でクラスを持つことにより、比較的デザイン上の制約を受けないように設計されています。

ユーティリティファーストのCSSフレームワーク

ユーティリティファーストとは「使いやすさを優先しよう」という考え方です。ユーティリティファーストの考え方によって作られるCSSは「ユーティリティクラス」と呼ばれます。

そのクラス名は、.button.cardのようにクラスが指定される要素の内容と密接に紐付きません。.text-xs.text-gray-600のように「このクラスを指定するとどうなるのか」というCSSのプロパティ名をもとにした命名がされています。

これにより、CSSがコンテンツに依存せず、再利用性が高くなっています。

CSSの知識は必須

Bulmaなど、CSSフレームワークによっては、フレームワークの知識がCSSの知識から独立しており、フレームワーク独自の知識のみである程度使えるものがあります。しかし、Tailwind CSSはあくまでもプロパティを元にしたユーティリティクラスが用意されているだけなので、CSSの知識は必須となるでしょう。

これらの特徴について、次の節では実際のコードを交えながらみていきましょう。

Tailwind CSSの設計思想

CodeGridでは、Tailwind CSSはどのような設計思想なのだろうか? ということを座談会形式で話し合ったシリーズがあります。こちらも参考にしてみてください。

コードから見るその特徴

例として、次のような見た目のプロフィール表示コンポーネントを作ります。

このプロフィールコンポーネントをTailwind CSSを使って作成した場合のHTMLは、次のようになります。書き方の詳細は次回以降で解説しますので、まずはTailwind CSSがどのようなコードになるのかの雰囲気を掴んでみてください。

<li class="flex w-64 bg-gray-200 p-2 rounded-md items-center">
  <img
    src="https://pxgrid2.imgix.net/profile/yuto.jpg?fm=webp&w=160&h=160"
    class="w-16"
  />
  <div class="ml-4">
    <p>
      中野 祐人
    </p>
    <p class="text-xs mt-1 text-gray-600">
      Jamstackエンジニア
    </p>
  </div>
</li>

各要素にたくさんのクラスが指定されています。たとえば、初めの<li>タグに指定されているクラスごとのスタイルは以下のようになっています。

.flex {
  display: flex;
}
.w-64 {
  width: 16rem;
}
.bg-gray-200 {
  background-color: #edf2f7;
}
.p-2 {
  padding: 0.5rem;
}
.rounded-md {
  border-radius: 0.375rem;
}
.items-center {
  align-items: center;
}

それぞれのクラスが一つずつプロパティを持っており、その組み合わせによってプロフィールコンポーネントの外枠を作っています。このように複数のクラスの集合として一つのコンポーネントを形作るのが「UIコンポーネントを持たない」ということです。

また、liタグに指定されているbg-gray-200というクラス一つをとっても、「背景色を灰色にしたい」という意味以上のものは含まれていません。それにより、ほかに背景色を灰色にしたい部分が出てきたときには、そのまま再利用することができるのです。

このようにユーティリティクラスのクラス名は「これらが何を作っているのか?」というコンテンツの情報を一切含んでいません。これが上で説明した「ユーティリティファースト」のCSSであるということです。

style属性にスタイルを書く場合との違いについて

Tailwind CSSでHTMLにスタイルを当てていく作業は、HTMLに直接書いていくという面でHTMLのStyle属性にスタイルを書いていくのと似ています。しかし、実際style属性にスタイルを書く方法だけを使ってWebサイトを作るのは厳しいものがあります。Tailwind CSSでは以下の点でそれを解消しています。

必要な文字数が少ない

style属性に必要なスタイルをすべて書いていくと、その文字数の多さからHTMLの可読性が大きく損なわれてしまいますが、Tailwind CSSでは上のコード例でわかるように、かなり省略された命名がなされています。

メディアクエリや、:hoverなどの擬似クラスも使える

デフォルトの設定では構成ファイルを減らすためにオフになっていますが、Tailwind CSSではクラス名に接頭辞をつけることでメディアクエリや擬似クラスを使うことができます。設定すると、たとえばbg-gray-100というクラスをホバー時に当てたければhover:bg-gray-100という記述を追加するのみで実現することが可能です。

Tailwind CSSはこのbg-gray-200のようなユーティリティクラスの集まりであり、ほかのクラスすべてに同じことが言えます。この特徴により、いろいろなデザインに対応できる柔軟性とCSSの再利用性を高めているのです。

命名規則について

Tailwind CSSのクラス名は一定の規則性を持って命名されています。HTMLに見慣れないクラス名がいくつも宣言されている形に抵抗を覚えたかもしれませんが、命名規則を知っておくだけでずいぶんと読みやすくなります。ここでは学習の助けとなるよう、Tailwind CSSの構文の規則性に触れてみましょう。

以下は先ほどのプロフィールコンポーネントの例で指定されているクラスの中からいくつか抜き出したものです。

  • flex
  • bg-gray-200
  • text-xs
  • rounded-md
  • p-2
  • w-64
  • mt-1
  • ml-4

初めに見て取れる規則性としては、単語と単語のセパレーターとして-が用いられる、ということです。この例ではflex以外すべてのクラスが-で区切られ命名されています。

またbg-gray-200200p-2w-64264などの末尾にくる数字や、rounded-mdtext-xsmdxsは「複数ある数値の一つの段階」を指定しています。mdはmediumの略称です。

以上の知識を持っていれば、bg-gray-200は「backgroundgray200という色の段階を示している」ということ、rounded-mdは「rounded、すなわち何かを丸くするプロパティで、その段階はmd、要するに真ん中ぐらいが指定されている」ということがわかるでしょう。

そのほかp-2ml-4w-64なども一見難しそうですが、ppaddingmmarginwwidthhheightの略です。頭文字ですので、一度覚えてしまえば難しくはないでしょう。加えてtopbottomrightleftも同じく頭文字で表されます。mt-1margin-topml-4margin-leftを指定しているというわけです。また、flexdisplay:flexというプロパティと値を持ちます。

このように命名は基本的にCSSのプロパティ名が想像できるようになっているので、CSSをある程度書ける人ならすぐに理解できるのではないでしょうか。なお、クラスはすべて公式ドキュメントに掲載されているので、覚えるまでは見ながら使用するのが良いでしょう。

これまでのやり方とTailwind CSSとの比較

ここでは筆者が考える「Tailwind CSSを使うと解決できること」を、BEMを意識した「これまでのやり方」と比べながら紹介します。

先に挙げたプロフィールの例を「これまでのやり方」で表した場合は、以下のようになります。

<li class="profile">
  <img
    src="https://pxgrid2.imgix.net/profile/yuto.jpg?fm=webp&w=160&h=160"
    class="profile__image"
  />
  <div class="profile__text">
    <p>
      中野 祐人
    </p>
    <p class="profile__detail">
      Jamstackエンジニア
    </p>
  </div>
</div>
</li>

BEMを意識した命名にし、profileというBlockにimage、text、detailというElementが属しているという構成です。

.profile {
  background-color: #edf2f7;
  width: 16rem;
  padding: 0.5rem;
  border-radius: 0.375rem;
  display: flex;
  align-items: center;
}
.profile__image {
  width: 4rem;
}
.profile__text {
  margin-left: 1rem;
}
.profile__detail {
  font-size: 0.75rem;
  margin-top: 0.25rem;
  color: #718096;
}

CSSはTailwind CSSを参考に、同じ見た目になるようにしています。

この「これまでのやり方」と、今まで紹介してきた「Tailwind CSSを使ったやり方」を比較してみましょう。

記述量の違い

まず、コードを書くときの違いを考えてみます。単純に文字数の比較をすると、Tailwind CSSで作った場合はスペースを含めて233文字、これまでのやり方で作った場合はHTMLが212文字+CSSが539文字の合計751文字でした。

これは単純計算でTailwind CSSを使って実装した場合、文字数が1/3以上減少するということです。実際にはこれがすべてではありませんが、それでもこれだけ記述量が減るということは、その分実装にかかる時間は少なくなると言えるでしょう。

ただし、場合によってはTailwind CSSのほうが記述量が増える場合もありますので、それに関しては後述します。

実装が進んでいったときの対応

次に、実装が進んでいったときに起こりそうな問題を考えてみましょう。

もし同じサイト内で、上で挙げたプロフィールコンポーネントのデザインと酷似した、以下のような記事を表示するコンポーネントを作成することになったとします。

これは先ほどのプロフィールコンポーネントの名前部分に記事タイトル、職種部分に日付、画像部分がサムネイルになっただけで、HTML、CSSのマークアップ上は何の変更もないものになります。

できるだけ既存のプロフィールコンポーネントのマークアップを活かしたいと考えますが、すでにprofileというブロックの名前にしてしまっている以上、プロフィールブロック以外に、このCSSをそのまま利用するわけにはいきません。ですのでこれをこれまでのやり方で実装する場合、以下の2つのアプローチが考えられます。

  • 既存のprofileというクラス名を抽象化し、listItemなどの名前に変更。プロフィールコンポーネントと記事コンポーネントのどちらでも使用する
  • profileブロックのクラスを複製し、中身がまったく同じだがクラス名だけ違うarticleのようなブロックを作成する

しかし、この2つのアプローチにはいくつか問題があります。

これまでのやり方を取った場合のジレンマ

1つ目のアプローチを取ると、今後どちらかのコンポーネントのみに変更があった場合に、結局複製して別のコンポーネントに分けなければならない可能性があります。また、抽象的な名前は他のコンポーネントと似る可能性が高く、抽象的な名前はすぐに枯渇するので、だんだん名前を絞り出す作業が生まれるようになります。

2つ目のアプローチを取ると、DRY(don't repeat yourself)の原則にのっとらないコードが生まれてしまいます。中身が同じという重複したコンポーネントは、CSSファイルを肥大化させ、複雑にします。

Tailwind CSSで解決する

そもそも、これらの問題は「基本的にCSSはクラス名を決めてからでないと使用できない」というところに根本的な原因があります。CSSは、その性質上、全体のコードがどのようになるかあまり見えない状態から命名する必要があります。それが最適な粒度でコンポーネント化することを困難にし、その結果として起こる「初期段階での抽象化」がこれらの問題を生みます。

Tailwind CSSを使うとこのコンポーネントは、以下のように表現されます。

<li class="flex w-64 bg-gray-200 p-2 rounded-md items-center">
  <img
    src="https://cdn.netlify.com/4272892f6c393b1fdabd7c8bfaebd6bec3c04e18/ee990/img/logos/jamstack.svg"
    class="w-16"
  />
  <div class="ml-4">
    <p>
      Jamstackとは?
    </p>
    <p class="text-xs mt-1 text-gray-600">
      2020/07/11
    </p>
  </div>
</li>

これは画像、テキストのみを変更し、クラスはプロフィールコンポーネントとまったく同じものになります。

抽象的なクラス名を捻り出す必要もなければ、新しいクラスを作成する必要もありません。それぞれのコンポーネントで今後新たな変更があった場合は、それぞれのコンポーネントで調整するだけです。

そして開発が進み、繰り返しのパターンが明確になった任意のタイミングで抽象化するという選択を取ることもできます。このようにコンポーネント化を選択肢として後回しにできるのです。

このようにTailwind CSSを使えば上記のようなCSSのジレンマを回避し、のちの仕様やデザインの変更に柔軟に対応しながら、論理的なコードを容易に書くことができます。

コンポーネント化について

今回は触れていませんが、Tailwind CSSには既存のユーティリティクラスをまとめ、独自のクラスを作成できる機能があります。作者であるAdam Wathan氏はあくまでも「ユーティリティファースト」であり、CSSでコンポーネントを作成することがよくないわけではないと述べています。詳細は次回以降に解説する予定です。

コードに一貫性を持たせられる

Tailwind CSSは、フォントサイズやmargin paddingの数値など、CSSで任意の数値を指定する部分の値をいくつかに絞ることで、コードに一貫性を持たせることができるという利点もあります。

CSSの運用が長くなればなるほど、また、CSSを書く人が増えれば増えるほど、見た目上、ほとんど差がないにもかかわらず、アプローチ方法や単位が違うコードが複数生まれる可能性が出てきます。これらのコードが多くなると、CSSファイルは複雑性を増し、どんどん肥大化してしまいます。

Tailwind CSSを使うと最初から全員が「既存のコードを使う」というベースの元で作業できるので、CSSの無駄な肥大化や複雑性を排除できます。

Tailwind CSSが非効率になるケース

逆にTailwind CSSを使う際に注意しなければならない部分もあります。それは、CSS以外でコンポーネント化できない環境ではかえって非効率になる場合もあるということです。

Tailwind CSSが最近盛り上がりを見せている背景には、JSフレームワークやテンプレートエンジンを用いた開発が主流になってきていることが大きく関わっていると筆者は考えます。フレームワークやテンプレートエンジンが持っている「コンポーネントのカプセル化機能」によって、CSSがスコープを意識する必要性が下がったのです。

そのようなスコープをCSSで意識しなくても良い環境ではTailwind CSSを使用しても問題ありません。しかし、もし通常のHTML、CSSのような環境でTailwind CSSを使用すると起こる問題として「この要素がどういう意味を持つのか」といった情報がコードのどこにも存在しなくなることが挙げられます。

たとえば、上で紹介したプロフィールのコンポーネントをたくさんの人の分並べる必要があったとしましょう。

コンポーネント化できない環境でTailwind CSSを使用したコードは、以下のようになります。

<ul>
  <li
    class="border-gray-200  flex w-64 bg-gray-200 p-2 rounded-md items-center"
  >
    <img
      src="https://pxgrid2.imgix.net/profile/yuto.jpg?fm=webp&w=160&h=160"
      class="w-16"
    />
    <div class="ml-4">
      <p>
        中野 祐人
      </p>
      <p class="text-xs mt-1 text-gray-600">
        Jamstackエンジニア
      </p>
    </div>
  </li>
  <li
    class="border-gray-200  flex w-64 bg-gray-200 p-2 rounded-md items-center"
  >
    <img
      src="https://pxgrid2.imgix.net/profile/kyosuke.jpg?fm=webp&w=160&h=160"
      class="w-16"
    />
    <div class="ml-4">
      <p>
        中村 享介
      </p>
      <p class="text-xs mt-1 text-gray-600">
        Jamstackエンジニア
      </p>
    </div>
  </li>
  <li
    class="border-gray-200  flex w-64 bg-gray-200 p-2 rounded-md items-center"
  >
    <img
      src="https://pxgrid2.imgix.net/profile/takazudo.jpg?fm=webp&w=160&h=160"
      class="w-16"
    />
    <div class="ml-4">
      <p>
        高津戸 壮
      </p>
      <p class="text-xs mt-1 text-gray-600">
        フロントエンド・エンジニア
      </p>
    </div>
  </li>
</ul>

コードが長くなること自体はある程度仕方がないですが、これらのコードが何を形作っているのか? どこからどこまでにまとまりがあるのか? といった情報が、このコードには、まったく含まれていません。それにより、このコードで何が行われているのか理解するのに時間がかかってしまいます。

一方、BEMを意識した「これまでのやり方」で書いたコードは以下のようになります。

<ul>
  <li class="profile">
    <img
      src="https://pxgrid2.imgix.net/profile/yuto.jpg?fm=webp&w=160&h=160"
      class="profile__image"
    />
    <div class="profile__text">
      <p>
        中野 祐人
      </p>
      <p class="profile__detail">
        Jamstackエンジニア
      </p>
    </div>
  </li>
  <li class="profile">
    <img
      src="https://pxgrid2.imgix.net/profile/kyosuke.jpg?fm=webp&w=160&h=160"
      class="profile__image"
    />
    <div class="profile__text">
      <p>中村 享介</p>
      <p class="profile__detail">
        Jamstackエンジニア
      </p>
    </div>
  </li>
  <li class="profile">
    <img
      src="https://pxgrid2.imgix.net/profile/takazudo.jpeg?fm=webp&w=160&h=160"
      class="profile__image"
    />
    <div class="profile__text">
      <p>
        高津戸 壮
      </p>
      <p class="profile__detail">
        フロントエンド・エンジニア
      </p>
    </div>
  </li>
</ul>

こちらではprofileというコンポーネントが複数繰り返されていることが、ひと目見てわかります。これはクラス名に「この要素がどういった意味・役割を持つのか」という情報を持たせているからです。

このようにCSS以外でコンポーネントを管理する仕組みがない状態でTailwind CSSを使った場合、そのコードが何を作っているのか? という情報がコードのどこにも存在しなくなってしまう場合があります。

このような環境でTailwind CSSを使用すると、かえって運用上のコストが増える可能性があるので注意が必要です。

まとめ

今回はTailwind CSSの特徴と、コード例を通して使うべき場合とそうでない場合を見てきました。

その技術ができた背景にある考え方や特徴という知識があれば、技術選定をより正確に行うことができます。また、考え方に共感できれば、学習のモチベーションも上がるでしょう。

Tailwind CSSに興味を持った方はぜひ一度使ってみることをお薦めします。

次回からは詳しい使い方の解説をしていきたいと思います。