CSS Gridとメイソンリーレイアウト

CSS Grid Level 1 と Level 3 で作ったメイソンリーレイアウトの比較。詳しくは本文中で見ていきます。

Pinterest などでお馴染みの、石を積み上げたような形になる「メイソンリーレイアウト(Masonry Layout)」。CSS では CSS Grid Level 3 で実現する方法が提案されています。しかし、2020年に Firefox の実験的機能でサポートされ、2023 年初頭に Safari Technology Preview でサポートされたあと、進展のない状態が続いていました。

そこにポストされたのが、先日の WebKit の記事です。メイソンリーを CSS Grid の機能にするべきか、それとも display: masonry でメイソンリー専用の機能とするべきかが問われています。

Help us invent CSS Grid Level 3, aka "Masonry" layout | WebKit
https://webkit.org/blog/15269/help-us-invent-masonry-layouts-for-css-grid-level-3/

「そもそもメイソンリーはグリッドなのか?」という疑問から始まり、「メイソンリーの機能を含めると CSS Grid がさらに複雑になってしまう」といった危惧もあるようです。

実際、メイソンリーの機能やアルゴリズムは CSS Grid Level 3の草案 でその複雑さが垣間見れます。それに加えて先行きが不透明だったこともあり、『作って学ぶ HTML + CSS グリッドレイアウト』では取り上げるのを見送りました(CSS Grid Level 2 までであの分量になったというのもありますが…)。

とはいえ、よくあるメイソンリーレイアウトを形にするだけであれば CSS Grid の設定自体は簡単です。せっかくですので、これまでのグリッドと、メイソンリーの機能を使ったときのグリッドとで何が違うのかを確認してみます。

  • Chrome や Edge は未対応です。
  • Safari Technology Preview では標準でメイソンリーに対応していますので、何もしなくても表示を確認できます。
  • Firefoxで表示を確認する場合、 flag(実験的な機能)を有効化する必要があります。アドレスバーに about:config と入力し、layout.css.grid-template-masonry-value.enabled を true にします。
Firefoxの設定画面

これまでのグリッドとメイソンリーのグリッドの違い

これまでのグリッドの機能(CSS Grid Level 1)でメイソンリーっぽいものを作った場合

「メイソンリーレイアウトっぽいもの」はこれまでの CSS Grid の機能でも作ることができます。たとえば、grid-template-columns の repeat(auto-fill …) で画面幅に合わせて列数が変わるグリッドを用意して、画像を自動配置します。縦長の画像は「grid-row: span 3」で 3 行分を使った配置にすると、次のようなレイアウトになります。

Masonry Layout with CSS Grid Level 1 (Modular Grids)
Firefox での表示。グリッドの構造と行列のライン番号を表示しています
【CodePen】
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
img.portrait {
grid-row: span 3;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}

このコードでは行の構成(grid-template-rows)を指定していません。行は列数と画像の数に合わせて自動生成され、それによって構成されたセルに画像が自動配置されます。

当然、配置先のセルと同じサイズになる画像ばかりではありません。そのため、 画像の横幅と高さは 100%に、object-fit は cover にして、配置先に合わせたサイズで切り抜いています。画像のオリジナルの縦横比は維持されませんので、メイソンリーレイアウトとしては不完全ということになります。

ただ、行が構成する横方向のラインで画像の位置が揃い、カチッとした印象のレイアウトになりますので、Bento UI(ベントーUI)のようなレイアウトを実現するのには適しています。『作って学ぶ HTML + CSS グリッドレイアウト』でも、Bento UI のサンプルはこの方法をベースに構築しています。

行列で構成されるこのグリッドは、WebKit の記事では「Modular Grids(モジュラーグリッド)」と呼ばれています。

メイソンリーの機能(CSS Grid Level 3)で作った場合

メイソンリーレイアウトで画像を並べるためには、grid-template-rows を「masonry」と指定します。それだけで、画像は次のような形で並びます。画像は切り抜かずにオリジナルの縦横比で表示しています。span も指定していません。

Masonry Layout with CSS Grid Level 3 (Columnar Grids)
Firefox で Masonry Layout の Flag を有効にして表示したもの
【CodePen】
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
grid-template-rows: masonry;
gap: 16px;
img {
width: 100%;
height: auto;
}
}

