CSS のコンテナクエリでは cqicqw といった、基準となるコンテナ(Query Container)の相対的なサイズを表す特別な単位が使用できます。

以前の記事「CSS コンテナクエリ考察」でも少しだけ触れていますが、今回は、このコンテナクエリの単位の基本的な説明に加え、リキッドレイアウト、アスペクト比の保持、Masonry ライクなレイアウトの 3 つの活用方法をとおして、その可能性を考えていきます。

コンテナクエリの単位

見出し「コンテナクエリの単位」

コンテナクエリで使用できる特別な単位(Container Query Length Units)は以下のとおりです。

単位内容
cqw基準コンテナの幅の 1%
cqh基準コンテナの高さの 1%
cqi基準コンテナのインラインサイズの 1%
cqb基準コンテナのブロックサイズの 1%
cqmincqicqb の小さい方の値
cqmaxcqicqb の大きい方の値

例えば 30cqi であれば、基準となるコンテナの横幅 30% を意味します。

cqi は論理プロパティなので、書字方向に応じて水平・垂直方向が変わりますが、この場合は横書き(writing-mode: horizontal-tb)を前提としています。

参照されるサイズはもっとも近いコンテナ要素です。祖先要素をさかのぼっても container-type が指定されている要素(基準コンテナ)が見つからない場合には、最終的に小さいビューポート(sv*)が参照されます。

子要素の幅に 30cqi を指定する例
<!-- HTML -->
<div class="container">
  <div class="child">
    <!-- -->
  </div>
</div>

<!-- CSS -->
<style>
.container {
  container-type: inline-size;
}
.child {
  inline-size: 30cqi;
}
</style>

以下は、それぞれ 30cqi60cqi90cqi を指定したデモです。

Live Demo
30cqi
60cqi
90cqi

このように、@container を使用しない場合であっても、container-type を指定した基準コンテナがあれば、cqi といった単位は有効です。

しかし、上記の例であればパーセント単位(%)でも実現できるので、あまり恩恵は感じられません。もう少し別の使い方を探っていきます。

リキッドレイアウトを実現するには、ビューポートの単位(vw)を使用することが一般的ですが、cqicqw を使用することで、コンポーネントのサイズを基準としたリキッドレイアウトが可能になります。

まずは、ビューポートの単位 vw を使用した実装を見ていきます。

例えば、デザインカンプの幅が 375px で、要素の内側の余白が 20px、フォントサイズが 16px であれば、リキッドレイアウトの指定は以下のようになります。

vw を使用したリキッドレイアウトの例
.fluid {
  padding: calc(20 / 375 * 100vw);
  font-size: calc(16 / 375 * 100vw);
}

別のアプローチとしては、:root に指定した基準となる変数を指定する方法が考えられます。変数に値や計算式を格納することで、それ以降の指定がシンプルになります。

CSS 変数をルート要素に指定したリキッドレイアウトの例
:root {
  --layout-width: 375;
  --fluid-ratio: calc(1 / var(--layout-width) * 100vw);
}
.fluid {
  padding: calc(20 * var(--fluid-ratio));
  font-size: calc(16 * var(--fluid-ratio));
}

3 番目のアプローチとして、:rootfont-size10px 相当で指定し、各要素では rem でサイズを指定する方法も考えられます。

rem を使用したリキッドレイアウトの例(非推奨)
/* 🚨 この方法はアクセシビリティ上の問題があります */
:root {
  --layout-width: 375;
  font-size: calc(10 / var(--layout-width) * 100vw);
}
.fluid {
  padding: 2rem;
  font-size: 1.6rem;
}

先ほどよりさらにコードがシンプルになり、メンテナンス性が向上するのですが、ルート要素のフォントサイズを vw(ビューポート幅)で指定した場合、ブラウザ側でフォントサイズの変更ができなくなるという、アクセシビリティ上の問題があるので避けたほうがよいでしょう。

以下に該当する WCAG 2.1 の達成基準を引用します。

