CSS コンテナクエリの準備はできてる? 安定版のブラウザにサポート、ポリフィルも大幅にアップデートされました
Post on:2022年10月6日
コンテナクエリは今までのメディアクエリのスクリーンベースではなく、親コンテナをベースにして子要素のスタイルを定義できます。レイアウトやコンポーネントをはじめ、レスポンシブ対応のフォントサイズにも大活躍する新機能です。
そんなコンテナクエリが安定版のブラウザにサポートされ、ポリフィルも大幅にアップデートされたので、基礎知識、基本的な使い方、便利な新単位、ポリフィルの使い方と注意事項を紹介します。
CSSの新機能でネックになるのが、ブラウザのサポート。しかし、CSSの新機能コンテナクエリは、違います。サポートされていないブラウザ用にポリフィルも同時に開発されており、2年くらい前のブラウザでもサポートされています。
Container queries begin to land in stable browsers while the polyfill gets a big update
by Una Kravets, Gerald Monaco
下記は各ポイントを意訳したものです。
※元サイト様のライセンスに基づいて翻訳しています。
- コンテナクエリが登場!
- コンテナクエリとは
- コンテナクエリの使い方
- コンテナクエリの新しい単位
- コンテナクエリのポリフィル
- コンテナクエリのポリフィルの使い方
- 新しいポリフィルの機能
- ポリフィルの制限と注意事項
コンテナクエリが登場!
これはエキサイティングなニュースです!
デベロッパーからの要望がもっとも高かった機能「コンテナクエリ」が、ブラウザに実装されました!
2022年10月現在、Chrome 105+(Edgeも105+)とSafari 16でサイズベースのコンテナクエリが使用でき、コンテナクエリの単位も使用できます。
また、ChromeのAuroraチームはコンテナクエリのポリフィルのアップデートに尽力しており、より多くのブラウザとユースケースをサポートできるようなりました。
参考: CSSの新機能コンテナクエリのポリフィルがこれほど使いやすく、Googleから提供されたことは素晴らしい
コンテナクエリはCSS Containment Module Level 3の一部です。この仕様には新しい単位値とサイズとスタイルの両方のコンテナタイプがカバーされていますが、ブラウザではサイズのみのサポートから開始されています。そのため、この記事ではサイズのコンテナにフォーカスをあてています。
コンテナクエリとは
コンテナクエリとは、親要素をターゲットにして子要素のスタイルを定義できるCSSの新機能です。
たとえば、親要素のサイズに応じて、コンポーネントベースのレスポンシブデザインを実装できます。今まではビューポートのサイズ情報のみを参照するメディアクエリでしたが、コンテナクエリははるかに詳細で有用です。
左: メディアクエリ、右: コンテナクエリ
コンテナクエリを使用すると、再利用可能なコンポーネントを作成でき、ページ内の配置場所に応じて異なるスタイルで表示できます。これにより、ページやテンプレートの種類によらず、レスポンシブ性の高いコンポーネントを作成できます。
【訳者注】
コンテンツ用とサイドバー用のカード、コンテンツ用とサイドバー用の検索ボックスなど、配置場所に応じて異なるスタイルで表示できます。
コンテナクエリの使い方
たとえば、下記のようなHTMLがあるとします。
1 2 3 4 5 6 7 |
<!-- カードの親 --> <div class=”card-parent”> <div class=”card> <!-- カードのコンテンツ --> … </div> </div> |
コンテナクエリを使用するには、最初にターゲットにする親要素にコンテナを設定します。container-type
プロパティを記述するか、container
プロパティというショートハンドを記述します。
1 2 3 4 |
.card-parent { /* 親のインライン方向のサイズをクエリ */ container-type: inline-size; } |
次に、@container
ルールを使用して、もっとも近い親に基づいてスタイルを設定できます。上記の画像のように、カードを1カラムから2カラムになるデザインの場合、下記のように定義します。
1 2 3 4 5 6 7 |
@container (min-width: 300px) { .card { /* カードコンテナ(.card-parent)が、>= 300pxのときに適用されるスタイル */ /* つまり、1カラムから2カラムのレイアウトに変更 */ grid-template-columns: 1fr 1fr; } } |
親要素のコンテナに名前をつけて、より明示的にします。
1 2 3 4 5 |
.card-parent { container-type: inline-size; /* 名前はcontainer-nameで定義するか、containerショートハンドで定義 */ container-name: card-container; } |
container
プロパティのショートハンドで定義すると、下記のようになります。
1 2 3 4 |
.card-parent { /* ショートハンドを使用すると、1行で定義できます */ container: card-container / inline-size; } |
そして、先ほどのCSSを下記のように書き直します。
1 2 3 4 5 |
@container card-container (min-width: 300px) { .card { grid-template-columns: 1fr 1fr; } } |
これで親要素のサイズに応じて、1カラムと2カラムのカードが実装できます。
真ん中のカードのサイズが変わることに注目してください。
実際の動作は、デモページでご覧ください。
See the Pen
Simplified CQ Card Demo in Grid - Polyfill by web.dev (@web-dot-dev)
on CodePen.
コンテナクエリの新しい単位
コンテナクエリをさらに便利にするために、コンテナベースの単位も使用できます。下記はコンテナ単位とそれらがどのようにコンテナのサイズに対応しているかです。
cqw
: クエリ コンテナの幅の 1%cqh
: クエリ コンテナの高さの 1%cqi
: クエリ コンテナのインラインサイズの 1%cqb
: クエリ コンテナのブロックサイズの 1%cqmin
:cqi
またはcqb
の小さい方の値cqmax
:cqi
またはcqb
の大きい方の値
コンテナベースの単位をどのように使用するかの一例が、レスポンシブ対応のフォントサイズです。
1 2 3 |
.card h2 { font-size: 15cqi; } |
上記のCSSでは、font-size
をコンテナのインラインサイズの15%に定義しています。つまり、インラインサイズ(幅)が大きくなるとフォントサイズも大きくなり、小さくなるとフォントサイズも小さくなります。
さらに、clamp()
関数を使用すると、最小サイズと最大サイズを設定でき、コンテナのサイズに応じてレスポンシブにフォントサイズを変更できます。
1 2 3 |
.card h2 { font-size: clamp(1.5rem, 15cqi, 3rem); } |
参考: CSSの比較関数が便利すぎる!min(), max(), clamp()の使い方を詳しく解説
このCSSで、ヘッダのフォントサイズが3rem
より大きくなったり、0.5rem
より小さくなったりすることはありません。そして、その間のフォントサイズはコンテナのインラインサイズの15%が適用されます。
実際の動作は、デモページでご覧ください。
See the Pen
CQ Card with Responsive Type - Polyfill by web.dev (@web-dot-dev)
on CodePen.
このデモではさらに一歩進んで、2カラムで表示されるように幅の広いカードをより小さいサイズ範囲にしています。
コンテナクエリのポリフィル
コンテナクエリは非常にパワフルで魅力的な機能であるため、プロジェクトで安心して使用するには、ブラウザのサポートが大きな役割を占めていることを理解しています。そのため、わたし達はコンテナクエリのポリフィルにも取り組んできました。ポリフィルは、以下のブラウザをサポートしています。
- Firefox 69+
- Chrome 79+
- Edge 79+
- Safari 13.4+
参考: CSSの新機能コンテナクエリのポリフィルがこれほど使いやすく、Googleから提供されたことは素晴らしい
圧縮時のサイズは9Kb以下、MutationObserverでResizeObserverを使用して、現在安定版のブラウザで利用できる@container
のクエリ構文を完全にサポートしています。
- 個別クエリ (
width: 300px
,min-width: 300px
など) - 範囲クエリ(
200px < width < 400px
,width < 400px
など) - コンテナベースの単位(
cqw
,cqh
,cqi
,cqb
,cqmin
,cqmax
)
コンテナクエリのポリフィルの使い方
コンテナクエリのポリフィルを使用するのは、簡単です。
下記のscript
タグをhead
内に記述するだけです。
1 2 3 4 5 |
<script type="module"> if (!("container" in document.documentElement.style)) { import("https://unpkg.com/container-query-polyfill@^0.2.0"); } </script> |
また、User-Agent
に基づいて条件付きでポリフィルを配信するサービスを利用したり、独自にポリフィルをセルフホスティングすることも可能です。
すべてのブラウザでコンテナクエリのサポートがリリースされているか、ロードマップに記載されているので、ポリフィルを他のコードにバンドルすることは避けることをお勧めします。
最高のユーザーエクスペリエンスを得るために、最初はスクロールせずに見えるコンテンツでポリフィルを使用し、@supports
クエリでポリフィルがロードされる準備が整うまで一時的にローディングのインジケーターに置き換えることをお勧めします。
高速なネットワークとデバイス、またはコンテナクエリをネイティブにサポートするデバイスでは、このローディングのインジケータが表示されることはありません。
1 2 3 4 5 6 7 8 9 10 |
@supports not (container-type: inline-size) { .container, footer { display: none; } .loader { display: flex; } } |
このテクニックは、ロード時のジャンクを減らすためにLCPを効果的にトレードオフするので、特にローエンドのデバイスでは結果的に前者のリグレッションが発生する可能性があります。
新しいポリフィルの機能
2022/9にアップデートされたポリフィルは、以下をサポートします。
- ネストされた
@container
ルール。 @supports
と@media
クエリの下での@container
ルールのネスト、およびその逆に入れ子もサポートされています。@supports (container-type: inline-size)
のような条件付きCSSはポリフィルがロードされた後に渡されます。- CSSの構文を完全サポート(構文上有効なコメントをどこに書いても問題はありません)。
- 縦書きモードをサポート(
writing-mode
経由)。 - コンテナ相対単位(
cqw
,cqh
など)はクエリ条件、プロパティ宣言、アニメーションキーフレームでサポートされています。拡張コンテナクエリ構文は、- 範囲指定構文(
(200px < width < 400px)
など) - 等式クエリ(
width = 200px
など)
- 範囲指定構文(
::before
,::after
などの疑似要素をサポート。:is(...)
,:where(...)
がないブラウザは、オプションの回避策でサポートされます。orientation
,aspect-ratio
もサポートされています。- 機能に基づくクエリの正しいフィルタリング (たとえば、
container: inline-size
のheight
クエリは、横書きモードでは正しく許可されません)。 - DOMの変更(たとえば、
<style>
や<link>
要素が実行時に削除される)。
アップデートされたポリフィルでは、size()
でサイズクエリを囲む必要があるレガシーなサイズクエリ構文(たとえば、size(width >= 200px)
)をサポートしなくなりました。
ポリフィルの制限と注意事項
コンテナクエリのポリフィルを使用する場合、注意すべき点がいくつかあります。
ポリフィルの制限
- Shadow DOMはまだサポートされていません。
- コンテナ相対単位(
cqw
,cqh
など)は、@media
クエリ条件ではまだサポートされていません。- Safari: コンテナ相対単位は、15.4より前のアニメーションキーフレームではサポートされていません。
calc()
,min()
,max()
またはその他の数学関数は、クエリ条件ではまだサポートされていません。- このポリフィルは、インラインおよび同一オリジンのCSSに対してのみ機能します。クロスオリジンのCSSやiframe内のCSSは (ポリフィルが手動で読み込まれていない限り) サポートされません。
layout
とstyle
の保持には、基本的なブラウザのサポートが必要です。- Safari 15.4+
- Firefoxは現時点ではスタイル コンテインメントをサポートしていませんが、現在取り組んでいます。
ポリフィルの注意事項
- FIDとCLSへの影響を防ぐために、ポリフィルはたとえ同期的にロードされたとしても、最初のレイアウトがいつ発生するかについては、LCPを不当に遅らせることを避けようとする以外、何の保証もしません。言い換えれば、最初のペイントについては依存しないでください。
ResizeObserver Loop Errors
を生成します。オリジナルのポリフィルでもこのようなことはありましたが、注意が必要です。これはcontainer-type: inline-size
のblock-size
がクエリの評価後に変更される可能性が高いために発生しますが、ResizeObserver
にはblock-size
の変更を気にしないことを伝える方法がありません。- このポリフィルは、Web Platform Testsでテストを行い、70%の合格率を達成しました。これはJavaScript APIなどの一部の機能がポリフィルされていないためで、合格率は意図的に70%に近づけています。
- 2.23%のより古いブラウザには、
:where()
の回避策が必要です。- Safari 14
- Chromium 88
- Edge 88
- Samsung Internet 15
- Firefox 78
sponsors