`@apply` がCSS Mixins仕様のダッシュ識別子を素通しするよう変更
Tailwind CSS v4において、@apply にCSS Custom Functions and Mixins仕様のダッシュ識別子(--my-mixin 形式)が渡された場合、Tailwindのユーティリティとして解釈せずそのまま出力するよう変更されました。これにより、ネイティブCSSミックスインとTailwindの @apply が共存できるようになります。
背景
CSS Custom Functions and Mixins仕様では、@apply にダッシュ識別子(-- で始まる識別子)を渡すことでネイティブミックスインを適用する構文が定義されています。しかし、Tailwindのコンパイラはこのダッシュ識別子をユーティリティとして解釈しようとしてコンパイルに失敗していました。
#19422 では、-- で始まる識別子をTailwindが知らない場合、ブラウザや互換レイヤーが処理できるようそのまま素通しすべきであると報告されています。ミックスイン仕様は @apply というルール名を共有しているため、Tailwind側での明示的なハンドリングが必要でした。
技術的な変更
apply.ts と ast.ts の2ファイルに変更が加えられ、ダッシュ識別子の検出と適切なルーティングが実装されました。
packages/tailwindcss/src/apply.ts では、@apply のパラメータを走査する際に識別子を normalIdents(通常のユーティリティ)と dashedIdents(ダッシュ識別子)の2つのリストに振り分けるよう変更されました。
変更前:
for (let [idx, part] of parts.entries()) {
if (idx % 2 === 0) candidateOffsets[part] = offset
offset += part.length
}
変更後:
let normalIdents: string[] = []
let dashedIdents: string[] = []
for (let [idx, part] of parts.entries()) {
if (idx % 2 === 0) {
if (part[0] === '-' && part[1] === '-') {
dashedIdents.push(part)
} else {
normalIdents.push(part)
}
candidateOffsets[part] = offset
}
offset += part.length
}
振り分けの結果に応じて、以下の3つのパスで処理されます:
-
dashedIdentsのみ: CSSミックスインとみなし、WalkAction.Skipを返して処理をスキップ(そのまま出力) -
dashedIdentsとnormalIdentsが混在: エラーをスローし、ユーザーにルールを分離するよう促す -
normalIdentsのみ: 従来どおりTailwindユーティリティとして処理
packages/tailwindcss/src/ast.ts では、ASTの最適化処理において @apply ルールが保持されるよう変更されています。
変更前後:
// 変更前
copy.name === '@import'
// 変更後
copy.name === '@import' ||
copy.name === '@apply'
この変更により、ダッシュ識別子のみを持つ @apply ルールがAST最適化フェーズで除去されず、出力CSSに保持されます。
設計判断
ミックスインとユーティリティの混在を意図的に禁止する設計 が採用されました。
以下のような混在した記述はすべてエラーになります:
.foo {
/* 以下はすべて無効 */
@apply --my-mixin underline;
@apply --my-mixin() underline;
@apply underline --my-mixin;
@apply underline --my-mixin();
}
この制約は、1つの @apply ルールがTailwindのコンパイルパスとブラウザのネイティブ処理パスの両方を同時に経由することによる不整合を防ぐための判断です。混在させたい場合はルールを分離することが求められます。
なお、PRのコメントにあるとおり、Lightning CSSがこの構文にまだ対応していないため、プロダクションビルドでは正しいコードが生成されないケースがあります。テストコードにも TODO コメントとして明記されており、Lightning CSS側の対応を待ちながら段階的に解消される方針です。
まとめ
この変更は、将来のネイティブCSS機能との共存を見据えた前方互換性への対応です。ダッシュ識別子という明確な識別子プレフィックスを利用してTailwindの処理対象を区別し、エラーメッセージで混在を禁止することで、仕様と実装の境界を明確に定義しています。