`tracking-*` ユーティリティの正規化候補選択ロジックを改善
tracking-[0.05em] のような任意値クラスを名前付きユーティリティへ正規化(canonicalize)する際、複数の候補が存在しても正規形を提示できなかった問題が修正されました。負の値より正の値を優先するルールを追加することで、tracking-* 全クラスの正規化提案が機能するようになります。
背景
Tailwind CSS IntelliSense には、任意値クラス(例: tracking-[0.05em])を等価な名前付きユーティリティ(例: tracking-wider)へ置き換える提案機能があります。この正規化は内部的に「シグネチャ」を用いており、同一のシグネチャを持つ2つのクラスは同一とみなされます。
問題は、tracking-* のデフォルトテーマ値がネガティブな値を含む点にありました。たとえば tracking-tighter は -0.05em、tracking-wider は 0.05em を持ちます。このとき -tracking-tighter(元の値の符号反転)は 0.05em となり、tracking-wider と同じシグネチャを持ちます。その結果、tracking-[0.05em] の正規化候補として tracking-wider と -tracking-tighter の2つが見つかり、従来の実装では「複数候補が存在する場合はスキップ」というルールにより、正規化提案が出力されませんでした。
tailwindlabs/tailwindcss-intellisense#1558 で報告されたこの問題では、以下の表のように tracking-widest だけが正しく提案される奇妙な挙動が観察されていました:
| ユーティリティ名 | 値 | 任意値クラス | 提案 |
|---|---|---|---|
tracking-tighter |
-0.05em |
tracking-[-0.05em] |
✗ |
tracking-tight |
-0.025em |
tracking-[-0.025em] |
✗ |
tracking-normal |
0em |
tracking-[0em] |
✗ |
tracking-wide |
0.025em |
tracking-[0.025em] |
✗ |
tracking-wider |
0.05em |
tracking-[0.05em] |
✗ |
tracking-widest |
0.1em |
tracking-[0.1em] |
✓ |
tracking-widest のみが提案されていたのは、-tracking-tightest が存在しないため競合候補が生じなかったという偶然の産物でした。
技術的な変更
canonicalize-candidates.ts 内の arbitraryUtilities と bareValueUtilities の2か所において、複数候補が存在する場合のフォールバックロジックが追加されました。
変更前:
// Multiple utilities can map to the same signature. Not sure how to migrate
// this one so let's just skip it for now.
//
// TODO: Do we just migrate to the first one?
if (replacements.length > 1) return
変更後:
// Multiple utilities can map to the same signature.
if (replacements.length > 1) {
// Prefer positive values over negative values
let maybeReplacement: string | undefined = undefined
for (let replacement of replacements) {
if (replacement[0] === '-') continue // Skip negative values
// If multiple non-negative replacements exists then we are unsure
// what to do, so let's bail.
if (maybeReplacement) return
// Consider this replacement
maybeReplacement = replacement
}
// ...
return
}
新ロジックは次の手順で動作します。複数の候補が得られたとき、- で始まる候補(負値ユーティリティ)をすべてスキップし、残った非負の候補を追跡します。非負の候補が1つだけであればそれを採用し、2つ以上あれば依然として「どれを選ぶべきか不明」としてスキップします。
また、packages/tailwindcss/src/canonicalize-candidates.test.ts にはリグレッションテストが追加され、上記テーブルの全パターンと、-tracking-tighter → tracking-wider のような負値ユーティリティから正値への変換も検証されます。
設計判断
負の値ユーティリティの廃止ではなく、候補選択ルールの拡張 という方針が採用されました。
PR本文では -tracking-* 形式の名前付きユーティリティを deprecated にすることも検討されています。しかし公式ドキュメントの「Using negative values」でこのパターンが明示的に紹介されているため、後方互換性を壊す変更は取れませんでした。
代わりに「正の候補を負の候補より優先する」という追加ルールを設け、既存の API を変えずに問題を解消しています。このアプローチは裸値(bare value)ユーティリティに関する既存の優先度処理とも一貫しており、コードベース全体の設計方針と整合しています。
まとめ
本PRは、シグネチャベースの正規化において「複数候補がある場合は諦める」という単純なフォールバックを、「負値より正値を優先する」という明示的なルールへと昇格させた変更です。tracking-* の全ユーティリティに対して正規化提案が機能するようになり、IntelliSense の体験が改善されます。