達成基準 1.4.4 テキストのサイズ変更 (レベル AA):
キャプション及び文字画像を除き、テキストは、コンテンツ又は機能を損なうことなく、支援技術なしで 200% までサイズ変更できる。

達成基準 1.4.4: テキストのサイズ変更を理解する | WCAG 2.1 解説書

ここまで、従来のビューポートベースでのリキッドレイアウトの例を紹介しましたが、続いて、コンテナクエリの単位を使用した例を見ていきます。

コンテナクエリベース

見出し「コンテナクエリベース」

ビューポートベースでは、同じコンポーネントであっても、収まるレイアウトの幅に応じてスタイル調整が必要でしたが、コンテナクエリでは、基準となるコンテナのサイズに基づいたサイズが適用されるため、同一のコンポーネントをさまざまなレイアウトで使用することが可能になります。

例えば、デザインカンプにおけるカードコンポーネントの幅が 200px で、要素の内側の余白が 20px、フォントサイズが 16px であれば以下の指定が考えられます。

cqi を使用したリキッドレイアウトの例
<!-- HTML -->
<div class="card-container">
  <div class="card">
    Lorem ipsum
  </div>
</div>

<!-- CSS -->
<style>
.card-container {
  container-type: inline-size;
}
.card {
  padding: calc(20 / 200 * 100cqi);
  font-size: calc(16 / 200 * 100cqi);
}
</style>

以下はライブデモですが、スライダーを動かしてコンテナの幅を変更すると、余白(padding)やフォントサイズ(font-size)もあわせて変化します。

Live Demo
640px
Lorem ipsum, dolor sit amet consectetur adipisicing elit.

なお、値を変数に格納することもできますが、:root に指定する方法とは異なり、基準となるコンテナごとに指定する必要があります。

CSS 変数をコンテナに指定したリキッドレイアウトの例
.card-container {
  --inline-size: 200;
  --fluid-ratio: calc(1 / var(--inline-size) * 100cqi);
  container-type: inline-size;
}
.card {
  padding: calc(20 * var(--fluid-ratio));
  font-size: calc(16 * var(--fluid-ratio));
}

このように、コンテナクエリベースでリキッドレイアウトを実現することができるのですが、コンポーネントの組み合わせによっては、余白やフォントサイズのバランスが悪くなる可能性もあるため、緻密な設計が求められるかもしれません。

リキッドレイアウトのメリット・デメリット

見出し「リキッドレイアウトのメリット・デメリット」

ウェブサイトにおけるリキッドレイアウトの実装については、以下のメリット(Pros)と、デメリット(Cons)が考えられます。

  • 拡大・縮小してもデザインカンプに近い印象を与えられる
  • テキストの折り返し位置が変わらない
  • 水平スクロールバーが発生しづらい
  • 各デザインカンプの溝(中間サイズのレイアウト)を補完できる

リサイズしたときのレイアウトの変化が流麗であるという点も挙げられますが、ユーザが利用した際に気づく可能性は低いかもしれません。

  • 実装や検証のコストが増加する
  • コードが複雑になるためメンテナンス性が下がる
  • テキストの可読性が損なわれ、アクセシビリティ上の問題が発生する可能性がある 1
  • 絶対値(px)を指定した要素と競合してレイアウトが破綻する可能性がある

コンテナクエリベースでは、前述したように、コンポーネントの組み合わせによるバランス調整の難しさも挙げられます。

最終的にはウェブサイトの目的次第ですが、導入する際にはこれらの点を踏まえて慎重に検討したほうがよいでしょう。

cqicqw は、もう一方の軸である block-sizeheight に指定できるため、要素のアスペクト比(縦横比)を保持することができます。

例えば 16:9 のカードコンポーネントであれば、以下のようなコードになります。

16:9 のコンポーネントの例
.card-container {
  container-type: inline-size;
}
.card-item {
  block-size: calc(9 / 16 * 100cqi);
}

アスペクト比の保持は、当然ながら aspect-ratio を使用しても実現できますが、cqi では min-*max-* が使用できるという利点があります。

