CSSのfloatやflexではなくdisplay: gridでレイアウトを作成するべき

現在も多いfloatとflex

Webサイトを作成するときに以下のような最大幅520pxで中の余白が20pxの3行 x 3列のレイアウトをCSSで作成するにはfloat: left, display: flex, display: grid のいずれかが使用されることが多い。

box1
box2
box3
box4
box5
box6
box7
box8
box9

しかし、float: leftとdisplay: flexはdisplay: gridに比べてデメリットが多いので、このようなレイアウトで使用するのは好ましくない。

display: gridは使うのが難しいというイメージを持っている人が多いが、シンプルなレイアウトであればfloat: leftやdisplay: flexよりもdisplay: gridのほうが簡単に扱える。

float: leftの場合

float: leftで3行 x 3列のレイアウトを作成する場合は以下のようになる。

<div class="wrap-float">
  <div class="box">box1</div>
  <div class="box">box2</div>
  <div class="box">box3</div>
  <div class="box">box4</div>
  <div class="box">box5</div>
  <div class="box">box6</div>
  <div class="box">box7</div>
  <div class="box">box8</div>
  <div class="box">box9</div>
</div>
.wrap-float {
  /* display: flow-root; */
  overflow: hidden;
  max-width: 520px;
}

.wrap-float > .box {
  width: calc(100% / 3 - 40px / 3);
  float: left;
  padding: 20px;
  box-sizing: border-box;
  background: skyblue;
}

.wrap-float > .box:not(:nth-child(3n)) {
  margin-right: 20px;
}

.wrap-float > .box:not(:nth-last-child(-n+3)) {
  margin-bottom: 20px;
}
box1
box2
box3
box4
box5
box6
box7
box8
box9

floatの解除にはoverflow: hidden;を使用しているがIE 11を対象外にするのであれば代わりにdisplay: flow-root;でも有効。

一見すると問題ないように見えるがfloat: leftだとテキストが複数行になったときに同じ行の高さが変わらないという致命的な欠点がある。

box1
box2
box3
box4
box5
foo
box6
box7
box8
box9

また、CSSのコードも21行とflexやgridを使用するよりも多くなってしまう。

さらに.boxの幅はwidth: calc(100% / 3 - 20px * 2 / 3); で計算しているため、余白が20pxから30pxに変わった場合はこの部分も変更しなければならない。

display: flexの場合

display: flexで3行 x 3列のレイアウトを作成する場合は以下のようになる。

<div class="wrap-flex">
  <div class="box">box1</div>
  <div class="box">box2</div>
  <div class="box">box3</div>
  <div class="box">box4</div>
  <div class="box">box5<br>foo</div>
  <div class="box">box6</div>
  <div class="box">box7</div>
  <div class="box">box8</div>
  <div class="box">box9</div>
</div>
.wrap-flex {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  max-width: 520px;
}

.wrap-flex > .box {
  width: calc(100% / 3 - 20px * 2 / 3);
  padding: 20px;
  box-sizing: border-box;
  background: skyblue;
}

.wrap-flex > .box:not(:nth-last-child(-n+3)) {
  margin-bottom: 20px;
}
box1
box2
box3
box4
box5
foo
box6
box7
box8
box9

CSSのコード量はfloat: leftと比べると少なくなっているが、.boxにmargin-bottomを付けて下の余白を追加しているので、.box:not(:nth-last-child(-n+3)) で最終行以外ののmargin-bottomを追加する処理がこちらも必要になってしまう。

display: gridの場合

display: gridで3行 x 3列のレイアウトを作成する場合は以下のようになる。

<div class="wrap-grid">
  <div class="box">box1</div>
  <div class="box">box2</div>
  <div class="box">box3</div>
  <div class="box">box4</div>
  <div class="box">box5<br>foo</div>
  <div class="box">box6</div>
  <div class="box">box7</div>
  <div class="box">box8</div>
  <div class="box">box9</div>
</div>
.wrap-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr 1fr 1fr;
  max-width: 520px;
}

.wrap-grid > .box {
  padding: 20px;
  box-sizing: border-box;
  background: skyblue;
}
box1
box2
box3
box4
box5
foo
box6
box7
box8
box9

前述のfloat: leftやdisplay: flexに比べてCSSのコードが少なく簡潔になっているのがわかる。

