CSS グリッドで「ヘッダーは横いっぱい、その下に左サイドバーとメイン、一番下にフッター」といったレイアウトを組むとき、子要素ごとに何行目から何列目までを数えて指定するのは骨が折れます。grid-template-areas を使うと、グリッドのマスに名前を付けて、レイアウトを文字列の並びで「絵のように」書けます。この記事では、grid-template-areas の基本の書き方から、マスの結合、ピリオドによる空きマス、メディアクエリで並びだけを差し替えるレスポンシブ対応までを、実際に動かせるデモ付きで解説します。
目次
grid-template-areas はレイアウトを「絵」で書くプロパティ
grid-template-areas は、グリッドコンテナ(display: grid を指定した親要素)に指定するプロパティで、グリッドの各マスに好きな名前を付けます。値は文字列を複数並べたもので、1つの文字列が1つの行(row)、文字列の中に空白区切りで並べた名前が左から順の列(column)に対応します。
.layout {
display: grid;
grid-template-areas:
"header header" /* 1行目:2列とも header */
"sidebar main" /* 2行目:左が sidebar、右が main */
"footer footer"; /* 3行目:2列とも footer */
}
この3行の文字列そのものが、完成後のレイアウトの見取り図になっています。あとは子要素側で、どの名前のエリアに入るかを grid-area で指定するだけです。行番号・列番号を数える必要はありません。
/* 子要素は「どのエリアに入るか」を名前で指定するだけ */
.layout__header { grid-area: header; }
.layout__sidebar { grid-area: sidebar; }
.layout__main { grid-area: main; }
.layout__footer { grid-area: footer; }
エリア名は自分で決める識別子で、引用符で囲んだり文字列にしたりはしません。grid-area: header; のようにそのまま書きます。使える文字は CSS の識別子のルールに従うので、数字始まりは避け、side-bar のようにハイフンでつなぐ書き方が無難です。
基本の書き方と、列・行のサイズ指定
grid-template-areas は、書いた文字列の数だけ行を、名前の数だけ列を作ります。それぞれの大きさは grid-template-columns と grid-template-rows で決めます。省略した場合、そのマスのサイズは auto(中身に合わせた大きさ)になります。サイドバーの幅を固定してメインに残りを与えたいなら、次のように書きます。
<div class="layout">
<header class="layout__header">header</header>
<aside class="layout__sidebar">sidebar</aside>
<main class="layout__main">main</main>
<footer class="layout__footer">footer</footer>
</div>
下のデモは、この HTML に grid-template-areas を適用した管理画面風のレイアウトです。CSS タブの文字列の並びを、たとえば "sidebar main" から "main sidebar" に入れ替えると、HTML はそのままで緑のサイドバーが右の列へ移動します。列の幅は grid-template-columns の 140px と 1fr のままなので、入れ替えたあとは左のメインが 140px、右のサイドバーが残り全部の幅になります。文字列を書き換えるだけで配置が変わることを確かめてみてください。
<div class="layout">
<header class="layout__header">header</header>
<aside class="layout__sidebar">sidebar</aside>
<main class="layout__main">main</main>
<footer class="layout__footer">footer</footer>
</div>
.layout {
display: grid;
/* 列は「サイドバー幅 + 残り全部」、行は「ヘッダー / 中身 / フッター」 */
grid-template-columns: 140px 1fr;
grid-template-rows: 56px 1fr 44px;
/* 1行の文字列が1つの行(row)。名前の数(2個)が列の数と一致している */
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 8px;
height: 260px;
padding: 8px;
background: #f3f4f6;
font-family: sans-serif;
box-sizing: border-box;
}
/* 子要素は「どのエリアに入るか」を名前で指定するだけ */
.layout__header {
grid-area: header;
background: #2563eb;
}
.layout__sidebar {
grid-area: sidebar;
background: #10b981;
}
.layout__main {
grid-area: main;
background: #f59e0b;
}
.layout__footer {
grid-area: footer;
background: #6b7280;
}
.layout > * {
display: grid;
place-items: center;
color: #fff;
font-size: 14px;
border-radius: 6px;
}
同じ名前を並べるとマスが結合される
先ほどの例で "header header" と同じ名前を2つ並べたのは、隣り合う同じ名前のマスが1つのエリアとして結合されるからです。横に並べれば列をまたいで広がり、縦(別の行の同じ位置)に並べれば行をまたいで広がります。テーブルの colspan / rowspan に近い感覚です。
.layout {
display: grid;
grid-template-columns: 140px 1fr;
grid-template-areas:
"header header" /* 横に2マスぶち抜き */
"sidebar main"
"sidebar footer"; /* sidebar が縦に2行ぶち抜き */
}
ただし、結合できるのは長方形になる形だけです。同じ名前を L 字や十字のように配置すると、その宣言は無効になり、グリッドの領域名は一切設定されません。次の例のように sidebar を L 字型に置くことはできません。
.layout {
display: grid;
/* sidebar が L 字になっているので、この宣言はまるごと無効 */
grid-template-areas:
"header header"
"sidebar main"
"sidebar sidebar";
}
無効になった場合、エラーは表示されずにその宣言だけが無視されます。エリア名が存在しない状態になるため、子要素の grid-area: header; も行き先を失い、意図した位置には配置されません。「文字列を書いたのに見た目が変わらない」ときは、まずこの長方形のルールを疑ってください。
ピリオドで空きマスを作る
何も置きたくないマスには、名前の代わりにピリオド . を書きます。ピリオドは「名前のないマス」を表す特別な記号で、そこには grid-area で配置された要素は入りません。. は1つ以上連続して書けて、... のように並べても1マスぶんとして扱われます。列の幅に合わせて見た目を揃えたいときに便利です。
下のデモは3列3行のグリッドで、いくつかのマスを空きにした例です。title は1行目の左2マス、photo は2行目の中央だけ、note は最下行の3マスぶち抜きになっていて、残りの3マスはピリオドで空けています。色の付いていない部分がピリオドで確保した空きマスです。
<div class="board">
<div class="board__title">title</div>
<div class="board__photo">photo</div>
<div class="board__note">note</div>
</div>
.board {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 70px);
/* ピリオドは「何も置かない空きマス」。連続して書いてもピリオド1個ぶんの扱い */
grid-template-areas:
"title title ."
". photo ."
"note note note";
gap: 8px;
padding: 8px;
background: #f3f4f6;
font-family: sans-serif;
}
.board__title {
grid-area: title;
background: #2563eb;
}
.board__photo {
grid-area: photo;
background: #ec4899;
}
.board__note {
grid-area: note;
background: #10b981;
}
.board > * {
display: grid;
place-items: center;
color: #fff;
font-size: 14px;
border-radius: 6px;
}
メディアクエリで並びだけを差し替える
grid-template-areas が最も力を発揮するのが、レスポンシブ対応です。HTML の順序も、子要素に付けた grid-area の指定も一切変えずに、親の文字列だけを書き換えればレイアウトが丸ごと入れ替わります。PC では2列、スマホでは縦積み、という切り替えが数行で書けます。
/* スマホ(既定):1列にすべてを縦積み */
.layout {
display: grid;
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
gap: 8px;
}
/* 768px 以上:2列にしてサイドバーを左へ */
@media (min-width: 768px) {
.layout {
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
}
}
/* 子要素側の指定は画面幅に関係なく共通 */
.layout__header { grid-area: header; }
.layout__sidebar { grid-area: sidebar; }
.layout__main { grid-area: main; }
.layout__footer { grid-area: footer; }
注目したいのは、スマホ側で main を sidebar より前に置いている点です。HTML 上はサイドバーが先に書かれていても、狭い画面では本文を先に読ませる、といった並べ替えが文字列の順番だけで実現できます。メディアクエリごとに列数が変わる場合は grid-template-columns も一緒に書き換えて、文字列内の名前の数と列の数を必ず合わせます。
ただし、見た目の順序と HTML の順序がずれると、キーボード操作やスクリーンリーダーの読み上げ順は HTML のままになります。読み上げ順が不自然になるほど大きく並べ替える場合は、HTML 側の順序を見直すことも検討してください。
grid-area の4値記法との関係
子要素に書く grid-area は、もともと配置に関する4つのプロパティをまとめた一括指定です。スラッシュ区切りで書く場合、値は次の順番になります。
| 書く位置 | 対応するプロパティ | 意味 |
|---|---|---|
| 1つ目 | grid-row-start | 開始する行の線 |
| 2つ目 | grid-column-start | 開始する列の線 |
| 3つ目 | grid-row-end | 終了する行の線 |
| 4つ目 | grid-column-end | 終了する列の線 |
たとえば grid-area: 1 / 1 / 2 / 3; は「1本目の行線から2本目の行線まで、1本目の列線から3本目の列線まで」、つまり1行目を2列ぶち抜きにする指定です。行と列が交互に来るので、margin のような上・右・下・左の並びとは違う点に注意してください。
/* 番号で指定する書き方 */
.layout__header {
grid-area: 1 / 1 / 2 / 3;
}
/* エリア名で指定する書き方(grid-template-areas と組み合わせる) */
.layout__header {
grid-area: header;
}
この2つは同じ結果になりますが、番号での指定は列や行を1つ増やしただけで全部を書き直すことになります。エリア名で書いておけば、レイアウトの変更は親の文字列だけで完結します。なお grid-template-areas でエリアを作ると、そのエリアの四辺には header-start / header-end のような名前付きの線も自動で作られるため、grid-row: header-start / footer-end; のように線の名前で範囲を指定することもできます。
grid-template-areas を書いてもレイアウトが変わらないとき
行ごとに名前の数が揃っていない
すべての文字列は、同じ数の名前(またはピリオド)を含んでいなければなりません。"header header" と "sidebar main footer" のように2個と3個が混ざっていると、グリッドが長方形にならないため宣言全体が無効になります。空けたい部分を書き忘れているケースが多いので、足りない位置にピリオドを補って数を揃えます。列数を grid-template-columns で明示しておくと、いくつ書くべきかが分かりやすくなります。
同じ名前が長方形になっていない
同じ名前のマスは、必ず1つの長方形にまとまっている必要があります。離れた位置に同じ名前が2箇所あったり、L 字・階段状になっていたりすると無効です。「1行目の左と2行目の右に同じエリアを置きたい」といった配置は grid-template-areas では表現できないので、その要素だけ grid-row / grid-column で個別に配置するか、レイアウト自体を見直します。
名前の書き方が合っていない
文字列の中の名前と、子要素の grid-area に書いた名前は完全に一致している必要があります。CSS のエリア名は大文字と小文字を区別するため、Main と main は別のエリアです。また、子要素側で grid-area: "main"; のように引用符を付けるのは誤りで、引用符が必要なのは親の文字列だけです。文字列を改行して書くときにセミコロンを打つ位置を間違えるのもよくあるミスで、セミコロンは最後の文字列のあとに1つだけ置きます。
指定した相手が親子関係になっていない
grid-template-areas が効くのは display: grid(または inline-grid)を指定した要素で、grid-area が効くのはその直接の子要素だけです。ラッパー用の div を1つ挟んだために孫要素になっていると、名前を書いても配置されません。ブラウザの開発者ツールで親要素に grid のバッジが付いているか、子要素が意図した階層にあるかを確認してください。
まとめ
grid-template-areas は、グリッドのマスに名前を付けて、レイアウトを文字列の見取り図として書けるプロパティです。1つの文字列が1行に対応し、名前の数は列の数と揃える必要があります。同じ名前を隣接させればマスが結合され、ピリオドを置けば空きマスになります。ただし結合できるのは長方形の形だけで、名前の数が揃っていない場合や長方形にならない場合は宣言全体が無効になります。子要素は grid-area にエリア名を書くだけでよく、メディアクエリで親の文字列を差し替えれば HTML の順序を変えずにレイアウトを組み替えられます。番号で位置を数える書き方に比べて、あとから読み返したときに構造が一目で分かるのが最大の利点です。