CSSのif()で、条件ごとにプロパティの値を切り替える方法

​​CSS​Playwright,カスタムプロパティ

CSS の if() 関数を使うと、1つのプロパティの値を、条件に応じてその場で切り替えられます。条件には、カスタムプロパティの値、画面の幅などのメディアクエリー、ブラウザーが対応しているかどうかの3種類が使えます。@media のブロックや、種類ごとのクラスを書き分けなくても、宣言の中だけで分岐が書けます。

css
.btn {
  background-color: if(
    style(--variant: primary): #00c8e8;
    style(--variant: danger): #d85878;
    else: #fff;
  );
}
補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。ページを Playwright で開き、getComputedStyle() で計算値を調べました。Firefox や Safari など、Chromium 以外のブラウザーでの表示は試していません。各ブラウザーの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。

書き方は「条件: 値;」を並べるだけ

if() の中には、「条件: 値」の組を ; で区切って並べます。上から順に条件を調べ、最初に当てはまった組の値が使われます。else はいつでも当てはまる条件で、どれにも当てはまらなかったときの値を書くのに使います。最後の組のあとの ; は、あってもなくてもかまいません。

条件に使えるのは、次の3つです。

条件の書き方 調べるもの 例
style() その要素のカスタムプロパティの値 style(--variant: danger)
media() 画面の幅や向き、印刷かどうかなど media(width < 700px)
supports() ブラウザーがその値やセレクターに対応しているか supports(color: lch(50% 40 200))

検証環境で、3つの条件をそれぞれ試しました。media(width < 700px): 4px; else: 24px を書いた padding は、画面の幅が 1000px のときは 24px、600px に変えると 4px になりました。supports(color: lch(50% 40 200)) は当てはまり、lch() の色が使われました。存在しない関数の色を supports() で調べた場合は、else の色になりました。

and・or・not も使えます。style((--scheme: dark) and (--contrast: hi)) は、2つのカスタムプロパティがどちらも合っているときだけ当てはまりました。

カスタムプロパティでボタンの見た目を切り替える

ここでは、style() でカスタムプロパティを調べる書き方を使います。ボタンの種類を --variant で受け取り、背景色・文字色・枠の色を決めてみます。

