【CSS】コンテナクエリ(container size queries)について

  • URLをコピーしました!

コンテナクエリについて学習しました。これは、祖先要素を基準にしてスタイルを定義できるCSSの機能です。

例えば、祖先要素の「幅が500px以下の時」や「イタリック体の時」にその子孫要素のスタイルを自由に定義できます。

コンテナサイズクエリ(container size queries)とコンテナスタイルクエリ(container style queries)の2種類があります。

今回はコンテナサイズクエリについて書きます。

目次

基本的な書き方

index.htmlを用意しました。これにコンテナクエリを書いていきます。

<div class="parent">
  <div class="child">子要素だよ〜🍓</div>
</div>

コンテナコンテキストを生成

レスポンシブ対応したい要素の祖先要素にcontainer-typeまたはcontainerプロパティを指定します。指定された要素は、基準コンテナになり「コンテナコンテキスト(container context)」と呼ばれます。

上記index.htmlの場合、class属性parentを持つdiv要素に指定します↓

.parent {
  container-type:inline-size;
  //または、container:parent / inline-size;
}

全ての要素はデフォルトでスタイルクエリコンテナになります。

@containerを使って条件を指定

基準コンテナのサイズがどうなったらスタイルを適用するのかを定義します。
例えば、「基準コンテナの幅が500px未満の時」という条件は以下のように書きます↓

.parent {
  container-type:inline-size;
  //またはcontainer:parent / inline-size;
   max-width:400px;
}

@container (width < 500px) {
 .child {
    background-color:red;
  }
}

コンテナクエリの具体的な使用例を知りたい方はこちら↓

そもそもなぜコンテナクエリが誕生したのか知りたい方はこちら↓

使用できる記述子について

記述子とは、@container(条件) {...}の「条件」の部分です。

今回取り扱う記述子は以下です↓

  • width
  • height
  • inline-size
  • block-size
  • aspect-ratio
  • orientation

他にも記述子はあります。CSS変数や将来追加されるかもしれない状態クエリです。

詳しくは以下の記事をご覧ください↓

inline-size

writing-modeの値によって、widthまたはheightプロパティのどちらかに対応します。

  • horizontal-〇〇の場合→widthに対応
  • vertical-〇〇の場合→heightに対応

writing-modeの初期値はhorizontal-tbのため、inline-sizeの初期対応プロパティはwidthになります。

inline-sizewidthに対応する際、block-sizeheightに対応します。
反対にinline-sizeheightに対応する際、block-sizewidthに対応します。

細かく分けると以下が指定できます↓

  • inline-size
  • max-inline-size
  • min-inline-size

writing-modeについてはこちら↓

block-size

writing-modeの値によって、widthまたはheightプロパティのどちらかに対応します。

  • horizontal-〇〇の場合→heightに対応
  • vertical-〇〇の場合→widthに対応

writing-modeの初期値はhorizontal-tbのため、block-sizeの初期対応プロパティはheightになります。

細かく分けると以下が指定できます↓

  • block-size
  • max-block-size
  • min-block-size

aspect-ratio

コンテナコンテキストの幅と高さの比(アスペクト比)に基づいてスタイルを定義できます。
比とは「ある数が、もう一方の数のどれだけに当たるかを表した数」のことです。

ある要素の幅800px、高さ400pxの場合、幅と高さの比は「800:400」→「2:1」になります。幅は高さの2倍であり、高さは幅の0.5倍となります。

aspect-ratioの分子はwidth、分母はheightです。
aspect-ratio: width / height
スラッシュ( )とheightが省略された場合、heightの規定値は1です。

細かく分けると以下が指定できます↓

  • aspect-ratio
  • max-aspect-ratio
  • min-aspect-ratio

コード例です↓

@container hoge (aspect-ratio: 3/2) {
   // 3 / 2 = 1.5 親コンテナのアスペクト比が1.5と等しい時
    .hogehoge {
         //style
      }
}
@container hoge (max-aspect-ratio: 3/2) {
   // 親コンテナのアスペクト比が1.5以下の時
    .hogehoge {
         //style
      }
}
@container hoge (min-aspect-ratio: 2) {
   // 2 / 1 = 2 親コンテナのアスペクト比が2以上の時
    .hogehoge {
         //style
      }
}

orientation

コンテナコンテキストが縦長または横長に基づいてスタイルを定義します。

以下が指定できます↓

  • portrait(縦長。幅より高さの値が大きい。)
  • landscape(横長。高さより幅の値が大きい。)

各プロパティについて解説

