メインコンテンツへ移動

CSS Feature Query

無料2018-08-18#CSS#CSS特性查询#特性查询最佳实践#@supports best practice#CSS Grid polyfill#Modernizer vs CSS Feature Query

Gridレイアウトにポリフィルはありますか?

1. 役割

media query(メディアクエリ)と同様に、feature query(特性クエリ)も条件付きスタイルの一種であり、特定のスタイル規則をサポートしている環境にのみ、指定されたスタイルセットを適用します:

The @supports CSS at-rule lets you specify declarations that depend on a browser's support for one or more specific CSS features. This is called a feature query.

ちょっと待ってください、この能力はCSSに生まれつき備わっているようです

新しいプロパティや値が将来既存のプロパティに追加できるようにするために、ユーザエージェントは、未知のプロパティを含む宣言、不正な値を含む宣言、未知の@キーワードを含む@ルールなど、不正なスタイルシートの一部を無視しなければなりません。

P.S. 詳細は4.2 解析エラーの処理規則を参照してください。

CSSは設計当初からフォールトトレラント(容認性がある)であり、現在の環境でサポートされているスタイル規則は正しく適用され、サポートされていないものは静かに無視されます:

Browsers simply skip over code they don’t understand, without throwing an error.

例えば、よく見かけるものです:

.card {
  margin: 10px;
  border: 1px solid #ddd;
  box-shadow: 3px 5px 5px #eee;
}

box-shadow をサポートしている環境では影が表示され、浮いているカードのように見えますが、サポートされていない環境ではマージンとボーダーだけが残り、フラットな普通の矩形ブロックになります。これは自然なスタイルのフォールバックと言えます。このような許容能力により、新機能の採用における不安が軽減されます(サポートされていなければ、フォールバック案に戻るだけです)。

では、feature query はどのような能力をもたらすのでしょうか

これは、親切なプログレッシブ・エンハンスメント・メカニズムが組み込まれたようなものです。以前は通常 Modernizr で行っていたことに対して、今では別の選択肢が増えました。例えば:

Override one layout method with another.

以前との違いは、影響範囲にあります。フォールトトレラントなフォールバックは、サポートされていないスタイルが適用された要素にのみ影響しますが、特性クエリによるフォールバックは、任意の要素グループに影響を与えることができます。例えば:

.card {
  margin: 10px;
  border: 1px solid #ddd;
}
@supports (box-shadow: 3px 5px 5px #eee) {
  /* 以前のCSSフォールトトレラントなフォールバックでは不可能だった、他の要素への影響 */
  body:before {
    content: 'box-shadow is supported!';
    background-color: green;
  }

  .card {
    box-shadow: 3px 5px 5px #eee
  }
}

P.S. 厳密に言えば、フォールトトレラントなフォールバックは通常「宣言レベル」(一部の宣言が無視または適用される)ですが、特性クエリによるフォールバックは「規則セットレベル」(一部の規則セットが無視または適用される)です:

宣言は、空であるか、プロパティ名の後にコロン(:)とプロパティ値が続く形式で構成され、その間に空白文字を入れることができます。

規則セット(「規則」とも呼ばれる)は、セレクタの後に宣言ブロックが続く形式で構成されます。

2. 構文

構文レベルでは、feature query は @supports CSS at-rule と呼ばれます。media query との比較は以下の通りです:

@supports (display: grid) {
  div {
    display: grid;
  }
}

@media screen and (min-width: 900px) {
  article {
    padding: 1rem 3rem;
  }
}

非常に似ており、どちらも @キーワード の後の条件が成立したときに {...} 内のスタイル規則を適用することを示します。両者とも条件付きグループ規則(conditional group rules)です:

/* 一般的な構造 */
@IDENTIFIER (RULE) {/* CSS block */}

/* @supports CSS at-rule */
@supports <supports-condition> {
  <group-rule-body>
}