メイソンリーの機能で作ったグリッドの大きな特徴は、「グリッドの行が構成されない」ところです。そのため、縦横比の異なる画像が隙間なく並ぶ仕組みになっています。一方で、行がありませんので横方向での画像の配置は揃いません(だがそれがいい、というやつです)。

列のみで構成されるこのグリッドは、WebKit の記事では「Columnar Grids(カラムナーグリッド)」と呼ばれています。

何が問題なのか

grid-template-rows: masonry と指定するだけでメイソンリーレイアウトになりますので、設定としてはとてもシンプルです。それだけを見ると何の問題もないのでは? と思ってしまいますが、「多数の CSS Grid の機能と整合性が取れるのか、整合性を取るために複雑になりすぎるのでは」というところが懸念されています。

たとえば、列ごとの横幅を変えた場合、固定サイズと可変サイズを混ぜた場合、画像に span を指定した場合、配置先を指定した場合、配置先が存在しない場合、justify や align との関係、ラインやエリアの扱い、position との関係、などなどなど…

CSS Grid Level 1 や Level 2 の機能でさえ大半が使いこなされているとは言い難い現状の中、Level 3 ではメイソンリー専用のプロパティも追加され、そのアルゴリズムはさらに複雑さを増そうとしています。

そのため、メイソンリーの機能を「display: masonry で独立させた方がシンプルになる」という話になるわけです。

display: masonry にすればシンプルになるけれど…

display: masonry にした場合、今度はグリッドに用意された多数の「便利な」機能は使えない、もしくは個別に実装されないと使えないということになります。

「そんな機能使わない!」で終わればよいのですが、あとから「Flexbox で gap が使えたらいいのに」となったのと同じように、「グリッドのあの機能が使えたら…」となる可能性は捨てきれません。

WebKitの記事のデモでは、一般的なメイソンリーレイアウト(均一なカラム幅で画像を並べたもの)は「クラシックなメイソンリー(Classic masonry)」とされています。そのうえで、CSS Grid の便利な機能を組み合わせたさまざまなレイアウト(カラム幅を変えたものや新聞タイプのレイアウトなど)が紹介されています。

WebKit の記事のデモより。
左から順に「クラシックなメイソンリー(Classic masonry)」、「異なる幅のカラムを混ぜたもの(Alternating narrow & wide columns)」、「新聞のレイアウトに使用したもの(Newspaper: Various size of content)」

メイソンリーが CSS Grid に組み込まれることで、メイソンリー以外の、まだ見ぬレイアウトが生まれてくる可能性があるとも言えるわけです。

GitHubでは

GitHubのissueでは CSS Grid の機能にするべきという意見が大勢となっています。Masonry JS libraryの制作者の方も

私の経験では、大多数のユーザーは均一なカラム幅を望んでいます。CSSグリッドはトラックサイジングでレイアウトに大きな力を与えますが、このような機能はメイソンリーレイアウトには不要だと思います。通常、メイソンリーレイアウトではアイテムのサイズを同じにしたいものです。ですから、実用的であることを望むのであれば、display: masonryにすることでメイソンリーレイアウトの使い方の95%を満たすことができます。

https://github.com/w3c/csswg-drafts/issues/10233#issuecomment-2070148836

としつつも、「しかしながら、CSSグリッドが確立された規約となった今、grid-template-rows: masonry はメイソンリーがどのように機能するかという私のメンタルモデルとよく合致しています」とコメントされています。

まとめ

メイソンリーを CSS Grid の一部とする考え方にも、display: masonry として独立させる考え方にも一理あり、難しい問題です。

ただ、実際にコードを書いてみて、「grid-template-rows: masonry は行のないグリッドを生成するものなんだな」と捉えてしまうと、これもまた CSS Grid の1つ… とストンと納得できてしまった自分がいます。

CSS Grid の一部になってくれたほうが最終的には覚えることも少なく済んで、レイアウトのバリエーションも広がりそうです。どうせなら、マルチカラムレイアウトも CSS Grid で実現できてもいいのでは? という気さえしてきます。

そして、個人的にはWebKitの記事で言及されている「CSS Grid Level 4」がとても気になります。

多くの開発者は、CSS Grid Level 4 がグリッドエリアやグリッドラインのスタイリングメカニズムを提供することを望んでいます。たとえば、トラックに背景色を付けたり、gap 内にライン引いたりする方法があれば良いでしょう。

まだまだ発展の余地がたくさんありそうな CSS Grid。今後の展開を楽しみにしつつ、CSS Grid をどんどん活用していきたいと思います。