🍃

tailwindcss v4.0-alpha

2024/03/09に公開

こちらの記事はtailwindcss v4.0-alphaにおける主な変更点をまとめております。
tailwindcss v4.0の正式リリースは今年後半(早くて夏ごろ)になるようです。

Oxide

Oxideという新しいエンジンが導入されます。特徴はこちらになります。

  • 最大10倍の高速化:Tailwind CSSによって作られるWebサイトのビルド時間を960msから105msに短縮します。
  • バンドルサイズの縮小:RustLightning CSS(CSSの解析エンジン)という高性能な技術によって部分的に書き直されましたが、インストール時のサイズは35%以上小さいです。
  • Rustを採用:フレームワークの処理がもっとも重い部分にRustを使って高速化し、その他は拡張性のためにTypeScriptを使ってます。
  • 依存関係は1つ:Oxideが依存するのはLightning CSSのみです。従来はpostcss autoprefixerなどの依存関係がありましたが、それが1つになります。
  • カスタムパーサー:独自のCSSパーサーと独自のデータ構造を用いることによってpostcssを使ってたときよりもパース処理を2倍以上高速化します。

CSSパイプライン

Lightning CSSをフレームワークに直接統合してるため、CSSパイプラインについて何も設定する必要がありません。CSSパイプラインとは、CSSファイルを最適化し、ブラウザで使用するために準備する一連の処理工程です。

  • 組み込みの@importpostcss-importは使わず@importで簡単にセットアップできます。
  • 組み込みのベンダープレフィックス:autoprefixerを追加する必要もありません。autoprefixerはCSSに自動でブラウザ固有のプレフィックスを追加し、異なるブラウザでの互換性を確保しつつ、開発者は手動でプレフィックスを追加する手間を省けました。ただその辺はもう自動でやってくれるようです。
  • 組み込みのネストサポート:ネストされたCSSをフラットにするためのプラグインも必要ありません。CSS自体がネストをサポートするようになってるためです。
  • 構文変換:oklch()の色やmedia queryの範囲などのモダンなCSS機能も色んなブラウザで動くようにトランスパイルされます。

コンポーザブルなバリアント

group-* peer-* has-* not-*のようなバリアントを組み合わせることができます。従来はgroup-has-*のようなバリアントはフレームワークで明示的に定義されてましたが、group-*は既存のhas-*バリアントと組み合わせることができ、focusのような他のバリアントとも組み合わせられるようになりました。

index.html
<div class="group">
- <div class="group-has-[&:focus]:opacity-100">
+ <div class="group-has-focus:opacity-100">
    <!-- ... -->
  </div>
</div>

Zero-configurationのコンテンツ検出

従来tailwindcssを使うときtailwind.config.jscontentキーを設定しtailwindcssクラスが使用されてるファイルのパスを指定する必要がありました。この設定によりtailwindcssは指定されたファイルで使用されてるクラスを検出し、対応するスタイルを生成しました。しかし、その設定はもう不要になります。具体的には何を使ってtailwindcssをプロジェクトに統合するかによって、コンテンツの検出は次の2つのいずれかの方法を以って行われます。

  • postcss pluginCLIを使用するとtailwindcssはバイナリファイルを無視し.gitignoreに書かれてるパスも追わないようになります。それ以外のものはすべてクロールしどこにtailwindcssが使われてるか検出します。
  • vite pluginを使用するとモジュールグラフに依存します。それによってユーザーがtailwindcssクラスを使ってるファイルを正確に把握し、誤検知が発生しません。将来的にはvite以外のバンドラプラグインにも拡張するようです。

2つ目の方法ではtailwindcssviteの作ったモジュールグラフを利用します。モジュールというのはJavaScriptにおける1つのファイルのことです。そのファイルの中でインポートやエクスポートしますが、その依存関係をモジュールグラフと言います。viteに限らずJavaScriptのバンドラはモジュールを解析する仕組みになってて、このモジュールはあのモジュールに依存してるというようにグラフを描きそれを最終的に1つのJavaScriptにコンパイルするという仕組みになってます。そのモジュールグラフを使うことによってtailwindcssがインポートされてるモジュールのみ検知できるため、誤検知が発生しません。1つ目の方法だとtailwindcssクラスを使ってないモジュールもスキャンしてしまって無駄になりますが、モジュールグラフを使用するとそれがなくなります。Next.jsはバンドラにviteが使われてませんが、将来的にturbopackNext.jsの次世代バンドラ)でも同じことができるようになりそうですね。