また、条件部分では、AND(and)、OR(or)、NOT(not)の論理演算をサポートしています:

@supports (display: grid) and (not (display: inline-grid))
@supports (transform-style: preserve) or (-moz-transform-style: preserve)

P.S. プロパティ名と値のペア形式の条件のみをサポートしており、他の形式はサポートしていません。例えば:

@supports (@charset "utf-8") {
  /* スタイル規則 */
}

3. 使い方

実際のシナリオでは、一般的なパターン(ベストプラクティス)は以下の通りです:

/* フォールバック用スタイル - レガシー環境向け */

@supports (最先端の機能) {
  /* 拡張用スタイル - モダン環境向け */
  /* 必要であれば、一部のフォールバックスタイルを上書きする */
}

注意が必要なのは、ある機能をサポートしていないことが、否定形式の feature query(@supports not)と等価ではないということです:

@supports not (height: 100vh) {
  /* vh をサポートしていない環境にのみ、このスタイル規則セットを適用したい */
}

vh をサポートしていない環境を期待通りに絞り込むことはできません。なぜなら、@supports 自体がサポートされていない場合、このフォールバック用スタイルセットを含む @ルール全体が無視されるからです。つまり、この判定は信頼できず、一部(@supportsvh もサポートしていない環境)を見落としてしまいます。

同様に、肯定形式の feature query も完全に信頼できるわけではありません。例えば:

@supports (height: 100vh) {
  /* vh をサポートしている環境にのみ、このスタイル規則セットを適用したい */
}

@supports をサポートしていないが vh をサポートしている環境では、期待通りに動作しません。しかし、一般的にサポート状況を判断する必要がある機能は、feature query よりもさらに新しいものであることが多いため、通常はそれほど心配する必要はありません。

4. 優雅な降格(Graceful Degradation)と漸進的拡張(Progressive Enhancement)

ブラウザ間の不一致の問題に対し、これらは2つの類似した対応戦略ですが、違いは以下の点にありま��:

  • 優雅な降格(Graceful Degradation):モダンな環境を優先し、レガシーな環境に対しては妥協します(すべてのエフェクトを有効にし、レガシーな環境ではエフェクトを下げますが、最低限の利用可能性は保証します)。

  • 漸進的拡張(Progressive Enhancement):レガシーな環境を優先し、モダンな環境には特別な配慮をします(まず最低限の利用可能性を確保し、その後に特殊な機能の追加を検討します)。

Graceful degradation starts complex with a goal of providing a simple experience when needed. Progressive enhancements starts simple and then adds on to that with the desired feature-rich experience.

Modernizer

Modernizr は、一般的な特性検出ソリューションであり、JavaScript を使用して実行環境が特定の機能をサポートしているかどうかをチェックします。

Modernizer checks if a feature is available in the browser and returns true or false.

簡単に言えば、Modernizr はモダンな環境とレガシーな環境を区別するのに役立ち、レガシーな環境に対してフォールバック(fallback)を行ったり、パッチ(polyfill)を当てたりすることを可能にします。例えば:

if (Modernizr.awesomeNewFeature) {
  showOffAwesomeNewFeature();
} else {
  getTheOldLameExperience();
}

JS ソリューションとしての利点は、十分に柔軟であることです。CSS 特性のクエリだけでなく、JS で検出可能なすべての特性をサポートしています。例えば:

// メディア特性
var query = Modernizr.mq('(min-width: 900px)');
if (query) {
  // ブラウザのウィンドウ幅が 900px 以上
}
// DOMイベント
Modernizr.hasEvent('blur') // true;
// プラグイン特性
Modernizr.on('flash', function( result ) {
  if (result) {
  // ブラウザに Flash がインストールされている
  } else {
    // ブラウザに Flash がインストールされていない
  }
});

