🦔

TailwindCSS 3.4のアップデート内容がすごい!

2023/12/29に公開

はじめに

アイディオットDX開発部の岩澤です。
先日、TailwindCSS v3.4がリリースされました。

個人的に今回のリリース内容が激アツだったのですが、まだ内容を追えてない方や、今回の変更の何が嬉しいのかわからない方向けに、サクッと解説していきたいと思います。

h-dvh

h-dvhというclassが指定できるようになりました。
たとえば以下のように、スクロールするとアドレスバーが縮小する場合など、動的にかわるビューポートの高さにあわせて、要素の高さも動的にビューポートの高さにあわせられるようになります。

dvh

これを実現するために、今まではカスタムのユーティリティを作成してましたが、その必要がなくなりました。
あとは、個人的にはあまり使わなそうだなと思いましたが、lvh、svhというユーティリティクラスも追加されています。

has

:has()はCSSの中でもかなり強力な擬似クラスに分類されるものですが、それがTailwindCSSでもサポートされました。
hasを使うと、子要素や兄弟要素のセレクタや状態に応じて、スタイルを変更することができるようになります。

便利さを実感していただくために、:has()を使った場合と使わなかった場合のReactコンポーネントのコードを見てみましょう。

以下はチェックボックスがチェックされたら親要素の背景色を青色に変更するコードです。

export function WithHas() {
  return (
    <div className='has-[:checked]:bg-blue-200 flex w-full gap-x-2 bg-red-200 p-4'>
      <label htmlFor='background-toggle'>Check me</label>
      <input type='checkbox' id='background-toggle' />
    </div>
  )
}
'use client'

import { useState } from 'react'

export function WithoutHas() {
  const [isChecked, setIsChecked] = useState(false)

  return (
    <div
      className={`flex w-full gap-x-2 p-4 ${isChecked ? 'bg-blue-200' : 'bg-red-200'}`}
    >
      <label htmlFor='background-toggle'>Check me</label>
      <input
        type='checkbox'
        id='background-toggle'
        onChange={(e) => setIsChecked(e.target.checked)}
      />
    </div>
  )
}

has

素晴らしい!
hasを使うことで、注目すべきは'use client'が不要になったことです。
useStateによる状態管理が不要なったことで、RSCにできますね。

Style children with the * variant

*:で、子要素に統一したスタイルを適用できるようになります。

<ul className='*:rounded-full *:border *:border-sky-100 *:bg-sky-50 *:px-2 *:py-0.5 hover:*:bg-blue-500 flex gap-x-4'>
  <li>Sales</li>
  <li>Marketing</li>
  <li>SEO</li>
</ul>

asta

liタグに同じclassを繰り返し指定しなくていいので一見すると便利ですが、TailwindCSSでは、子要素に直接定義することを推奨しているようです。
そのため、上記のようなユースケースでは、以下のようにしておくのが無難です。

<ul className='flex gap-x-4'>
  {lists.map((list, index) => (
    <li key={index} className='rounded-full border border-sky-100 bg-sky-50 px-2 py-0.5 hover:bg-blue-100'>{list}</li>
  ))}
</ul>

となるとどんな場面で有効なのか?についてですが、ドキュメントに例がありました。
以下は、data-slotdescriptionが指定されている子要素にのみmt-4のスタイルを当てています。

function Field({ children }) {
  return (
    <div className="data-[slot=description]:*:mt-4 ...">
      {children}
    </div>
  )
}

function Description({ children }) {
  return (
    <p data-slot="description" ...>{children}</p>
  )
}

function Example() {
  return (
    <Field>
      <Label>First name</Label>
      <Input />
      <Description>Please tell me you know your own name.</Description>
    </Field>
  )
}

このように、特定の条件に一致した子要素にだけスタイルを当てたい場合とかは便利に使えそうです。

size

size-10と指定することで、h-10 w-10と同じ意味になります。
地味に嬉しいです。

text-wrap: balance

text-balanceを指定すると、テキストの折り返しをいい感じの位置でやってくれるようになります。

