1. ホーム
  2. CSS

【CSS】@supports の使い方|ブラウザーの対応状況で書き分ける機能クエリを解説

Share

CSS には新しいプロパティが次々に増えますが、すべての閲覧環境がすぐに対応してくれるわけではありません。@supports(機能クエリ)を使うと「このブラウザーがこの書き方を理解できるかどうか」を CSS 側で判定して、対応しているときだけ新しいスタイルを適用できます。この記事では、@supports の基本構文から not / and / or の組み合わせ、セレクターの対応を調べる selector()、そして「そもそも @supports が要る場面・要らない場面」の見極め方までを、動かせるデモ付きで解説します。

@supports はブラウザーの理解度で CSS を切り替える仕組み

@supports@media と同じ「条件付きグループ規則」の仲間です。@media が画面幅などの環境で中身を出し分けるのに対し、@supportsブラウザーがその CSS を解釈できるかどうかで中身を出し分けます。条件が真なら中のルールが適用され、偽ならブロックまるごと無視されます。

style.css
/* 「display: grid; と書いて解釈できるブラウザー」だけが中身を読む */
@supports (display: grid) {
  .cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
  }
}

条件は必ず丸かっこで囲み、中にはプロパティ名と値をコロンでつないだ宣言を書きます。セミコロンは不要です。ブラウザーはこの宣言を実際に適用するのではなく、「自分の CSS パーサーがこの組み合わせを解釈できるか」だけを判定します。

古い書き方を先に書き、@supports で上書きする

@supports の定石は、すべてのブラウザーで動く書き方を先に素で書き、そのあとに @supports ブロックで新しい書き方に上書きするという順番です。@supports@media と同じく詳細度を一切上げないため、同じ詳細度のルールが競合したときはあとに書いたほうが勝ちます。逆に @supports を先に書いてしまうと、あとから書いたフォールバックに上書きされて、せっかくの分岐が無駄になります。

下のデモは、カードの並びを「flexbox + margin による余白」で先に組み、@supports (display: grid) の中で grid + gap に置き換えたものです。CSS タブの @supports (display: grid)@supports (display: griddd) のような存在しない値に書き換えると条件が偽になり、フォールバック側の表示(水色のカード)に切り替わります。条件の真偽で見た目がどう変わるかを試してみてください。

<p class="status">現在の表示方法:</p>
<div class="cards">
  <div class="card">カード1</div>
  <div class="card">カード2</div>
  <div class="card">カード3</div>
</div>
body {
  font-family: sans-serif;
  margin: 12px;
}

/* 1. まず「非対応ブラウザーでも動く書き方」を素で書く */
.cards {
  display: flex;
  flex-wrap: wrap;
  margin: -6px;
}

.card {
  width: calc(33.333% - 12px);
  margin: 6px;
  padding: 20px 0;
  text-align: center;
  border-radius: 6px;
  background: #93c5fd;
  box-sizing: border-box;
}

.status::after {
  content: "フォールバック(flexbox と margin)";
  color: #b45309;
  font-weight: bold;
}

/* 2. 対応しているブラウザーだけ、あとから上書きする */
@supports (display: grid) {
  .cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
    margin: 0;
  }

  .card {
    width: auto;
    margin: 0;
    background: #2563eb;
    color: #fff;
  }

  .status::after {
    content: "grid と gap(@supports の中身が適用された)";
    color: #2563eb;
  }
}
Preview

このデモで @supports が効いているのは、grid に切り替えるときに .cardsmargin: -6px.cardwidth / margin打ち消す必要があるからです。フォールバック用に足した余白の調整がそのまま残ると、gap と二重にかかって崩れてしまいます。こうした「古い書き方の後始末」をまとめて囲めるのが機能クエリの利点です。

not / and / or で条件を組み合わせる

条件は論理演算子でつなげられます。not は条件を反転させ、and は両方を満たすとき、or はどちらかを満たすときに真になります。書き方と判定される内容は次のとおりです。