しかし、いくつかの問題があります:

  • パフォーマンス:追加の JS を導入する必要があり、検出する新機能が増えるにつれてファイルサイズが大きくなり、パフォーマンスの負担になります。

  • 拡張性:サードパーティのサポートに依存しており、最新の機能に対応する特性検出が提供されるまで時間がかかる場合があります。これは(Modernizr のバージョン更新による)手動拡張に相当します。

  • 使いやすさ:対象となる特性の名前(batteryapiflexbox など。その他の名前は Features detected by Modernizr を参照)を表から探してからでないとチェックできず、あまり便利ではありません。

  • 特性の粒度:上述の特性名を最小検出単位とすることは、必ずしも適切ではありません。例えば、justify-content: space-evenly に対応する特性名が存在しない可能性があります。

  • 信頼性:補助的な手段に頼って特性のサポート状況を検出するため、100% 確実ではありません。例えば、一部の実装バージョンでは正確に区別できない可能性があります。

CSS Feature Query

ブラウザに組み込まれた CSS 特性検出サポートです。あるスタイル規則をサポートしているかどうかを最もよく知っているのは、当然ながらブラウザ自身です。今回は feature query を通じて、その内部状態が公開されたに過ぎません。

Modernizr と比較して、いくつかの利点があります:

  • パフォーマンスの向上:純粋な CSS ソリューションであり、JS の関与を必要としません。

  • 優れた拡張性:ブラウザの基本能力として、新しい機能がリリースされた直後に検出可能であり、手動での拡張は不要です。

  • 自然な構文:特性名を表から探す必要がなく、スタイル宣言をそのままクエリ条件として使用できます。

  • 細かい粒度:プロパティ名と値のペアという粒度で、十分に柔軟です。

  • 信頼性:サポートしているかどうかはブラウザ自身が判断するため、絶対的に信頼できます。

もちろん、欠点はスタイル特性のクエリのみをサポートしていることであり、CSS 以外の特性には無力です。そのため、機能面では Modernizr は CSS feature query のスーパーセットと言えます。

5. 互換性

  • デスクトップ:FirefoxChrome[Safari 9+][Edge 12+][IE 11-] 以前はすべて非対応)

  • モバイル:[iOS 9.0+][Android 4.4+]

P.S. 詳細は Can I use を参照してください。

モバイル環境ではほぼ安心して使用できます。仮に @supports をサポートしていなくても、実質的な影響はありません(単にそのスタイルセットが無視されるだけで、レガシーな環境と同じ挙動になります)。

特筆すべき点として、いくつか注意が必要です:

  • feature query は、バグのある機能実装や、不完全な機能実装(例えば、あるメカニズムをサポートしていないが、プロパティ名や値からは区別できない場合など)を識別するのには役立ちません。

  • feature query 自体の互換性の問題により、一部のシナリオで期待通りに動作しないことがあります(例えば、ある機能をサポートしているにもかかわらず、@supports に対応していないために無視されるなど)。しかし、深刻な影響を及ぼすことはありません。

典型的な例は Safari 8 です。flexbox をサポートしていますが、feature query をサポートしていないため、バッドケースが発生します:

フィーチャークエリに関しては、Internet Explorer よりも Safari 8 が最大の懸念点となる可能性があります。Safari 8 がサポートしている Flexbox のような新しいプロパティはたくさんあります。これらのプロパティから Safari 8 を除外したくはないでしょう。

例えば:

body {
  background-color: red;
}

@supports (display: flex) {
  body {
    display: flex;
    background-color: green;
  }
}

Safari 8 では、flexbox をサポートしているにもかかわらず、フォールバック用のスタイルが表示されてしまいます。

6. 応用シナリオ

応用シナリオとしては、feature query は新機能の互換性に関する不安を解消し、漸進的拡張の手段として利用されます。一般的な使い方は以下の通りです:

/* 互換性の信頼性が高いスタイル:アクセシビリティを確保する */

@supports (/* 互換性がそれほど信頼できない新機能 */) {
  /* 拡張:サポートされている場合、よりシンプルで効率的、かつ強力なソリューションを使用する */
}

