tochio

CSS Grid Layoutってどうよ?:聖杯レイアウトを試してみた

2016年の春辺りから、「そろそろ『CSS Grid Layout』が来るらしい」という話をよく聞くようになりましたが、最近になって、「いよいよ来るか!?」というニュアンスに変わってきたように思います。
CSS Grid Layoutと言われても、「なんか便利ですごいらしい」という、ぼんやりとした印象しかなかったので、調べつつ、ちょっと触ってみました。

そもそもCSS Grid Layoutって何よ?

2017年5月現在、W3Cで仕様策定中の、新しいレイアウトモジュールです。
グリッドレイアウトを実装する際に便利なプロパティが揃っています。
仕様の策定は2011年頃から行われていて、2017年2月に「CSS Grid Layout Module Level 1」として勧告候補となっています。

各ブラウザの実装はなかなか進んでいませんでしたが、Chromeが2017年3月にリリースされたバージョン57から対応したので、Chrome、Firefox、Safariでは、問題なく使えるところまで来ました。
各ブラウザの実装状況は以下の通りです。
参考:Can I use...CSS Grid Layout

Can I useで確認すると、IE11とEdgeが「要ベンダープレフィックス」になっています。
また、IE11とEdgeは、まだCSS Grid Layoutの策定が始まった頃の古い仕様を元に実装されているため、記述の仕方が最新の仕様とは少し異なっています。
参考:グリッド レイアウト (Windows)
また、古い仕様の詳細は、以下でご確認ください。
Grid Layout

今回は、最新の仕様に沿っていこうと思いますので、古い仕様にしか対応していない、IE11とEdgeのことはちょっと置いておきます。それ以外のモダンブラウザで表示できればOKです。
Autoprefixerを使用すれば、IE11とEdge用のベンダープレフィックスを自動でつけてくれるようです)

  • ヘッダ、フッタ、3カラムのシンプルなレイアウト(いわゆる「聖杯レイアウト」と呼ばれるものです)
  • レスポンシブで、デバイス幅600pxでレイアウトが切り替わる
というテストページを作成しましたので、それぞれのプロパティを見ていきましょう。

■html

<div class="grid_container">
    <header class="grid_header">header</header>
    <nav class="grid_nav">nav</nav>
    <main class="grid_main">main</main>
    <aside class="grid_aside">aside</aside>
    <footer class="grid_footer">footer</footer>
</div><!-- /.grid_container -->

■css

body {
    margin: 0;
    padding: 0;
}
header,footer,main,nav,aside {
    box-sizing: border-box;
    padding: 10px;
}
header {
    background-color: #F7B3F9;
}
footer {
    background-color: #C9F4FF;
}
main {
    background-color: #BAFCDE;
}
nav {
    background-color: #FDFFCC;
}
aside {
    background-color: #FFDE99;
}

.grid_container {
    display: grid;/* グリッドコンテナであることを定義 */
    grid-template-areas: 'header'/* 1行目 */
                         'nav'/* 2行目 */
                         'main'/* 3行目 */
                         'aside'/* 4行目 */
                         'footer'/* 5行目 */;
    grid-template-rows: 60px 50px 1fr auto 60px;/* 行の高さ */
    grid-template-columns: 1fr;/* 列の幅 */
    grid-row-gap: 0; /*行と行の間の余白 */
}
.grid_header {
    grid-area: header;
}
.grid_footer {
    grid-area: footer;
}
.grid_main {
    grid-area: main;
}
.grid_nav {
    grid-area: nav;
}
.grid_aside {
    grid-area: aside;
}

/* デバイス幅が600px以上の時 */
@media screen and (min-width: 600px) {
    .grid_container {
        grid-template-areas: 'header header header'/* 1行目 */
                             'nav main aside'/* 2行目 */
                             'footer footer footer';/* 3行目 */
        grid-template-rows: 60px 1fr 100px;/* 行の高さ */
        grid-template-columns: 20% 1fr 20%;/* 列の幅 */
        grid-row-gap: 10px;/* 行と行の間の余白 */
        grid-column-gap: 10px;/* 列と列の間の余白 */
        /* ショートハンドだと
        grid-gap: 10px 10px;
        */
        height: 100vh;/* フッタの位置を最下部に */
    }

}

「グリッドコンテナ」を定義する

「display: grid;」もしくは「display: inline-grid;」が指定された要素が「グリッドコンテナ」となります。
まずこれを定義しておかないと、CSS Grid Layoutは使えません。
「グリッドコンテナ」に、グリッドの数や、列、行といったレイアウトを指定し、その直下の子要素をそれに従って配置します。

grid-template-areas

子要素に個別の名前を付け、グリッドコンテナの中でどのような並び方をするのかを、視覚的に定義するプロパティです。
それぞれの行に表示する子要素に名前をつけて、「' '」の中に記述していきますが、ここでつけた名前は、後述のgrid-areaと連動します。
デバイス幅600pxまでのスマホやタブレットの時は、行が5、列が1の表示になるので、すべての子要素が縦に並びます。
デバイス幅600px以上の時は、行が3、列が3の表示になるので、「' '」の中に、その行に表示されるすべての列の名前を、スペース区切りで記述します。