行列の数と余白の値はgap (grid-column-gap, grid-row-gap) と grid-template-rows (grid-template-column) の値を変えるだけなのでレスポンシブWebデザインとの相性も良い。

例えば2列にして行の余白を10px、列の余白を30pxにする場合は以下のようにgapではなくgrid-row-gapとgrid-column-gapにすれば反映できる。

.wrap-grid2 {
  display: grid;
  grid-row-gap: 10px;
  grid-column-gap: 30px;
  grid-template-columns: 1fr 1fr;
  max-width: 520px;
}
box1
box2
box3
box4
box5
foo
box6
box7
box8
box9

gridにはfrという単位が使える

grid-template-columnsにはfrという単位も使用できる。

frは親要素の幅に合わせたグリッドの幅に自動的に調整することができる単位。

frはfractionの略で「分数」という意味。

例えば3列の中身の幅を1:2:1にしたい場合は「1fr 2fr 1fr」を指定する。

幅520pxでgap: 20px;の場合は(520 - 20 * 2) / 4 = 120なので、1frは120px、2frは240pxとなる。

.wrap-grid-fr {
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr 2fr 1fr;
  max-width: 520px;
}
box1
box2
box3
box4
box5
foo
box6
box7
box8
box9

ちなみにgrid初心者だと「1fr 1fr」と「auto auto」は同じと勘違いされることが多い。

autoは1frの代わりにはならず正しく幅が計算されないので注意。

Chromeのデベロッパーツールで確認

Chromeのデベロッパーツールでdisplay: gridの要素部分をElementsタブで確認して「grid」のバッジをクリックすればグリッド表示が切り替わって確認しやすくなる。

Chromeのデベロッパーツールでdisplay: gridを確認
Chromeデベロッパーツールでdisplay: gridを確認

デフォルトの1, 2, 3, 4の行番号は今回のケースだと邪魔なのでLayoutのGridから「Hide line labels」で非表示にしてShow track sizesとExtend grid linesを有効にしたほうがdisplay: gridの確認がしやすくなる。

Chromeデベロッパーツール Layout Grid
Chromeデベロッパーツール Layout Grid
Layout Grid 設定変更後
Layout Grid 設定変更後

display: gridはIE11でも使えるが…

display: gridはベンダープレフィックスを追加するだけではIE11では使用不可。

.wrap-grid {
  display: -ms-grid;
  display: grid;
  gap: 20px;
  -ms-grid-columns: 1fr 20px 1fr 20px 1fr;
  grid-template-columns: 1fr 1fr 1fr;
  max-width: 520px;
}

ちなみにAutoprefixerで自動付与している。

-ms- を追加するだけでIE11でも使用できると勘違している人は結構多い。

Autoprefixer CSS online

IE11でも3行 x 3列にするにはHTMLとCSSの両方を変えなければならない。

潔くIE11はサポートから除外するのが懸命だ。

ちなみになるべく元のCSSを流用してHTMLを変えてIE11対応の3行3列にする場合は以下のようになる。

<div class="wrap-grid">
  <div class="box">box1</div>
  <div class="box">box2</div>
  <div class="box">box3</div>
</div>
<div class="wrap-grid">
  <div class="box">box4</div>
  <div class="box">box5<br>foo</div>
  <div class="box">box6</div>
</div>
<div class="wrap-grid">
  <div class="box">box7</div>
  <div class="box">box8</div>
  <div class="box">box9</div>
</div>
.wrap-grid3 {
  display: -ms-grid;
  display: grid;
  -ms-grid-columns: 1fr 20px 1fr 20px 1fr;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 20px;
  max-width: 520px;
}

.wrap-grid3:not(:nth-of-type(3)) {
    margin-bottom: 20px;
}

.wrap-grid3 > .box {
  padding: 20px;
  box-sizing: border-box;
  background: skyblue;
}
.wrap-grid3 > .box:nth-of-type(1) {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}
.wrap-grid3 > .box:nth-of-type(2) {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
}
.wrap-grid3 > .box:nth-of-type(3) {
  -ms-grid-row: 1;
  -ms-grid-column: 5;
}
box1
box2
box3
box4
box5
foo
box6
box7
box8
box9
カテゴリーcss