書き方真になる条件
(display: grid)プロパティと値の組み合わせを解釈できるとき
not (display: grid)解釈できないとき
(A) and (B)A と B の両方を解釈できるとき
(A) or (B)A と B のどちらかを解釈できるとき
selector(:has(a))そのセレクターを解釈できるとき

or がよく使われるのは、ベンダープレフィックス付きの書き方も合わせて判定したいときです。次の例は、プレフィックスあり・なしのどちらかが通れば背景ぼかしを適用します。

style.css
/* どちらかが解釈できれば適用する */
@supports (backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px)) {
  .header {
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    background: rgba(255, 255, 255, 0.6);
  }
}

/* 非対応のときだけ、不透明な背景でごまかす */
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .header {
    background: #ffffff;
  }
}

注意したいのは、andor を同じ階層に混ぜて書けないことです。(A) and (B) or (C) のように並べると条件全体が不正になり、そのブロックは適用されません。混在させるときは ((A) and (B)) or (C) のように、まとまりをかっこで明示します。not のあとに or の式を置くときも、上のコードのように全体をもう一段かっこで囲む必要があります。

selector() でセレクターの対応を判定する

プロパティではなくセレクターの対応を知りたいときは selector() を使います。かっこの中にセレクターを書くと、ブラウザーがそれを解釈できるかどうかが判定されます。:has()::backdrop のように、比較的新しいセレクターを使うときに役立ちます。

style.css
@supports selector(:has(img)) {
  /* :has() が使えるときだけの装飾 */
  .card:has(img) {
    padding-top: 0;
  }
}

下のデモでは、バッジが入っているリスト項目だけを :has() で強調しています。@supports selector(:has(.badge)) が真なら青い枠、偽なら装飾なしになり、どちらの分岐に入ったかがメッセージで分かるようにしてあります。:has の部分を :hasnot のような架空の擬似クラスに書き換えると、条件が偽になったときの表示を確認できます。

<ul class="list">
  <li>りんご</li>
  <li>みかん <span class="badge">NEW</span></li>
  <li>ぶどう</li>
</ul>
<p class="note"></p>
body {
  font-family: sans-serif;
  margin: 12px;
}

.list {
  list-style: none;
  padding: 0;
}

.list li {
  padding: 10px 12px;
  margin-bottom: 6px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
}

.badge {
  padding: 2px 8px;
  border-radius: 999px;
  background: #ef4444;
  color: #fff;
  font-size: 12px;
}

/* :has() を解釈できるブラウザーだけ、バッジ付きの項目を強調する */
@supports selector(:has(.badge)) {
  .list li:has(.badge) {
    border-color: #2563eb;
    background: #eff6ff;
    font-weight: bold;
  }

  .note::before {
    content: "selector(:has(.badge)) → true。強調表示が有効です。";
    color: #2563eb;
  }
}

/* 非対応のときだけ表示するメッセージ */
@supports not selector(:has(.badge)) {
  .note::before {
    content: "selector(:has(.badge)) → false。強調表示なしで表示しています。";
    color: #b45309;
  }
}
Preview

@media と組み合わせて使う

@supports@media はどちらも条件付きグループ規則なので、互いに入れ子にできます。順番はどちらが外側でも構いません。「grid が使えて、かつ画面が広いときだけ2カラムにする」なら、次のように書けます。

style.css
@supports (display: grid) {
  .layout {
    display: grid;
    gap: 24px;
  }

  /* 機能クエリの中にメディアクエリを入れる */
  @media (min-width: 768px) {
    .layout {
      grid-template-columns: 240px 1fr;
    }
  }
}

入れ子にした場合は外側と内側の条件を両方満たしたときだけ中身が適用されます。grid 対応の判定を1か所にまとめられるので、フォールバック用のスタイルと新しいスタイルが混ざらず、あとから読み返しやすくなります。