■上記cssの27~31行目・55~57行目

    /* デバイス幅600px以下の時 */
    grid-template-areas: 'header'/* 1行目 */
                         'nav'/* 2行目 */
                         'main'/* 3行目 */
                         'aside'/* 4行目 */
                         'footer'/* 5行目 */;

    /* デバイス幅600px以上の時 */
    grid-template-areas: 'header header header'/* 1行目 */
                         'nav main aside'/* 2行目 */
                         'footer footer footer';/* 3行目 */

デバイス幅600px以上の時の状態を、図にするならこんな感じでしょうか。

2行目の列には左から「nav」「main」「aside」とそれぞれ別の名前が設定されているので、それぞれ「nav」「main」「aside」という名前の列として扱われますが、1行目の列はすべて「header」、3行目の列はすべて「footer」なので、連結された1列として扱われます。

grid-template-rows

行の高さを指定します。
1行目から順に、スペース区切りですべての行の高さを記述していきます。
デフォルトは「auto」なので、特に指定がない場合は「auto」にしておけばいいでしょう。

■上記cssの32行目・58行目

    /* デバイス幅600px以下の時 */
    grid-template-rows: 60px 50px 1fr auto 60px;/* 行の高さ */

    /* デバイス幅600px以上の時 */
    grid-template-rows: 60px 1fr 100px;/* 行の高さ */

grid-template-columns

列の幅を指定します。
こちらも、左側から順に、スペース区切りですべての列の幅を記述していきます。
デフォルトは「auto」なので、特に指定がない場合は「auto」にしておけばいいでしょう。

■上記cssの33行目・59行目

    /* デバイス幅600px以下の時 */
    grid-template-columns: 1fr;/* 列の幅 */

    /* デバイス幅600px以上の時 */
    grid-template-columns: 20% 1fr 20%;/* 列の幅 */

grid-template-rowsとgrid-template-columnsは、px、%、auto、frの単位で指定できます。

ここで、これまで見たことのない「fr」なる単位が出てきました。
この「fr」は、flexboxの「flex-grow」と似た動きをします。
デバイス幅600px以上の時の各列の幅は、左カラムと右カラムは全体の20%、中央のカラムは1fr=左カラムと右カラムの幅を除いたエリア全体に広がって表示される、といった感じです。
参考:flex-grow - CSS | MDN

grid-row-gap/grid-column-gap

grid-row-gapは行と行の間の余白、grid-column-gapは、列と列の余白を指定できます。
ショートハンドの場合は、「grid-gap: 行 列;」の順に記述します。

■上記cssの34行目・60~64行目

    /* デバイス幅600px以下の時 */
    grid-row-gap: 0; /*行と行の間の余白 */

    /* デバイス幅600px以上の時 */
    grid-row-gap: 10px;/* 行と行の間の余白 */
    grid-column-gap: 10px;/* 列と列の間の余白 */
    /* ショートハンドだと
    grid-gap: 10px 10px;
    */

「グリッドアイテム」を定義する

「グリッドコンテナ」直下の子要素が「グリッドアイテム」です。「グリッドコンテナ」で指定されたレイアウト通りに配置されます。
「グリッドコンテナ」直下の子要素のみ「グリッドアイテム」となり、孫要素は「グリッドアイテム」として扱われません。

grid-area

「グリッドコンテナ」のgrid-template-areasでつけた名前を、子要素に定義します。これを設定しておかないと、想定通りのレイアウトにはできません。
今回のレイアウトでは、以下の5つのみ指定しています。

■上記cssの36~50行目

.grid_header {
    grid-area: header;
}
.grid_footer {
    grid-area: footer;
}
.grid_main {
    grid-area: main;
}
.grid_nav {
    grid-area: nav;
}
.grid_aside {
    grid-area: aside;
}

結局CSS Grid Layoutって使えるの?

触っている分には楽しいですが、即、実作業で使えるか?と言われれば、使いづらい、と感じます。
冒頭でも書きましたが、IE11とEdgeが最新の仕様に対応していない、というのは、現状ではなかなか大きなネックになるのではないでしょうか。

IE11とEdgeにも対応しようと思ったら

最新の仕様と一緒に古い仕様でも書く、という方法なら対応は可能です。
ただ、勧告候補になっているのは最新の仕様なので、個人的には、今から古い仕様を覚えるのはどうなのかな、と思います。Flexboxの時もそうでしたが、古い仕様に対応したままになっているブラウザがひとつでもあると、実作業では使いづらくなってしまうので、実装するなら最新の仕様に合わせていってもらいたいものです。

今回のようなレイアウトは既存の方法でも実装は可能ですが、CSS Grid Layoutではもっと複雑にグリッドアイテムを配置する、といったことができるので、その方法については次の機会にでも。

参考サイト

新しいウェブ体験を作ろう TAMのPWA開発
お問い合わせはこちら