STUDY

CSSでグリッドレイアウトをしてみよう-実践編

こんにちは。

フロントエンドエンジニアの木下です。
前回の基礎編では、グリッドレイアウトがどのような仕組みになっているかの説明でしたが、今回は実際によくあるレイアウトを元にコードを書いていこうとおもいます。

カラムレイアウト

スマートフォンが出てくる前はほとんどのウェブサイトが固定幅でしたが、現在はリキッドレイアウトで実装することが多いと思います。
ナビゲーション領域が固定幅、コンテンツ領域は可変で実装することが多いので、それを目指すと以下のようなコードになります。

HTML<body>
<header>ヘッダー</header>
<main>メイン</main>
<nav>ナビ</nav>
<footer>フッター</footer>
</body>
cssbody {
    display: grid;
    min-height: 100vh;
    grid-template-rows: 100px 1fr 100px; // ヘッダー・フッターの高さは100px
    grid-template-columns: 100px 1fr;    // ナビゲーションの幅は100px
}
header {
    grid-row: 1;
    grid-column: 1 / span 2;
}
main {
    grid-row: 2;
    grid-column: 2;
}
nav {
    grid-row: 2;
    grid-column: 1;
}
footer {
    grid-row: 3;
    grid-column: 1 / span 2;
}

body要素にdisplay:gridを指定すると、小要素がすべてグリッドアイテムになるので、グリッド関連の各プロパティが指定できるようになります。
min-height:100vhを指定しているのは、コンテンツ領域の高さがなくてもフッターは常に最下部に設置したい為です。

grid-template-rows、grid-template-columnsの2番目に指定されている1frですが、このfrはこれまで使用したことのない単位ですね。
fraction(分数)という意味なのですが、他で指定している分を利用可能な領域から引いた(今回のgrid-template-rowsでは100px×2の200px)余りを、fr指定されている数(今回は1frなので1)だけ分割して計算してくれる便利な指定です。

cssbody {
    grid-template-rows: 3fr 1fr 2fr;
}

もしこのような指定だった場合は、利用可能な領域を合計したfr分、つまり6分割した値なので、それぞれ3/6,1/6,2/6の領域になります。

header要素とfooter要素のgrid-columnに指定されているspanに関しては、spanの後ろについた数字分の領域になります。

cssheader {
    grid-column: 1 / span 2;
}
cssheader {
    grid-column: 1 / 3;
}

この2つのコードは、書き方は違いますが同じ指定になります。
spanを使った指定はテーブルレイアウトのrowspan,colspanと考え方が近いので、わかりやすいと思う人も多いかもしれません。

他にも以下のコードのようにセルに名前をつけて指定する方法もあります。
個人の場合はお好みの書き方を、チームで作る場合はルールを定めて混乱が起きないように気をつけてくださいね。

cssbody {
    display: grid;
    min-height: 100vh;
    grid-template-rows: 100px 1fr 100px;
    grid-template-columns: 100px 1fr;
    // グリッドセルに自由に名前をつけます。
    grid-template-areas:
        "header header"  // ダブルクオーテーションで囲まれたものがグリッドの一行になります。
        "nav contents"
        "footer footer";
}
header {
    grid-area: header;
}
main {
    grid-area: contents;
}
nav {
    grid-area: nav;
}
footer {
    grid-area: footer;
}

はじめてコードを見たときに、直感的に理解しやすいと思います。
ショートハンドを使えば幅や高さも名前も一気につけることができますが、みやすさ重視で私はrows,columns,areasにわけた書き方が好みです。

サンプルでは2カラムレイアウトでしたが、カラム数が増えても対応が簡単ですね。

パネル型レイアウト

次は、リストアイテムをパネル型にレイアウトする方法です。

まずは、このように整然と並ばせるにはアイテム側にはとくに指定をしなくても、グリッドコンテナを生成すれば自動的に左上から行を埋めて配置されます。

HTML<ul id="list">
    <li id="listitem-A">A</li>
    <li id="listitem-B">B</li>
    <li id="listitem-C">C</li>
    <li id="listitem-D">D</li>
    <li id="listitem-E">E</li>
    <li id="listitem-F">F</li>
</ul>
css#list {
    display: grid;
    grid-template-rows: 100px 100px;
    grid-template-columns: 200px 200px 200px;
}

サンプルではグリッドコンテナ自体が600pxとなっていますので、もし中央寄せにしたいのであればフレックスボックスレイアウトでも使用しているjustify-content: center;を指定すると良いです。
また、リキッドレイアウトで幅が可変の場合は、grid-template-columnsの値を%やfrを使うと良いですね。

パネル型レイアウトの場合、特定のアイテムだけ大きくさせて強調したり、単調とならないようデザインにリズムをつけたりすることがあるとおもいます。

このレイアウトの場合、さきほどのコードのままだと、Aの下のセルが空いてしまいFのスペースがなくなってしまいます。

css#list {
    display: grid;
    grid-template-rows: 100px 100px 100px 100px;
    grid-template-columns: 200px 200px 200px;
    grid-auto-flow: dense; // できるだけ敷き詰める指定
}
#listitem-B,
#listitem-C {
    grid-row: span 2;
    grid-column: span 2;
}

その問題は、グリッドコンテナにgrid-auto-flow: denseを指定することにより、できる限り隙間を埋めていくことができます。
grid-auto-flowの値にはdenseの他にrow(デフォルト値)とcolumnがあり、columnを指定した場合は左上から列をたどって配置されます。

他にもorderプロパティで直接指定する方法もあります。

おわり

他にもgrid-gapでグリッドアイテム(セル)同士の隙間を指定したり、細かな調整用のプロパティもありますが、大まかなレイアウトは今回のコードで概ね実現できるとおもいます。

これまでJavaScriptを使わないと実装できなかったレイアウトがグリッドレイアウトを駆使すれば簡単にできるようになりました。
ただ、レイアウトによっては、グリッドレイアウト、他にもフレックスボックスレイアウトを使用しても直接の親子兄弟要素の関係にしばられる問題が残り、どうしてもHTMLを構造化できない場合があります。
その問題は今後ブラウザサポートが増えていくであろうdisplay:contentsが解決してくれるでしょう。
セマンティックなマークアップがもっと広まっていくと良いですね!