CSSのコンテナクエリでvwが使いにくい問題を解決しようとした話

コンテナクエリ、使ってますか?

メディアクエリがウェブページのビューポート (表示領域) を基準とするのに対し、コンテナクエリはコンテナに設定した祖先要素 (コンテナ要素) の幅を基準として、@container による条件分岐や、cqw, cqh, cqi, cqb といった独自の単位が使えます。

今回は、幅の単位である「vw」や「vi」を「cqw」や「cqi」に置き換えて幸せになれると思ったら、幸せになれなかったという出来事をシェアしたいと思います。

「vw」や「vi」が使いにくい理由

「vw」や「vi」といったビューポートを基準とした単位には、スクロールバーの幅も含まれるため、安易に inline-size: 100vi; などと設定した場合、垂直スクロールバーの幅だけ水平スクロールが発生してしまう場合があります。

その一方、親要素の幅を基準とする「%」とは異なり、要素がどのような入れ子になっていたとしても、ビューポートとの相対的な幅が得られるので、使い方によっては非常に便利な単位ではあります。

スクロールバー問題の解決に「cqw」や「cqi」を使ってみる

「vw」や「vi」がビューポートの幅を100とした相対的な単位であるのに対し、「cqw」や「cqi」は直近のコンテナ要素の幅を100とした相対的な単位です。

「vw」や「vi」と同様に、要素がどのような入れ子になっていたとしても、直近のコンテナ要素との相対的な幅を得ることができます。また、overflow: scroll;overflow: auto; が設定されていない祖先要素をコンテナ要素とすることで、スクロールバー問題もなくなるので、幸せになれるはずです。

特定の要素をコンテナ要素にするには、container-type プロパティか、container-type プロパティと container-name プロパティのショートハンドプロパティである container プロパティを設定するだけです。

/* 個別に設定する場合 */
body {
  container-name: body;
  container-type: inline-size;
}

/* ショートハンドで設定する場合 */
body {
  container: body / inline-size;
}

 container-name プロパティで設定した名称は、@container による条件設定で使用できます。ただし、コンテナ要素自体は設定の適用対象にはならないので注意が必要です。

@container body (inline-size > 960px) {
  /* body 要素の横幅が 960px 超過の場合 */
  body > * {
    /* コンテナ要素の子孫要素が適用対象になるため、このセレクタは有効です。 */
  }
}
@container body (inline-size < 960px) {
  /* body 要素の横幅が 960px 未満の場合 */
  body {
    /* コンテナ要素自体は適用対象にならないため、このセレクタは無効です。 */
  }
}

関係ないですが「未満」の反対は「超過」なんですよね。

WordPress の「alignfull」と「alignwide」

WordPress のブロックエディタでは、ブロックを全幅表示させる「alignfull」と、幅広表示させる「alignwide」が設定できます。

ルート要素 (div.is-root-container) 直下の要素全てに max-width プロパティと margin プロパティが設定してあり、「alignfull」や「alignwide」 が設定されると、これらのプロパティを上書きして、全幅・幅広を実現しています。

.is-root-container > * {
  margin-right: auto;
  margin-left: auto;
  max-width: var(--wp--style--global--content-size);
}
.is-root-container > .alignfull {
  width: 100%;
  max-width: none;
}
.is-root-container > .alignwide {
  max-width: var(--wp--style--global--wide-size);
}

公開されたウェブページでも、同様の手法で全幅・幅広を実現できますが、要素が深く入れ子になっている場合、祖先要素すべての幅を 100%、余白を 0 にする必要があるため、かなり面倒です。本当に面倒です。

でも、「cqi」を使えば、驚くほどに簡単です。

body {
  container: body / inline-size;
}
.alignfull {
  margin-inline: calc(50% - 50cqi);
}
.alignwide {
  margin-inline: max(50% - var(--wp--style--global--wide-size) / 2,50% - 50cqi);
}

これだけです。どんなに要素が入れ子になっていても問題ありません。

ちなみに、var(--wp--style--global--content-size)var(--wp--style--global--wide-size) は、theme.json で設定できるコンテンツサイズと幅広サイズです。

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "appearanceTools": true,
    "layout": {
      "contentSize": "768px",
      "wideSize": "1024px"
    },
    …
  },
  …
}

結局、幸せになれなかった

コンテナクエリってすごく便利ですよね? しかし、ここで残念なお知らせです。

コンテナ要素の子孫要素に設定した position: fixed;position: absolute; として扱われます。

そのため、position: fixed; で実現できる、ハンバーガーボタンで開閉するドロワーメニューや、パララックス (視差効果) のような表現手法ができなくなります。

最強なのに呪われてる剣のようでがっかりです。

CodePen

See the Pen alignfull and alignwide with container query by nov (@numerofive) on CodePen.

…がっかりです。ただただがっかりです。

関連記事