以下は、CSS グリッドを使用した 2 カラムレイアウトのコードです。

CSS グリッドを使用した 2 カラムレイアウトの例
.card-container {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  container-type: inline-size;
}

/* cqi */
.card-item {
  min-block-size: calc(9 / 16 * 50cqi);
}

/* aspect-ratio */
.card-item {
  aspect-ratio: 16 / 9;
}

この 2 つを比較すると、コンテンツがアスペクト比に収まらないとき、隣り合うグリッドの見え方が異なります。cqi では min-* で指定しているため、隣り合うカードの高さがそろいますが、aspect-ratio ではそろいません。

2 カラムレイアウトにおける `cqi` と `aspect-ratio` での見え方

以下はライブデモです。contenteditable 属性を指定しているので、内容を編集して各要素の高さに違いを出すことができます。

Live Demo
cqi を使用したデモ
Lorem ipsum
Lorem ipsum
Lorem ipsum
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laborum consectetur ipsa nobis sint. Necessitatibus, aspernatur iure? A autem ea, et suscipit ratione provident esse iure corrupti tenetur unde officiis perspiciatis!
Lorem ipsum
aspect-ratio を使用したデモ
Lorem ipsum
Lorem ipsum
Lorem ipsum
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laborum consectetur ipsa nobis sint. Necessitatibus, aspernatur iure? A autem ea, et suscipit ratione provident esse iure corrupti tenetur unde officiis perspiciatis!
Lorem ipsum

最後に、Masonry のようなレイアウト2を考えてみます。

先にライブデモを確認します。スライダーを動かしてコンテナのサイズを変更しても、レイアウトは維持されたままになります。

Live Demo
640px

以下にコードを抜粋しますが、フォントサイズ(font-size)や、グリッド間の余白(gap)を cqi で指定しているため、比率を保ったまま拡大・縮小することができます。

可変する正方形のグリッドの例
<!-- HTML -->
<div class="masonry-container">
  <div class="masonry">
    <div></div>
    <div></div>
    <div></div>
    <!-- ... -->
  </div>
</div>

<!-- CSS -->
<style>
.masonry-container {
  container-type: inline-size;
}
.masonry {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 2cqi;
}
.masonry > div {
  aspect-ratio: 1;
  font-size: max(12px, 2cqi);
  counter-increment: number;
}
.masonry > div:nth-child(3n) {
  grid-column: span 2;
  grid-row: span 2;
}
.masonry > div::after {
  content: counter(number);
}
</style>

正直なところ、グリッドの行や列の指定でも cqi をうまく使いたかったのですが、この例では font-sizegap に適用するにとどまりました。

ちなみに「実例で学ぶ CSS Subgrid」という記事では、10 × 10 のグリッドを作るために以下の指定をしています。このように、トラック数が明確なグリッドのスペースを確保するケースでは行や列にも cqi の単位が使えそうです。

.map-container {
  container-type: inline-size;
}
.map {
  display: grid;
  grid: repeat(10, 10cqi) / repeat(10, 10cqi);
}

この記事では、コンテナクエリの単位の活用方法を説明しました。まだまだコンテナクエリを本格的に使う機会も少ないため、もっと有用な使い方があるかもしれません。

もちろん、無理に使うものではありませんが、引き続きコンテナクエリの可能性を追求し、役立つテクニックやアイデアが見つかりましたら紹介できればと考えています。

脚注

  1. 最近のブラウザは、デフォルトの最小フォントサイズが 0 に設定されているので、どこまでも際限なく縮小してしまいます。以前は、Google Chrome が 10px、Safari が 9px でした。

  2. CSS Grid Layout Module Level 3 の Editor’s Draft では、正式な Masonry Layout が提案されています。現時点では Firefox や Safari の機能フラグをオンにした状態、もしくは Firefox Nightly や Safari Technology Preview で一部の機能を試せますが、主要ブラウザでサポートされて安心して使えるようになるには、もう少し時間がかかりそうです。