css
.btn {
  border: 2px solid;
  background-color: if(
    style(--variant: primary): #00c8e8;
    style(--variant: danger): #d85878;
    else: #fff;
  );
  color: if(
    style(--variant: primary): #1f2a30;
    style(--variant: danger): #fff;
    else: #067a98;
  );
  border-color: if(style(--variant: danger): #d85878; else: #00c8e8);
}
html
<button class="btn" style="--variant: primary">保存する</button>
<button class="btn" style="--variant: danger">削除する</button>
<button class="btn">キャンセル</button>

検証環境で表示すると、同じ .btn のルールのまま、--variant の値だけで見た目が変わりました。--variant を書いていないボタンは、else の白い背景になります。

3つのボタンを縦に並べた画面。左に、そのボタンに書いた --variant の値。「--variant: primary」の「保存する」は水色の背景、「--variant: danger」の「削除する」はピンクの背景に白い文字、「(--variant なし)」の「キャンセル」は白い背景に水色の枠と文字
同じ .btn のルールで、–variant の値だけを変えた3つのボタン

カスタムプロパティは子要素に継承されるので、親に書いた値も style() で調べられます。検証環境でも、親の div に --variant: danger を書くと、中の .btn は danger の色になりました。JavaScript で element.style.setProperty('--variant', 'danger') のように値を変えたときも、その場で色が切り替わりました。

style() の比べ方には、注意する点が2つあります。1つは、比べられるのはカスタムプロパティだけということです。検証環境で style(color: rgb(255, 0, 0)) のように、ふつうのプロパティを調べる条件を書くと、文字が本当に赤い要素でも当てはまりませんでした。MDN にも、今のところカスタムプロパティしか調べられないと書かれています。

もう1つは、文字列とそうでない値が区別されることです。--variant: "primary" のように引用符で囲んで書くと、style(--variant: primary) には当てはまりませんでした。--variant:primary のように : のあとの空白を省いた書き方は、当てはまりました。

else を書かないと、前に書いた値には戻らない

どの条件にも当てはまらず else もないとき、if() は前に書いた宣言に戻りません。

css
.box {
  padding: 20px;
  padding: if(style(--size: big): 40px);
}

--size を書いていない要素で試したところ、padding は 20px ではなく 0px になりました。if() が何も値を返さず、後ろの宣言の値が空になったためです。CSS Values 5 の仕様にも、@media などと違って、条件が偽でもカスケードを巻き戻さないので、代わりの値は if() の中に書く必要があると書かれています。

どうなるかは、プロパティが継承されるかどうかで変わりました。

プロパティ 条件が偽で else なしのときの計算値
padding(継承されない) 0px(初期値)
color(継承される) 親要素の色

直前に書いた値を使いたいなら、その値を else に書きます。

カスタムプロパティの中で使ったときは、事情が違います。--c: if(style(--scheme: ice): blue); のように書いて条件が偽になると、--c は値がない扱いになり、var(--c, green) の2つ目の値(ここでは green)が使われました。MDN の説明とも一致します。

if と ( のあいだに空白を入れない

if (style(...): 40px; else: 10px) のように、if と ( のあいだに空白を入れると、宣言全体が無効になります。MDN にもそう書かれています。

検証環境で padding: 20px; のあとにこの書き方をすると、padding は 20px のままでした。CSS.supports('color', 'if (else: red)') も false でした。else の値にすらならないときは、まず空白を疑ってください。

else を先に書くと、後ろの条件は調べられない

条件は書いた順に調べられ、else はいつでも当てはまります。if(else: ...; style(--variant: primary): ...) のように else を先頭に書くと、--variant: primary の要素でも else の値になりました。else は最後に置きます。

値の一部や calc() の中にも書ける

if() は、プロパティの値の一部にも書けます。border の色だけを切り替える例です。

css
.card {
  border: 3px solid if(
    style(--variant: danger): #d85878;
    else: #00c8e8;
  );
}

検証環境では、--variant: danger の要素で、border-top-width は 3px、border-top-color はピンクになりました。

calc() の中にも書けます。width: calc(if(style(--wide: yes): 80%; else: 50%) - 50px); は、幅 500px の親の中で、--wide: yes の要素の width が 350px(500px の 80% から 50px を引いた値)になりました。

if() の値の中に、さらに if() を入れることもできます。検証環境で、外側で --scheme の値、内側で画面の幅を調べる if() を書き、--scheme: ice の要素を幅 1000px で表示したところ、外側の ice の分岐の中で、内側の else の色になりました。

Chrome 以外への備えを書いておく

互換性データでの対応状況は次のとおりです。MDN では、まだ実験的な機能とされています。

ブラウザー if()
Chrome・Edge(Android の Chrome を含む) 137 から
Firefox(Android を含む) 対応していない
Safari(iOS を含む) 対応していない

MDN には、if() に対応していないブラウザー向けの代わりの値は、自分で書いておく必要があると書かれています。紹介されているのは、if() の宣言の前に、ふつうの値の宣言を書いておく方法です。

css
.box {
  padding: 1em;
  padding: if(style(--size: "2xl"): 1em; else: 0.25em);
}

MDN の説明では、対応しているブラウザーでは後ろの if() が前の宣言を上書きし、対応していないブラウザーでは前の 1em が使われます。if と ( のあいだに空白を入れた例で見たとおり、検証環境の Chromium でも、無効な宣言のときは前の値が残りました。また、else の節で見たとおり、if() に対応しているブラウザーで前の値を使いたいときは、else に同じ値を書いておく必要があります。

まとめて切り替えたいときは、@supports で if() に対応しているかを調べられます。検証環境の Chromium 153 では、@supports (color: if(else: red)) の中に書いたルールが使われました。

css
@supports (color: if(else: red)) {
  /* if() を使うルール */
}

MDN でも、1つの条件で複数の宣言やルールをまとめて切り替えるときは、これまでどおり @media や @supports、@container を使うとされています。if() が向いているのは、1つのプロパティの値だけを、カスタムプロパティや画面の幅に合わせて変えたいときです。

​Posted by 86