HTML/CSSのレイアウト手法変遷 〜tableからgridまで〜

この記事は [LIFULL Advent Calendar 2024](https://qiita.com/advent-calendar/2024/lifull) 12日目です。

フロントエンドエンジニアのえびです。好きな寿司ネタはえんがわです。
普段は [LIFULL HOME'S の賃貸領域](https://www.homes.co.jp/chintai/)の開発をしています。

日々の進化が著しいフロントエンド界隈ですが、レイアウト手法についてはIE11が亡くなったことで安定してきました。
この記事では古代から現代に至るまでのレイアウト手法の変遷を紹介します。

## Block-level content と Inline-level content

Web サイトの要素はボックスモデルで成り立っています。レイアウトとはいわばボックスの並び順を決める作業でもあります。
ここで重要になるのが **Block-level content** と **Inline-level content** という概念です。

Block-level content は要素がブロックとして扱われ、全幅に広がります。
他の要素と連続すると新しい行から始まるのが特徴です。
`div` `p` などにデフォルトで指定されています。

対して Inline-level content は要素がインラインとして扱われ、幅は要素によって可変です。
他のインライン要素と連続した場合、その要素の右側に連なります。
そのため要素を横並びにしたい場合は Inline-level content を使います。
`span` などにデフォルトで指定されています。

他にも細かい仕様の違いがあるので Codepen で比較します。
Block-level content の `div` と Inline-level content の `span` にそれぞれ同じ `style` 属性をベタ書きしました。表示に大きく差が出ているのがわかります。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="GgKoXzr" data-pen-title="砂場" data-user="ebisawa-next" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ebisawa-next/pen/GgKoXzr">
  砂場</a> by Moeko Ebisawa (<a href="https://codepen.io/ebisawa-next">@ebisawa-next</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

これらは HTML の仕様に組み込まれていてタグごとにデフォルト値が指定されていますが、**CSSで上書きすることも可能です。**
これを踏まえて歴史を辿っていきましょう。

## 古代: CSS1時代

はじめて HTML1.0 が公開されたのは1990年ですが、CSS はまだ生まれてすらいませんでした。
1996年に CSS1 がリリースされたもののブラウザの実装が追いついておらず、機能を使いこなすのは難しい状況でした。
そのためデザインの全てを HTML だけで完結させる必要があり、そこで使われていたのが table レイアウトでした。

### Table layout

`table` タグを使って行うレイアウト手法です。
本来 `table` は表組をするためのタグですが Block-level content でも横並びができること、セル結合で多少複雑なレイアウトでも再現ができることから使われていました。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="ogvbQed" data-pen-title="Block-level content と Inline-level content" data-user="ebisawa-next" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ebisawa-next/pen/ogvbQed">
  Block-level content と Inline-level content</a> by Moeko Ebisawa (<a href="https://codepen.io/ebisawa-next">@ebisawa-next</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

しかし文書構造は破壊されますしアクセシビリティ的にも現代で使うことはまずありません。
table タグは表組用途のみで使いましょう。

## 中世: CSS2 / CSS2.1 時代

CSS2 が1998年に勧告されてからは多くの仕様が改善し、ブラウザ側も開発が進んで安定して動くようになりました。
そのため HTML は文書構造に注力し、CSS でスタイリングをする棲み分けが始まります。
レイアウト手法は徐々に table タグから CSS を使ったものに置き換えられていきます。

### float layout

[float](https://developer.mozilla.org/ja/docs/Web/CSS/float) は CSS のプロパティで、要素を包含ブロックの左右どちらかの側に沿うように設置し、テキストやインライン要素がその周りを回りこめるように定義します。
（実は float プロパティはCSS1から存在していましたが、ブラウザの事情もあって CSS2 になってから本格的に使われるようになりました）

Block-level content を横並びにするのはもちろん、float を仕込んだ要素を並べることで段落ちの挙動も作れるようになりました。
しかし挙動に少々癖があり、float を使うとレイアウトの計算が特殊になって後続要素も横に回り込んでしまいます。
そのため float のコンテナに以下のような CSS を仕込んで float を解除する必要がありました。いわゆる **clearfix** というテクニックです。
```css
.float-container::after {
  content: "";
  display: block;
  clear: both;
}
```

詳しい挙動は以下の Codepen をご覧ください。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="bNbEQjd" data-pen-title="Table layout" data-user="ebisawa-next" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ebisawa-next/pen/bNbEQjd">
  Table layout</a> by Moeko Ebisawa (<a href="https://codepen.io/ebisawa-next">@ebisawa-next</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### inline-block layout

`display` プロパティ値のひとつ、 `inline-block` を使う手法です。
`inline-block` は 2004年の CSS2.1 の草案で公開された仕様です。2007年ごろから勧告候補にあがってブラウザでの対応も進み、使われるようになっていきました。

簡単にいうと Block-level content と Inline-level content の良いとこどりです。
要素自体はブロックのようにふるまいつつ、他の要素に対してはインラインのようにふるまいます。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="dPbGwyr" data-pen-title="Float layout" data-user="ebisawa-next" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ebisawa-next/pen/dPbGwyr">
  Float layout</a> by Moeko Ebisawa (<a href="https://codepen.io/ebisawa-next">@ebisawa-next</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

前置きで「Block-level content と Inline-level content は CSS で上書きできる」と書いていましたが、このように display プロパティで操作が可能です。HTML ではインラインだけどブロックとして扱いたい、あるいはその逆のパターンもできるようになりました。
そのため、自由なレイアウトを実現しつつセマンティクスな HTML を保てるようになってきます。

しかし縦センタリングにコツが必要だったり、HTMLの改行でスペースが生まれることから厳密なレイアウトをしたい場合はちょっとだけ大変でした。

## 現代: CSS3

CSS3 は1999年に最初の草案が発表されました。
CSS3 はモジュールと呼ばれる複数の仕様書から構成されており、必要な機能だけを選択して使えるようになりました。これにより CSS2.1 と互換性を保ちつつ開発が可能です。ちなみに既存機能を拡張したものは Level 3, 新機能は Level 1 からスタートしています。

ここで Level1 として登場したのが **Flexible box layout** と **Grid Layout** の概念です。
これらによってレイアウト手法は大きく変化していきます。

### Flexible box layout（2016年〜）

`display` プロパティ値のひとつ、 `flex` を使う手法です。Flexbox とも呼ばれます。

`float` や `inline-block` はレイアウトしたい要素自体にプロパティを設定していましたが、
Flexisible box layout ではレイアウトしたい要素の**コンテナ（親要素）** に `display: flex;` を指定し、Flexitem（＝直下の子要素）の大きさや伸縮率を計算して自動でレイアウトをします。
他にも要素間のマージン(gap)や並び方の指定もできるので、文字通りフレキシブルなレイアウトが可能です。
float や inline-block, 後述の grid に比べて圧倒的に癖が少なく使いやすいです。

ドラフトが公開されたのは2008-2009年ごろですが、現在の仕様に近いものは2013年頃に公開されました。
その後2016年にW3Cの勧告候補に達し、2018年ごろから安定して使えるようになりました。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="GgKZeOY" data-pen-title="Float layout" data-user="ebisawa-next" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ebisawa-next/pen/GgKZeOY">
  Float layout</a> by Moeko Ebisawa (<a href="https://codepen.io/ebisawa-next">@ebisawa-next</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

なお IE11(とSafariが少々) では**挙動がアレ**で開発者の頭痛の種になっていました。（当時は [flexboxのバグに立ち向かう（flexboxバグまとめ）](https://qiita.com/hashrock/items/189db03021b0f565ae27) にお世話になりました…😇）

無事に IE11 がお亡くなりになったため安心して使えますね🪦🙏

### Grid Layout（2017年〜）

`display` プロパティの一つ `grid` を使う手法です。
Grid Layout と呼ばれます。
`display: grid;` を指定したボックスに行・列の概念を反映させ、そのセル内で自由なレイアウトを組むことができます。
Table layout と非常に似ていますが、こちらは CSS で指定をします。
アイディア自体は2007年ごろから議論され始め、2011年ドラフト公開・2017年正式な勧告となりました。
しかし例の如く IE11 の癖が強かったので、気軽に使われるようになったのはつい最近かなと思います。

文字通りグリッドデザインとの相性が非常によく、複雑な骨組みをするのがとても簡単です。
float, inline-block, flexbox では何層もの入れ子構造にしないと出来なかったレイアウトが grid では兄弟要素だけで完結してしまう場合もあります。
Flexbox 同様 gap の指定もできるのでそこも安心です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="ByBzWmo" data-pen-title="Flexible box layout" data-user="ebisawa-next" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ebisawa-next/pen/ByBzWmo">
  Flexible box layout</a> by Moeko Ebisawa (<a href="https://codepen.io/ebisawa-next">@ebisawa-next</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

grid の指定は一見難しいですが、MDN ではグリッドの概念と設定方法が細かく紹介されています。理解が捗るのでとてもおすすめです🤝
[グリッドレイアウトの基本概念 - MDN](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout)

なんもわかんねえ…🤦という場合は以下のジェネレータを使うのも良いでしょう。
行・列・余白・セルを指定するだけで HTML/CSS が生成されます。
[CSS Grid Generator](https://cssgrid-generator.netlify.app/)

## Flexible box layout と Grid layout の使いわけ

個人的には大枠は Grid, 細かい調整は Flexible box layout が便利かなと思います。
どちらも長所がはっきりしているので場合に応じて使い分けるとよいです。

### Flexible box layout を使う場面

- 一次元（一行または一列）のレイアウトをする場合
- 要素の整列やスペース配分を細かく調整したい場合
  - `justify-content` や `align-items` が便利

### Grid layout を使う場面

- 二次元（行・列）のレイアウトをする場合
- 全体の骨子を作る場合
- 画面幅によって要素の順番が複雑に入れ替わる場合
  - PCでは A -> B -> C だが、SPでは B -> C -> A など
  - 複雑なレイアウトの場合は Grid layout を使わないと実現が非常に難しい

## まとめ

table コーディングから grid までのレイアウト手法を振り返ってみました。
自分は業務でコーディングをし始めたのが2016年だったのでちょうど float/inline-block -> flex/grid の過渡期でしたね。
当時は float も inline-block も **わかんね〜〜〜〜〜！(検閲)〜〜〜〜〜！(検閲)〜〜〜〜〜！！！** と思いながらコーディングする毎日でしたが flex を使い始めてからは非常に心が安らかでした。
レイアウト手法が頭に入るとコーディングが格段に楽になりますのでぜひ覚えてもらえると嬉しいです☺️