CSSファーストの設定

tailwindcss v4.0の主な目標はフレームワークをよりCSSネイティブに感じられるようにすることにあります。従来はtailwind.config.jsを設定しglobals.css@tailwind base @tailwind components @tailwind utilitiesという独自の構文をインポートして使用してました。これらの構文はpostcssによって解析され、ブラウザが解釈可能なCSSに変換されてました。しかし、v4.0では@tailwindといった独自の構文の使用がなくなり、より標準的なCSSの構文に近づける方向に進んでます。新しいアプローチでは、@importを使用してtailwindcssを直接インポートし、CSSファイル内でtailwindcssの機能を利用できるようになります。

main.css
@import "tailwindcss";

また従来はtailwindcssをカスタマイズするときtailwind.config.jsextendを使って行ってましたが、今後は@themeを使ってCSS変数を上書きするような形でカスタマイズします。

main.css
@import "tailwindcss";

@theme {
  --font-family-display: "Satoshi", "sans-serif";

  --breakpoint-3xl: 1920px;

  --color-neon-pink: oklch(71.7% 0.25 360);
  --color-neon-lime: oklch(91.5% 0.258 129);
  --color-neon-cyan: oklch(91.3% 0.139 195.8);
}

上述の--color-neon-cyan: oklch(91.3% 0.139 195.8);によってtext-neon-cyanのように文字色を指定できます。

index.html
<div class="max-w-lg 3xl:max-w-xl">
  <h1 class="font-display text-4xl">
    Data to <span class="text-neon-cyan">enrich</span> your online business
  </h1>
</div>

--color-*: initial;のような構文によって名前空間をすべてリセットし、上書きすることもできます。--color-*: initial;は色関連の名前空間をすべてリセットします。

main.css
@import "tailwindcss";

@theme {
  --color-*: initial;

  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

tailwindcssデフォルトのthemeはこちらからご確認できます。

https://github.com/tailwindlabs/tailwindcss/blob/next/packages/tailwindcss/theme.css

そもそもtailwindcssデフォルトのthemeを使いたくないという場合はtailwindcss/preflight tailwindcss/utilitiesを使ってデフォルトのthemeのインポートをスキップできます。

main.css
- @import "tailwindcss";
+ @import "tailwindcss/preflight" layer(base);
+ @import "tailwindcss/utilities" layer(utilities);

@theme {
- --color-*: initial;
  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

またthemeのすべての値をカスタムCSSのネイティブCSS変数として利用できます。

dist/main.css
:root {
  --color-gray-50: #f8fafc;
  --color-gray-100: #f1f5f9;
  --color-gray-200: #e2e8f0;
  /* ... */
  --color-green-800: #3f6212;
  --color-green-900: #365314;
  --color-green-950: #1a2e05;
}

それによってvarから値を取ってくることもできます。
Framer MotionのようなアニメーションライブラリでもCSS変数を使えるようです。

index.html
<div class="p-[calc(var(--spacing-6)-1px)]">
  <!-- ... -->
</div>
jsx
import { motion } from "framer-motion"

export const MyComponent = () => (
  <motion.div
    initial={{ y: 'var(--spacing-8)' }}
    animate={{ y: 0 }}
    exit={{ y: 'var(--spacing-8)' }}
  >
    {children}
  </motion.div>
)

非推奨ユーティリティの削除など

破壊的変更はありませんが、共有事項がありました。

  • text-opacity-* flex-grow-* decoration-sliceなどの非推奨ユーティリティが削除され、text-{color}/* grow-* box-decoration-sliceを採用しました。
  • postcss plugincliが別のパッケージとなってtailwindcssは軽くなりました。@tailwindcss/postcss @tailwindcss/cliを使うなら別インストールする必要があります。
  • 元々gray-200だったborderのデフォルトカラーがなくなりました。
  • ringのデフォルトは3pxでしたが、1pxになりました。

参照

https://tailwindcss.com/blog/tailwindcss-v4-alpha

tailwindcss v4.0の正式リリースに向けてまだまだやることはあるようですが、
年内のリリースが待ち遠しいですね。

自らの備忘録のために投稿してますが、なにかお役に立てましたら幸いです!👏
また、なにか間違ってましたらご指摘いただけますと幸いです!🙏

Discussion