数年前、私はダッシュボードを作ろうと席に着きました。特別なものではありません。固定のサイドバー、メインコンテンツエリア、そしてフッターがあるだけです。Flexboxが当然の選択肢に思え、最初は思い通りに動いていました。しかし、メインセクションにカードの行を投入した瞬間、レイアウト全体が崩れました。その後3時間、flex-growの比率や入れ子になったパーセンテージ指定の幅と格闘し、パディングをいくら削ってもコンテナが整列してくれない様子を眺めて過ごしました。ページは今にも崩れ落ちそうな塔のように感じられました。
その苦い経験から、私はモダンCSSレイアウトにおける最も重要なことを学びました。FlexboxとGridはライバルではありません。それらは異なる目的のためのツールであり、ほとんどのレイアウトの失敗は、間違ったツールを選んでしまうことから起こるのです。
次元の違い
Flexboxは「廊下」だと考えてください。アイテムは行または列のいずれか、一方向に流れます。これは一次元的な分配のために作られています。ブラウザはコンテンツを確認し、その単一の軸に沿って、どのように引き伸ばすか、縮小するか、あるいは整列させるかを決定します。これにより、コンテンツがレイアウトを決定する状況において、Flexboxは完璧なツールとなります。ナビゲーションバー、フォームフィールドのスタック、あるいは等間隔のスペースが必要なタグのセットなどは、すべて一つの論理的なラインを共有しているため、flexコンテナの中に自然に収まります。
一方、Gridは「間取り図」です。これは厳密に二次元的であり、行と列を同時に扱います。まず構造を定義し、その中にアイテムを配置します。これにより、Gridはページ全体のアーキテクチャ、つまり垂直方向と水平方向の位置関係を同時に制御する必要があるあらゆる場所に理想的となります。入れ子になったFlexboxコンテナを使って複雑な二次元デザインを無理に実現しようとすると、マークアップが深くなり、ラッパーは脆弱になり、要素を一つ追加しただけで崩壊してしまうような、脆い計算式に陥ってしまいます。
Flexboxの役割は依然として重要
すべてのflexコンテナを急いでGridに置き換えようとしないでください。個々のコンポーネント内では、Flexboxの方がクリーンで、より直感的な場合が多いです。例えば、2つのボタンがあるカードのフッターで、片方を左に、もう片方を右に寄せたい場合、justify-content: space-betweenはまさに意図した通りの動きをします。高さの不明なボタン内のアイコンを垂直方向に中央揃えしたい場合は、align-items: centerが最もシンプルな方法です。名前の長さが異なるユーザーバッジの行のような、可変的なコンテンツは、Flexboxが「コンテンツ第一(content-first)」であるため、快適に収まります。コンテナが中身に合わせて適応するのです。
Gridは「コンテナ第一(container-first)」です。ブラウザにトラックの数とサイズを伝え、そのセルの中にアイテムを落とし込んでいきます。もしレイアウトが単にスペースを共有する兄弟要素の1行だけであれば、Gridを使うのは不必要な手間を増やすだけです。
ページレイアウトはGridの領域
ここでGridの出番となります。私のダッシュボードの悪夢を覚えていますか?本来、構造は最初からこのようにあるべきでした。
.page {
display: grid;
min-height: 100vh;
grid-template-rows: auto 1fr auto;
grid-template-columns: 200px 1fr;
}
.header { grid-row: 1; grid-column: 1 / -1; }
.sidebar { grid-row: 2; grid-column: 1; }
.main { grid-row: 2; grid-column: 2; }
.footer { grid-row: 3; grid-column: 1 / -1; }
何が起きているか見てみましょう。.pageコンテナは3つの行と2つの列を確立しています。最初の行は、autoのおかげでヘッダーの内容に合わせてサイズが決まります。真ん中の行は、1frによって残りの利用可能なスペースをすべて占有します。フッターは最後の行に配置され、そのコンテンツに応じたサイズになります。その間、サイドバーは幅200ピクセルに固定され、メインエリアが残りの部分を埋めます。
配置のロジックこそが、真の救いです。ヘッダーはカラムライン1から-1まで広がっています。これは、後からいくらカラムを追加しても、両方のカラムにわたって引き伸ばされることを意味します。サイドバーとメインコンテンツは2行目を共有しますが、別々のカラムを占有します。フッターは3行目で全幅に戻ります。子要素にパーセンテージの計算は必要ありません。高さの奪い合いをするような入れ子のラッパーも不要です。サイドバーの幅を1箇所変更するだけで、メインエリアは自動的に調整されます。レイアウト全体が単一のシステムとして機能するのです。
grid-template-areasによる可読性
さらに分かりやすくするために、セクションに名前を付けることができます。ライン番号を管理する代わりに、視覚的なマップを記述します。
.page {
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
.header { grid-area: header; }
.main { grid-area: main; }
これで、スタイルシートがワイヤーフレームのように読めるようになります。もし別のブレイクポイントでサイドバーを右側に表示したくなったら、テンプレート文字列内の2つの単語を入れ替えるだけです。このような構造的な可読性は、入れ子になったFlexコンテナの塔の中では実現不可能なものです。
2つの手痛い教訓
Gridは魔法ではありません。レイアウトをすぐに台無しにしてしまう2つの間違いがあります。
1つ目は、不適切な単位の使用です。もし grid-template-columns: 1fr 1fr 1fr と定義しても、デザイン上必要なコンテンツが2列だけであれば、その3番目のトラックは依然として存在します。そのトラックは空のまま残って予期しない隙間を生むか、あるいは実際のコンテンツを無理に押し込もうとしてデザインを崩してしまうかのどちらかになります。常に意図を持って指定しましょう。2列にしたい場合は、repeat(2, 1fr) と記述してください。
