container
コンテナにした親要素の幅に応じて、中の要素のスタイルを切り替えるアットルール。 ResizeObserverで幅を監視してクラスを付けていた処理を、CSSだけで書けます。
今の書き方
ラッパーに名前を付けてコンテナにし、その幅でスタイルを切り替えます。 画面幅ではなく置かれた場所の幅で決まるため、サイドバーでもメインでも同じ部品をそのまま使えます。
.card-wrapper {
container: card / inline-size;
}
@container card (width >= 400px) {
.card {
display: grid;
grid-template-columns: 120px 1fr;
}
}
<div class="card-wrapper">
<article class="card">
<img src="thumbnail.jpg" alt="" />
<div>...</div>
</article>
</div>以前の書き方
メディアクエリは画面幅しか分からないため、置かれた場所の幅で切り替えるにはJavaScriptが必要でした。
ResizeObserverでラッパーの幅を監視し、400px以上ならカードにクラスを付けます。
.card.is-wide {
display: grid;
grid-template-columns: 120px 1fr;
}
<div class="card-wrapper">
<article class="card">
<img src="thumbnail.jpg" alt="" />
<div>...</div>
</article>
</div>
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const card = entry.target.querySelector('.card')
card.classList.toggle('is-wide', entry.contentRect.width >= 400)
}
})
for (const wrapper of document.querySelectorAll('.card-wrapper')) {
observer.observe(wrapper)
}置き換えるときの注意点
- コンテナ自身には、
@containerのスタイルは当たりません。 切り替えたい要素の外側にラッパーを置き、ラッパーをコンテナにします。 inline-sizeを指定した要素は、中身から幅が決まらなくなります。inline-blockや、中身の幅で大きさが決まるFlexboxの子要素に指定すると、幅が0になります。 ブロック要素のように、幅が外側から決まる要素に指定します。container-type: sizeを指定すると、高さも中身から決まらなくなります。 高さを指定していない要素には、inline-sizeを使います。- 名前のない
@containerは、いちばん近いコンテナを見ます。 コンテナが入れ子になると意図しない幅で切り替わるため、名前を付けて対象を決めておきます。 - コンテナの幅を基準にした単位(
cqiなど)も使えます。 文字の大きさや余白を、置かれた場所の幅に合わせられます。
フォールバック
対象の5ブラウザはすべて対応済みなので、通常は以前の書き方を残す必要はありません。 狭いときのスタイル(縦並び)を基本にしておけば、対応していないブラウザでは縦並びのまま表示されます。 対応していないブラウザでも横並びにする必要がある場合だけ、以前の書き方のCSSを残し、JavaScriptを対応していないブラウザでだけ動かします。
// コンテナクエリに対応していないブラウザでだけ、以前の書き方のクラスを付ける
if (!CSS.supports('container-type', 'inline-size')) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const card = entry.target.querySelector('.card')
card.classList.toggle('is-wide', entry.contentRect.width >= 400)
}
})
for (const wrapper of document.querySelectorAll('.card-wrapper')) {
observer.observe(wrapper)
}
}Browser Support
Chrome
v105+2022-09-02Edge
v105+2022-09-01Safari
v16+2022-09-12Safari (iOS)
v16+2022-09-12Firefox
v110+2023-02-14