CSS に関数と条件分岐が入ってきた:@function と if()

CSS に追加された @function と if() は、コンポーネントが抱える「同じ宣言のコピペ」を減らす。どちらもまだ Baseline ではないが、何を置き換えるのかは先に読む価値がある。

CSS に二つ新しいものが入った。関数を定義できることと、条件を書けること。どちらもまだ実験的だが、置き換える対象はどのプロジェクトにもある重複だ。

@function:実行時にカスタムプロパティを読める関数

@function --step(--n <number>: 1) returns <length> {
  result: calc(var(--space-unit) * var(--n));
}

.card {
  padding: --step(2);
}

形は @function --名前(--引数 <型>: 既定値) returns <型> { ... }、戻り値は result: に書き、呼び出しは --名前(実引数)。

SCSS の関数との違いは書き方ではなく評価されるタイミングだ。

評価 var() を読めるか
SCSS 関数 ビルド時 いいえ(実行時の値は見えない)
CSS @function 計算値の段階 はい

この一行が価値のすべてだ。トークンがカスタムプロパティで、関数がそれを元に計算する場面では、プリプロセッサの関数は原理的に不可能 —— var(--space-unit) が何になるか知らないのだ。このサイトのテーマをトークン方式に移して以来、ずっと欲しかったものだ。

if():三つに分かれていた宣言を一つに

.card {
  grid-template-columns: if(
    media(width < 40rem): 1fr;
    else: 14rem 1fr;
  );
}

テストは三種類 —— style()(カスタムプロパティの値)、media()、supports()。else も複数分岐も書ける。

style() の形が最も役に立つ。

.badge {
  color: if(
    style(--skin: ink): var(--c-accent);
    style(--skin: riso): var(--c-ink-2);
    else: var(--c-text);
  );
}

これまでは、同じセレクタを三つの [data-skin=...] ブロックにそれぞれ書くしかなかった。

現状:どちらも Baseline ではない

MDN の表示は両方同じで、限定的な利用、Baseline 未達、実験的。最新のブラウザでは動くが、古い端末は期待できない。

今使うなら段階的強化しかない。しかも順序が重要だ。

.card {
  /* 先に古い書き方:if() を解釈できないブラウザはこちらを使う */
  padding: 1rem;
  padding: if(media(width > 60rem): 1.5rem; else: 1rem);
}

CSS が解釈できない値は宣言ごと破棄されるので、古いブラウザは静かに前の行に留まる。逆順に書くと逃げ道がなくなる。

私の判断

if() のほうが @function より価値が高い。

条件分岐はどのコンポーネントもぶつかる重複だ。ブレークポイント、スキン、明暗 —— 軸が増えるたびに層が増える。一方、関数の抽出が本当に効く場所は限られている。ほとんどの calc() の組み合わせは、関数にしても読みやすくならない。

もう一つ自制すべき点。CSS は純粋な宣言型から「宣言型 + 少しのプログラミング」へ動いているが、カスケード優先であることは変わらない。これで状態機械を作らないこと。if() で状態遷移を模し始めたら、そのロジックはそもそもスタイルシートに置くべきではなかった。

← 記事一覧に戻る

コメント

…