ブラウザの横幅などに応じて応じて異なるスタイルを適用するのが メディアクエリ ですが、画面内の矩形領域(コンテナ)の横幅等に応じて異なるスタイルを適用するのが コンテナクエリ です。
コンテナの container-type に inline-size または size を指定し、@container で異なるスタイルを指定する条件とスタイルを指定します。下記の例では通常コンテナ(#c1)内のテキストは赤色ですが、横幅が600px以上のコンテナ(#c2)内のテキストは青色になります。
.container { container-type: inline-size; border: 1px solid #ccc; padding: .2rem; margin-bottom: .2rem; } #c1 { width: 700px; } #c2 { width: 500px; } .text { color: red; } @container (width > 600px) { .text { color: blue; } }
<div id="c1" class="container"> <div class="text">Card Title</div> </div> <div id="c2" class="container"> <div class="text">Card Title</div> </div>
通常は最も近い親コンテナをルールの対象としますが、container-name でコンテナ名を付けてどの要素をルールの対象とするか指定することができます。下記の例では、最も近い親 (.my-sub-container) ではなく名前指定した .my-container の横幅に応じてスタイルが変わります。
.my-container { resize: horizontal; border: 1px solid #ccc; padding: 1rem; overflow: hidden; container-name: my-container; container-type: inline-size; } .my-sub-container { resize: horizontal; border: 1px solid #ccc; padding: 1rem; overflow: hidden; } @container my-container ( width < 400px ) { .my-target { color: red; } }
<div class="my-container"> <div class="my-sub-container"> <div class="my-target"> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA </div> </div> </div>
container でコンテナ名とコンテナタイプを同時に指定することができます。
.container { /* container-name: my-card; */ /* container-type: inline-size; */ container: my-card / inline-size; }
コンテナクエリ内では下記の長さの単位を使用できます。
クエリに and, or, not を使用することができます。
@container my-card (600px < width) and (width < 1000px) { ... } @container my-card (width < 600px) or (1000px < width) { ... } @container my-card not (width < 600px) { ... }
Chrome 133 でスクロール状態によるクエリがサポートされました。container-type には scroll-state を指定します。詳細は CSS scroll-state()(↗) を参照してください。
@container scroll-state(state) { ... } state = stuck: stuck-params | snapped: snap-params | scrollable: scrollable-params stuck-params = none | top | right | bottom | left | block-start | inline-start | block-end | inline-end snap-params = none | x | y | block | inline scrollable-params = none | top | right | bottom | left | block-start | inline-start | block-end | inline-end | x | y | block | inline
下記の例では stuck を用いて、h1 要素がページ上部にスタックされた時に背景色を変更しています。
.container { width: 400px; height: 400px; border: 1px solid #ccc; overflow-y: auto; .stuck-top { container-type: scroll-state; position: sticky; top: 0px; margin-top: 100px; h1 { @container scroll-state(stuck: top) { background-color: #ddd; } } } }
<div class="container"> <div class="stuck-top"> <h1>Lorem ipsum</h1> </div> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. ...</p> ... </div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. Deleniti exercitationem veritatis, voluptatem ratione quis quia rerum corrupti explicabo aperiam nisi ullam voluptate neque quasi culpa ducimus perferendis vel.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. Deleniti exercitationem veritatis, voluptatem ratione quis quia rerum corrupti explicabo aperiam nisi ullam voluptate neque quasi culpa ducimus perferendis vel.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. Deleniti exercitationem veritatis, voluptatem ratione quis quia rerum corrupti explicabo aperiam nisi ullam voluptate neque quasi culpa ducimus perferendis vel.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. Deleniti exercitationem veritatis, voluptatem ratione quis quia rerum corrupti explicabo aperiam nisi ullam voluptate neque quasi culpa ducimus perferendis vel.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. Deleniti exercitationem veritatis, voluptatem ratione quis quia rerum corrupti explicabo aperiam nisi ullam voluptate neque quasi culpa ducimus perferendis vel.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. Deleniti exercitationem veritatis, voluptatem ratione quis quia rerum corrupti explicabo aperiam nisi ullam voluptate neque quasi culpa ducimus perferendis vel.
下記の例ではスナップされているタイトルに対して色を黒にするスタイルを適用しています。
.snap-container { height: 10rem; width: 20rem; overflow-y: scroll; scroll-snap-type: y mandatory; border: 1px solid #ccc; .snap-content { scroll-snap-align: start; container-type: scroll-state; h1 { color: #ccc; } @container scroll-state(snapped: y) { h1 { color: #000; } } } }
<div class="snap-container"> <section class="snap-content"> <h1>Section-1</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...</p> </section> <section class="snap-content"> <h1>Section-2</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...</p> </section> <section class="snap-content"> <h1>Section-3</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...</p> </section> <section class="snap-content"> <h1>Section-4</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...</p> </section> </div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam, sequi. ...
下記の例では上方向にスクロールが可能な時に、画面の右下に [↑] ボタンを表示します。
.scroll-container { width: 400px; height: 400px; border: 1px solid #ccc; overflow-y: auto; container-type: scroll-state; } .top-button { position: fixed; bottom: 8px; right: 8px; margin: 4px; padding: 8px 16px; background-color: #333; color: #fff; border: none; border-radius: 5px; cursor: pointer; transform: scaleY(0); transition: .3s; } @container scroll-state(scrollable: top) { .top-button { transform: scaleY(1.0); } }
<div class="scroll-container"> <button class="top-button" onclick="this.parentElement.scroll({top:0, behavior:'smooth'})">↑</button> <div class="scroll-content"> <div class="h1">Lorem ipsum</div> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit...</p> </div> </div>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Adipisci amet, neque accusamus, voluptas labore eveniet consequatur eum non harum molestiae doloremque ipsam a tempora. Ratione impedit dolorem quia cumque quasi.