漸進的拡張は、複数の環境における不一致を受け入れることを意味します。実際、シャドウ、���丸、アニメーションなどについては、このような不一致を容易に受け入れることができます(対応していない環境では、これらの装飾的な効果を取り除きます)。しかし、flexbox や grid などのレイアウト手法については、レイアウトは通常不可欠なものであり、単なる装飾ではないため、漸進的拡張と結びつけるのは難しいように思われます。

Grid 特性の漸進的な利用

Is There A CSS Grid Polyfill?

Grid does things that are pretty much impossible with older layout methods. So, in order to replicate Grid in browsers that don’t have support, you would need to do a lot of work in JavaScript.

残念ながら、Grid レイアウトには CSS によるポリフィルは存在しません。では、この強力な機能を使い始めるのに何年も待たなければならないのでしょうか?

もちろん、そんなことはありません。少なくとも2つの選択肢があります

  • FremyCompany/css-grid-polyfill などの JavaScript ポリフィルを使用する。

  • 漸進的に(サポートされている環境でのみ)Grid 特性を使用する(つまり、環境ごとのレイアウトの差異を受け入れる)。

JavaScript によるパッチ案については言うまでもありませんが、漸進的なアプローチにおいて重要なのは、その差異を受け入れることです:

Web サイトは、すべてのブラウザで同じように見える必要はありません。

レイアウト効果も(シャドウや角丸などの効果と同様に)拡張スタイルの一種と考え、レガシーな環境では別のフォールバック用の(レイアウト)効果を表示することを許容します。例えば:

<div class="grid">
  <div class="one">One</div>
  <div class="two">Two</div>
  <div class="three">Three</div>
</div>

対応するスタイルは以下の通りです:

* { box-sizing: border-box; }

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-rows: 100px;
  grid-gap: 20px;
}

.grid > * {
  padding: 10px;
  border: 5px solid rgba(214,129,137,.5);
  border-radius: 5px;
  background-color: rgba(233,78,119,.5);
  color: #fff;
  float: left;
  width: 33%;
}

@supports (display:grid) {
  .grid > * {
    width: auto;
  }
}

display: feature queries demo より引用)

Grid をサポートしている環境では、美しく整然とした 3 列の等比レイアウトが表示されますが、サポートされていない環境では、少し窮屈な float レイアウトにフォールバックされます:

[caption id="attachment_1785" align="alignnone" width="625"]grid-layout-css-polyfill grid-layout-css-polyfill[/caption]

十分とは言えませんが、構造を変更せず JavaScript も使用しない場合、float 案ではこの程度が限界です。もしこのような差異を受け入れることができるのであれば、feature query を通じて漸進的に利用することで、新機能の互換性の問題はもはや重要ではなくなります。

カスタムプロパティ(CSS変数)のサポート状況をチェックする

@supports (--foo: green) {
  :root {
    --theme-color: gray;
  }

  .variable {
    color: var(--theme-color);
  }
}

CSS 変数を利用すれば、テーマ変更機能を拡張効果として提供することは非常に簡単です。

ドロップキャップ(頭文字の回り込み)効果

initial-letter をサポートしている環境(Safari など)では、一般的なタイポグラフィ効果(段落の最初の文字を 4 行分下げる)を簡単に実現できます:

@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
  p::first-letter {
    -webkit-initial-letter: 4;
    initial-letter: 4;
    color: #FE742F;
    font-weight: bold;
    margin-right: .5em;
  }
}

サポートされていない場合は、通常の方法で実現します:

p::first-letter {
  float: left;
  font-size: 4em;
  color: #FE742F;
  font-weight: bold;
  margin-right: .5em;
}

P.S. その他の例については、参考資料の feature query 関連(mix-blend-mode など)を参照してください。

参考資料

コメント

コメントはまだありません

コメントを書く