コンテナクエリ周りの主なプロパティについて解説します。

containerプロパティ

container-namecontainer-typeを一括で指定できるプロパティです。
スラッシュ( / )で値を区切れます。

以下、構文です↓

.base-container {
 container:[container-name] / [container-type];
}

container-typeプロパティ

コンテナクエリで使用されるコンテナの種類を指定します。

コンテナの種類には以下の通りです↓

  • normal
  • inline-size
  • size

container-type:normal;

初期値です。コンテナサイズクエリのクエリコンテナではないですが、コンテナスタイルクエリのクエリコンテナとして残ります。
つまり、コンテナサイズクエリではcontainer-typeプロパティを指定する必要がありますが、コンテナスタイルクエリでは指定しなくて良いです。

コンテナスタイルクエリについてはこちら↓

container-type:inline-size;

コンテナコンテキストの幅または高さのどちらか一方のみの変更を監視して、@containerの条件に一致したとき、レスポンシブ対応したい要素のスタイルが適用されます。

条件として使える記述子には以下があります↓

  • width
  • height
  • inline-size
  • block-size

aspect-ratioorientationは幅と高さ両方の情報がいるので、inline-sizeでは使えません。

テキストだと分かりにくいと思うのでコード例を用意しました↓

See the Pen container-type=inline-size by ユイト (@mikiprogram) on CodePen.

container-typeの値を変えてみると違いが分かりそうです。

上記の解説は全く僕の主観の解釈のため、間違っている可能性が高いです。W3Cの正確な解説を引用しておきます↓

Establishes a query container for container size queries on the container’s own inline axis. Applies layout containment, style containment, and inline-size containment to the principal box.

4.1. Creating Query Containers: the container-type property

container-type:size;

コンテナコンテキストの幅と高さ両方の変更を監視して、@containerの条件に一致したときレスポンシブ対応したい要素のスタイルが適用されます。

条件として使える記述子には以下があります↓

  • container-type:inline-size;で使える記述子全て
  • aspect-ratio
  • orientation

こちらもコード例を用意しました↓

See the Pen container-type=size by ユイト (@mikiprogram) on CodePen.

こちらの解説も正確性は無いのでW3Cの解説を引用します↓

Establishes a query container for container size queries on both the inline and block axis. Applies layout containment, style containment, and size containment to the principal box.

4.1. Creating Query Containers: the container-type property

container-nameプロパティ(省略可能)

コンテナコンテキストに名前をつけることができます。

複数のコンテナコンテキストを使うときに名前をつけて区別することができます。

名前をつける際のルールを抜粋します↓

  • 名前は引用符で囲まない
  • スペース区切りで複数の名前を指定できる

名前を引用符で囲まない

🙆OK例→container-name:layout;

🙅‍♂️NG例→container-name:"layout";

スペース区切りで複数の名前を指定できる

例→container-name:width height;

用途がよく理解できていないので、MDNのサンプルコードを載せます↓

.base-container { 
  container-type: inline-size;
  container-name: meta card;
}

@container meta (max-width: 500px) {
  p {
    visibility: hidden;
  }
}

@container card (max-height: 200px) {
  h2 {
    font-size: 1.5em;
  }
}

使える単位

クエリコンテナ(基準にした要素)ベースの単位が使えるようになりました↓

  • cqw: クエリコンテナのwidthの1%
  • cqh: クエリコンテナのheightの1%
  • cqi: クエリコンテナのinline-sizeの1%
  • cqb: クエリコンテナのblock-sizeの1%
  • cqmin: cqicqbの値を比べて、どちらか小さい方
  • cqmax:cqicqbの値を比べて、どちらか大きい方

使い方

「クエリコンテナの幅が1000px」 で子孫要素に60cqw を指定した場合は以下のようになります。

<style>
  * {
    box-sizing: border-box;
  }

  .container {
    container-type: inline-size;
    border: 1px solid;
    height: 300px;
    max-width: 1000px;
  }

  .inner {
    padding: 1rem;
    margin: 1rem;
    border: 1px solid;
    width: 60cqw;
  }
</style>
<div class="container">
  1000px
  <div class="inner">60cqw</div>
</div>

コンテナコンテキストを生成して、その子孫要素にクエリコンテナベースの単位を使います。

コンテナコンテキストを生成せず、クエリコンテナベースの単位を使うと、スモールビューポートサイズ(Small Viewport Size)の大きさになります↓

For each element, container query length units are evaluated as container size queries on the relevant axis (or axes) described by the unit. The query container for each axis is the nearest ancestor container that accepts container size queries on that axis. If no eligible query container is available, then use the small viewport size for that axis.