<p className='max-w-60 w-full bg-blue-100 text-balance'>このテキストが、いい感じに折り返されます</p>

textwrap

句読点のところで折り返されて欲しかったですが、日本語だとうまく効かないみたいですね。
英語だといい感じに折り返されます。

Subgrid

Gridの中にGridをネストできるSubgridが使えるようになりました。
全ブラウザでサポートされたのが割と最近のことなので、Subgrid自体をあまり知らないという方は、以下の記事が参考になります。

https://zenn.dev/tonkotsuboy_com/articles/css-subgrid-all-browsers

それでは、Subgridを指定した場合と指定しなかった場合の違いを見てみましょう。

下記はSubgridを指定した場合です。
各Grid要素がバランスよく配置されています。

<div className='grid grid-cols-2 gap-4 md:grid-cols-3'>
  {cards.map(({ h }, index) => (
    <div className='grid-rows-subgrid row-span-4 grid' key={index}>
      <h1 className='flex items-center'>{h}</h1>
      <div className='h-24 w-32 bg-blue-100' />
      <p>説明文{index}</p>
      <div>ラベル{index}</div>
    </div>
  ))}
</div>

subgrid

下記はSubgridを指定しなかった場合です。
見出しの文字が改行されることでレイアウトが崩れてしまっています。

<div className='grid grid-cols-2 gap-4 md:grid-cols-3'>
  {cards.map(({ h }, index) => (
    <div className='row-span-4 grid' key={index}>
      <h1 className='flex items-center'>{h}</h1>
      <div className='h-24 w-32 bg-blue-100' />
      <p>説明文{index}</p>
      <div>ラベル{index}</div>
    </div>
  ))}
</div>

nosubgrid

min-w, max-w, min-h

min-w-20, max-w-20, min-h-20のような指定ができるようになりました。
今まで、min-w-xsのような指定しかできなかったので不便だったので、やっと対応してくれて嬉しいです。

opacity

完全に5の倍数で指定できるようになりました。
今までは、opacity-0からopacity-30までは5の倍数で、opacity-30からopacity-70は急に10の倍数になるという謎の仕様でした。

grid-rows

grid-rowsで指定できるGridの行数が最大6行から最大12行に増えました。

forced-colors

新しくforced-colorsメディアクエリとforced-color-adjustが使えるようになりました。
これは、ユーザーが使用可能な色を制限している場合に、Webアプリ側のスタイルをコントロールします。
forced-color-adjust-noneを指定すると、ユーザーエージェントに強制された色を上書きます。何があってもこの色で表示したい!というケースで使います。
forced-color-adjust-autoを指定すると、forced-color-adjust-noneを無効化し、ユーザーエージェントに強制された色に従います。
forced-colors:を指定すると、ユーザーエージェントが色を強制してる場合に、スタイルを変更できます。
たとえば、<button className='forced-colors:appearance-auto'>とすると、ユーザーエージェントが色を強制していたら、ユーザーエージェントが提供するデフォルトのボタン要素のスタイルに変更します。

最後に

いかがでしたか?個人的にはhasが一番嬉しいですが、他のユーティリティクラスも便利なものばかりでしたね。
ぜひ、TailwindCSS v3.4にアップグレードして、よいTailwindCSSライフを満喫しましょう!

あとがき

AI・データ利活用をリードし、世界にインパクトを与えるプロダクトを開発しませんか?

アイディオットでは、今後の事業拡大及びプロダクト開発を担っていただけるエンジニアチームの強化を行っております。
さらに会社の成長を加速させるため、フロントエンドエンジニア、バックエンドエンジニア、インフラエンジニアのメンバーを募集しております!
日本を代表する企業様へ自社プロダクトを活用した、新規事業コンサルティング、開発にご興味のある方はお気軽にご連絡ください。

【リクルートページ】
https://aidiot.jp/recruit/
【募集ポジション一覧】
https://open.talentio.com/r/1/c/aidiot/homes/3925
【採用についてのお問合せ先】
株式会社アイディオット 採用担当:大島
メールアドレス:recruit@aidiot.jp

Discussion