カードの高さを揃えたければsubgridを使えばいい

広告
従来の方法では一箇所しかコンテンツの高さを揃えることができない
カードの高さを揃える際のアプローチとしてはdisplay:flex; flex-direction:column;
もしくはdisplay:grid;
を使用した方法が主流かと思われます。
/* カードを横並びする要素 */.card-wrapper { display: block grid; grid-template-columns: repeat(3, 1fr); gap: 24px;}
/* flexを使用した方法 */.card { display: block flex; flex-direction: column;}
.card-title { flex: 1;}
/* gridを使用した方法 */.card { display: block grid; grid-template-rows: auto auto 1fr auto; /* titleの行を1frと指定 */}
display:flex
を使用した例では揃えたい要素(ここではタイトルとします)にflex:1
を適用して、その部分が可変のスペースを吸収するように設定しています。display:grid
を使用した例ではgrid-template-rows
を使い、タイトル部分を1fr
とすることでそのコンテンツが残りの空間を埋めるように設定しています。
この方法であれば一箇所のコンテンツの高さを揃えつつ並べることができますが、複数箇所の高さを揃えることができません。また、flex:1
もしくはgrid-template-rows
で1fr
を指定した部分以外が不均一になると、全体の高さが一致しなくなるリスクがあります。

従来の方法で複数箇所の高さを揃えるにはセマンティックではないtable
要素を使用するか、JSで高さを調整するしかアプローチがありませんでした。現在ではsubgrid
を使用すれば適切なマークアップを保ちつつ、CSSのみで解決することができます。
※ matchHeight.jsを使用した高さを揃える方法を紹介している文献がありますが、これはfloat:left
で並べていた頃の遺物なので現在は使用する必要はありません。
subgridを使えば全てのコンテンツの高さを揃えることができる
subgrid
を使う方法は至極簡単で、先程紹介したdisplay:grid
を用いたアプローチのgrid-template-rows
の値をsubgrid
に変更し、grid-row
プロパティにspan {コンテンツの数}
を指定したものを追加するだけです。
/* これを */.card { display: block grid; grid-template-rows: auto auto 1fr auto;}
/* こうする */.card { display: block grid; grid-template-rows: subgrid; grid-row: span 4;}
今回のサンプルではサムネイル、タイトル、紹介文、価格の4行分のトラックを使用しているため、grid-row
プロパティの値はspan 4
となります。subgrid
では暗黙的なgridは生成されないため、コンテンツの数に対応したgrid-row
の指定がないと崩れて表示されてしまいます。指定漏れがないように気をつけてください。
また、サンプルでは全てのコンテンツの高さが揃っていることを証明するためにカードの横幅を固定値にしておりますが、仮に行を折り返したとしてもカード内の要素の高さ揃えは綺麗なまま維持されます。JSで高さを調整する場合、行の折り返しが発生する際の細かな調整が必要でしたがsubgrid
では不要です。

直接の子要素でないとsubgridは適用できないので注意
subgrid
を指定する際は「display:grid
で並べる要素>subgrid
を指定する要素>各コンテンツ」の構造でなければ適用できません。
以下の構造では「カードを並べる要素>section
要素>a
要素>各コンテンツ」となっており、そのままでは余分な要素が介入しているためsubgrid
が適用できなくなっています。
<div class="card-wrapper"> <section> <a href="..."> <h2>タイトル</h2> <div>サムネイル</div> <p>紹介文</p> <p>価格</p> </a> </section> ...</div>
この例であればsection
要素にdisplay:contents
を指定し、a
要素にsubgrid
を指定することで解決できます。
section { display: contents;}
a { display: block grid; grid-template-rows: subgrid; grid-row: span 4;}
ただし、display:contents
はスクリーンリーダーで読み上げが行われない可能性があるなどアクセシビリティに不都合が生じる可能性があります。 div
要素やaccessible nameの無いsection
要素であれば問題はないかと思われますが、スクリーンリーダーでの確認は怠らないようにしてください。
subgridは全てのモダンブラウザでサポート済み
subgrid
は全モダンブラウザで対応されております。iOS Safariはバージョン16からの導入なので、それ以前のバージョンをサポートする場合は残念ながら使用を控える必要があります。
当ブログの記事一覧にもsubgrid
が使用されています。