`@utility` に `--default(…)` を追加してフォールバック値をサポート
--value(…) および --modifier(…) の内部で --default(…) を使えるようになり、値なしでも機能する functional utility をCSSだけで定義できるようになりました。
背景
@utility の functional utility は、これまで値(value)が必須でした。たとえば tab-* というユーティリティを定義しても、tab 単体では値が null になるためクラスが生成されず、tab-4 のように明示的な値を必ず付けなければなりませんでした。#16824 では、modifier についても同様の問題が報告されています。@utility text を静的に定義すると text/50 のような modifier 付きの表記が解釈されず、modifier を使うためだけに text-* という functional utility の形を取らざるを得ませんでした。
この制約は、既存のJSベースのAPIでは対応できていた shadow/50 のような「値なし・modifier あり」のパターンが、CSSベースの @utility では実現できないことを意味していました。結果として、ユーザーはCSSだけで記述できるケースでもJSベースのAPIに頼らざるを得ないという状況が生じていました。
技術的な変更
--value(…) と --modifier(…) の引数として --default(…) という新しい構文が追加され、値や modifier が省略された場合のフォールバックを宣言できるようになりました。
基本的な使用例を示します。
@utility tab-* {
tab-size: --value(integer, --default(4));
}
この定義により、以下の挙動が得られます:
-
tab→tab-size: 4(--default(4)が適用される) -
tab-123→tab-size: 123(integer型の値として解決される) -
tab-foo→ 生成なし(integerでも default でもない)
calc() などの複合式の中でも動作します。
@utility tab-* {
tab-size: calc(--value(integer, --default(4)) * 2);
}
この場合、tab は tab-size: 8、tab-123 は tab-size: 246 として解決されます。実装面では、packages/tailwindcss/src/utilities.ts において、value === null の場合に即座に処理を打ち切っていた箇所が削除されました。代わりに、--default(…) が解決できたかどうかを追跡する仕組みが導入され、値が解決できない場合はその宣言を取り除く制御に変更されています。また、--modifier(…) に対しても同様に --default(…) がサポートされ、text-sm のようにデフォルトの line-height を設定するユーティリティを modifier なしで記述できるようになっています。
設計判断
--default(…) を --value(…) の内部引数として明示する方式 が採用されました。
PR内では複数のアプローチが検討されました。一つは、静的な宣言をフォールバックとして並べる方式です。
@utility tab-* {
tab-size: 4;
tab-size: --value(integer);
}
しかしこの方式には、tab-foo のような無効な値でも tab-size: 4 を持つクラスが生成されるという問題があります。無限個のクラスが同一のCSSを生成してしまい、不正な入力を静かに受け入れてしまいます。別の候補として --value(…, 4) のように直接フォールバックを末尾引数として渡す方式も検討されましたが、数値・キーワードが既存の解決ロジックで誤解釈されるリスクがありました。--default(…) を独立した関数として扱う現行の設計は、フォールバックであることを明示しつつ、tab-foo のような無効なクラスを生成しない安全な制約を維持しています。また、calc() 内のような複合プロパティ値の中でも機能するため、別プロパティの重複定義が不要になるという実用上のメリットもあります。
まとめ
--default(…) の追加により、shadow/50 や tab のような「引数省略可能な functional utility」がCSSベースの @utility だけで完結して定義できるようになりました。JSベースのAPIとのギャップが埋まり、CSSファーストなユーティリティ定義の表現力が大きく向上しています。