@supports が本当に必要な場面を見極める

CSS にはもともと「解釈できない宣言は無視される」という性質があります。同じプロパティを2回書けば、後ろの宣言を理解できるブラウザーはそちらを使い、理解できないブラウザーは前の宣言のまま表示します。この形で足りるなら @supports は不要です。

style.css
.title {
  /* 非対応なら 24px のまま。対応していれば下の行で上書きされる */
  font-size: 24px;
  font-size: clamp(20px, 4vw, 32px);
}

機能クエリが必要になるのは、この「上書きするだけ」では済まないときです。具体的には、新しい書き方に切り替えると別のプロパティを打ち消したり、複数のセレクターをまとめて書き換えたりする必要があるときです。先ほどのカードのデモがまさにその例で、grid に切り替えると親のマイナスマージンと子の幅指定が邪魔になるため、まとめて囲む価値があります。セレクターの対応を調べる selector() も、宣言の重ね書きでは代用できません。

もう一つ覚えておきたいのは、@supports 自体を知らない古いブラウザーはブロックまるごとを読み飛ばすという点です。つまり @supports not (...) で書いたフォールバックは、そうしたブラウザーには届きません。フォールバックは @supports の外に素で書いておくのが安全です。

@supports の条件が思ったとおりに効かないとき

プロパティ名だけを書いている

条件のかっこの中はプロパティ名と値の両方が必要です。@supports (aspect-ratio) のようにプロパティ名だけを書くと条件式として不正になり、ブロックは適用されません。@supports (aspect-ratio: 1 / 1) のように、実際にスタイルシートへ書くのと同じ形の宣言を入れます。値は何でもよいわけではなく、そのプロパティで実際に使える値を選んでください。

値が不正で常に偽になっている

判定はプロパティ名と値の組み合わせで行われるため、値の書き方を間違えるとどのブラウザーでも偽になります。@supports (width: 100) は単位が抜けていて不正、@supports (gap: 16 px) は数値と単位の間に空白が入っていて不正です。中身がまったく適用されないときは、条件に書いた宣言をそのまま普通のルールに貼り付けて、開発者ツールで有効な宣言として認識されるかを確かめると原因が切り分けられます。

かっこと演算子の書き方が合っていない

条件のかっこを省いた @supports display: grid や、宣言の末尾にセミコロンを付けた @supports (display: grid;) は不正です。また、andor を同じ階層に混ぜて書くこともできません。条件が複雑になってきたら、まとまりごとにかっこで囲んで階層をはっきりさせるほうが安全です。not は条件全体の前に置き、否定したい式は必ずかっこでくくります。

解釈できることと、期待どおり動くことは別

@supports が確かめているのは、あくまで「その宣言を CSS として解釈できるか」です。実装にバグがある場合や、同じプロパティでも文脈によって挙動が違う場合までは区別できません。たとえば gap は grid でもフレックスボックスでも使えるプロパティですが、過去には grid の gap にだけ対応していて、フレックスボックスでは効かないブラウザーが存在しました。この状態でも @supports (gap: 16px) は真になります。判定したい機能と、実際に使いたい文脈がずれていないかを意識して条件を選んでください。

まとめ

@supports は、ブラウザーがその CSS を解釈できるかどうかで中身を出し分ける機能クエリです。条件はかっこの中にプロパティ名と値をセットで書き、not / and / or で組み合わせられます。andor を混ぜるときはかっこで階層を示す点に注意してください。セレクターの対応は selector() で調べられ、@media とは自由に入れ子にできます。書き方の基本は「素のフォールバックを先に書き、@supports であとから上書きする」こと。詳細度は上がらないので、順序がそのまま結果になります。ただし、同じプロパティを重ね書きするだけで済むケースでは機能クエリは不要です。打ち消しが必要なときやセレクターを判定したいときに絞って使うと、CSS が無駄に複雑になりません。

参考ページ