`start` / `end` ユーティリティが値なしでCSSを生成するバグを修正
Tailwind CSS で誤って導入された、start および end ユーティリティが値なしでCSSを生成するバグが修正されました。レガシーユーティリティへの移行時に紛れ込んだ不要なフォールバックロジックを削除することで、意図しないCSSの出力が抑制されます。
背景
start-* および end-* ユーティリティがレガシーユーティリティとして移管された際に、バグが混入しました。この移管作業の中で、値が指定されていない場合(start や end 単体)にも --inset や --spacing テーマ変数を解決してCSSを生成するフォールバックロジックが残存してしまいました。
その結果、#20002 で報告されたとおり、.start および .end というドキュメント化されていないユーティリティクラスのCSSが生成されるようになっていました。これはユーザーが意図せずCSSバンドルに余分なスタイルを含んでしまう問題です。
技術的な変更
修正の核心は packages/tailwindcss/src/compat/legacy-utilities.ts 内の handleInset 関数から、値なし候補に対するフォールバックロジックを削除した点です。
変更前:
function handleInset({ negative }: { negative: boolean }) {
return (candidate: Extract<import('../candidate').Candidate, { kind: 'functional' }>) => {
if (!candidate.value) {
if (candidate.modifier) return
let value = designSystem.theme.resolve(null, ['--inset', '--spacing'])
if (value === null) return
return [decl(property, negative ? `calc(${value} * -1)` : value)]
}
// ...
}
}
変更後:
function handleInset({ negative }: { negative: boolean }) {
return (candidate: Extract<import('../candidate').Candidate, { kind: 'functional' }>) => {
if (candidate.value === null) return
// ...
}
}
変更前は !candidate.value(falsy判定)で値なし候補を検出した後、テーマ変数を解決してCSSを生成していました。変更後は candidate.value === null(厳密等値比較)で値がない場合は即座に return するだけです。フォールバックロジック全体が削除され、6行が1行になっています。
テストファイル legacy-utilities.test.ts では、テーマ定義が --spacing-4: 1rem(個別スペーシング変数)から --spacing: 0.25rem(スケール変数)に置き換えられました。この変更により、修正前のコードではテストが失敗することが確認でき、修正後は正しくパスすることが検証されています。生成されるCSSも inset-inline-start: var(--spacing-4) から inset-inline-start: calc(var(--spacing) * 4) へと変化しています。
設計判断
値なし候補の扱いを「フォールバック生成」から「早期リターン」に変更するという最小限の修正が選ばれました。
start-4 や start-[4px] のような値付きユーティリティの動作は一切変更されていません。値なし候補(start 単体)に対してのみ即座に処理を打ち切ることで、副作用の範囲を最小化しています。また、!candidate.value(falsy判定)から candidate.value === null(厳密等値比較)への変更により、判定条件がより明示的になっています。
まとめ
レガシーユーティリティへの移管時に混入したフォールバックロジックを削除するだけという、変更規模は最小限でありながら、意図しないCSSの生成という明確な問題を解消する修正です。値なし候補を厳密な null 判定で早期リターンする方式に統一したことで、handleInset 関数の動作がより明確になっています。