6. Container Relative Lengths: the cqw, cqh, cqi, cqb, cqmin, cqmax units

上記index.htmlの.containercontainer-typeを指定しなかった場合、.innerwidth:60cqw;は画面幅基準になります。画面幅が2000pxの時、.innerwidthは「2000px * 0.6 = 1200px」になります。

スモールビューポートサイズについてはこちら↓

%との違い

「%」とは、親要素のサイズが基準の単位です。

先ほどのindex.htmlでは、%とクエリコンテナベースの単位の違いが分かりにくいかもしれません。

以下を見てください↓

See the Pen Untitled by ユイト (@mikiprogram) on CodePen.

width:60%;を指定した.percentの値は、親要素である.containerの幅基準で値が決まっています。

一方、width:60cqw;を指定した.cqwの値はクエリコンテナである.base-containerの幅基準で値が決まっています。

%は親要素のみが基準となりますが、クエリコンテナベースの単位は親要素を含む祖先要素を基準にできます。

注意点

擬似要素はコンテナコンテキストにできない

::before::afterなどの擬似要素は@container{...}内でスタイルを定義することは可能ですが、コンテナコンテキストにすることはできません。

🙆OK例↓

<style>
  #container {
    width: 100px;
    container-type: inline-size;
  }
  @container (inline-size < 150px) {
    #inner::before {
      content: "BEFORE";
    }
  }
</style>
<div id=container>
  <span id=inner></span>
</div>

🙅NG例↓

<style>
  #container::before {
    display:block;
    content:"";
    width: 100px;
    height:100px;
    container-type: inline-size;
  }
  @container (inline-size < 150px) {
    #inner {
     background-color:red; /*背景色は赤色にならない*/
    }
  }
</style>
<div id=container>
  <span id=inner></span>
</div>

NG.htmlのHTML構造↓

<div id="container">
  #container::before
  <span id="inner"></span>
</div>

#container::before#inner と兄弟の関係のため祖先ではありません。そのため、コンテナコンテキストになりません。擬似要素がレスポンシブ対応したい要素の祖先になることがないのかもしれません?

親要素以外もコンテナコンテキストにできる

親要素のみコンテナコンテキストできるわけではありません。

レスポンシブ対応したい要素から見て祖先に当たる要素にcontainer-typeを指定するとそこがコンテナコンテキストとなります↓

<style>
  .ancestor {
    container-type: inline-size;
    width: 100px;
  }

  .parent {
    width: 160px;
  }

  @container (inline-size < 150px) {
    .inner::before {
      content: "BEFORE";
    }
  }
</style>
<div class="ancestor">
  <div class="parent">
    <span class="inner"></span>
  </div>
</div>

最も近い祖先要素がコンテナコンテキストとなる

複数の祖先要素にcontainer-typeが指定されている場合を考えます。

この場合、レスポンシブ対応したい要素から見て最も近い祖先要素がコンテナコンテキストになります。

<style>
  .container {
    container-type: inline-size;
  }

  .ancestor {
    width: 100px;
  }

  .parent {
    width: 160px;
  }

  @container (inline-size < 150px) {
    /*基準コンテナの幅が150px未満の時*/
    .inner::before {
      content: "BEFORE"; 
    }
  }
</style>
<div class="container ancestor">
  <div class="container parent">
    <span class="inner"></span>
  </div>
</div>

上記の場合、.inner::before content: "BEFORE";は表示されません。つまり、コンテナコンテキストはclass属性containerparentを持つdiv要素です。

position:fixed;の基準位置と固定表示

position:fixed;を指定したときのtopleftなどの値は、常にビューポートが基準位置だと思っていました。

しかし、position:fixedを指定する要素から見て先祖要素のcontainer-typenormal以外の場合、基準位置が変わることを学びました↓

container-type以外にも基準位置を変えるプロパティは存在します。
詳しくは、CSS Positioned Layout Module Level 3を確認してください。

また、固定表示して追従されなくなるので注意が必要です↓

Safariでhtml要素のcontainer-typeが働かない

Safariでhtml要素にcontainer-typeを指定しても、うまくいかないようです↓

各ブラウザの対応状況

2023年4月10日時点で、主要なブラウザはサポートされています↓

参考サイト

まとめ

コンテナクエリの基本的なことについては理解できてよかったです。具体的なデモを見ていつでも使えるようにこれから勉強します。

コンテクエリ周りの単位やスタイルクエリについても勉強します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次