Grid Layoutについて

IEがサポート終了したので、そろそろGrid Layoutを使ってみよう!

目次
  1. Grid Layoutの基本
  2. グリッドアイテムの配置
    1. 線番号で指定
    2. エリアで指定
  3. セル間隔・その他
  4. Grid LayoutとFlexbox
    1. レスポンシブな動作
    2. ボックスの配置
    3. どっちを使う?
      1. Grid Layout
      2. Flexbox

Grid Layoutの基本

基本概念とポイント

  • グリッドコンテナーを作成
    要素に対して「display: grid 」か「display: inline-grid 」を指定
    直接の子要素がグリッドアイテムに変わります
  • グリッドトラック(行や列)を定義
    grid-template-columns」行数と幅
    grid-template-rows」列数と高さ
  • トラック(行や列)のサイズ
    repeat() 関数で繰り返す書き方ができます
    • 固定の行や列のサイズ(px単位)
    • 可変の行や列サイズ(%やfr単位)
      frはグリッドコンテナー内の利用可能な空間の比です
    • auto
      同じ列または行にfr単位がある場合アイテムのコンテンツ幅に合わせてグリッドサイズを調整します(なけれがfrと同じ)
  • 暗黙的なグリッド
    *明示的なグリッドは定義したトラック(行と列)で構成されます
    すなわち行は定義しなくても、列グリッドは勝手に行を作ります
    これは暗黙的なグリッドです
    暗黙的なグリッドでは、デフォルトで自動サイズ調整するのでサイズはトラック内の内容物で決まります
  • 暗黙的なグリッドのトラックに高さや幅をつけます
    grid-auto-rows
    grid-auto-columns
    minmax() 関数を使うことでより柔軟に指定することができます
  • グリッドアイテムの配置
    • 位置が明示されていないアイテムはアルゴリズムにより自動で配置されます
    • 線番号」や「エリア(名前)」を指定してアイテムを配置することができます

用語

グリッド
列と行を定義する水平線と垂直線の集合が交差したものです
要素をグリッド上の行と列の中に配置できます
Grid Track(グリッドトラック
行と列のこと
2本のグリッド線の間の空間
暗黙的グリッドにもトラックが生成されます
Grid Cell (グリッドセル
4つの交差するグリッド線に囲まれた領域
最小単位
Grid Line (グリッド線
トラックを定義すると作成されます
アイテムの配置はトラックではなくグリッド線が対象になります
Grid Areas(グリッド領域ーエリア
1つまたは複数のセルの範囲(
*必ず四角形でL字型は作れません

1、display: gridでグリッドコンテナーを作成
2、grid-template-columnsで3つの同幅の行トラックを作成
3、自動で配置(線ベースの配置)される子要素(グリッドアイテム)の高さを
grid-auto-rows(最小値50px,最大値auto)で指定
*親のコンテナで指定します

1
2
3
4
5
6
.grid {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr;
/* repeat()関数を使用すると
grid-template-columns: repeat(3, 1fr); */
	 grid-auto-rows: minmax(50px, auto);
}
<div class="grid">
  <div class="grid__item">1</div>
  <div class="grid__item">2</div>
  <div class="grid__item">3</div>
  <div class="grid__item">4</div>
  <div class="grid__item">5</div>
  <div class="grid__item">6</div>
</div>

グリッドアイテムの配置

グリッドアイテムに「線番号」や「エリア(名前)」を指定してアイテムを配置します

線番号で指定

グリッドアイテムに「grid-column」「grid-row
グリッド線の線番号

  • 列の左端から順に:列線の1・2・3…
  • 行の上端から順に:行線の1・2・3…

反対から数える時は「-値」で指定できます

  • 列の右端から順に:列線の-1・-2・-3…
  • 行の下端から順に:行線の-1・-2・-3…

「grid-column-start,grid-column-end」と
「grid-row-start ,grid-row-end」は
grid-column」と「grid-row 」で値は「開始の線番号/終了の線番号」の一括指定ができます

1
2
3
4
5
6
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(50px, auto);
}
.grid__item:nth-child(1) {
/* 1から3の列・1の行 */
  grid-column: 1 / 4;
  grid-row: 1;
}
.grid__item:nth-child(2) {
/* 1の列・2から3の行 */
  grid-column: 1;
  grid-row: 2 / 4;
}

エリアで指定

  • グリッドコンテナーに「grid-template-areas
    *「grid-template-columns」「grid-template-row」と併用
    グリッドアイテムに「gird-area
  • グリッドコンテナーに「grid-template
    「grid-template-columns」「grid-template-row」は不要
    *各行の末尾で行の幅を指定(省略可)
    一番最後の行の後ろに「/」その下の行で各列の幅を指定
    グリッドアイテムに「gird-area

グリッドコンテナは、grid-template-areasでエリアに名前をつけて
グリッドアイテムは、grid-areaで配置します

タイトルです
サブです
メインです
.wrap {
display: grid;
grid-template-areas:
"title title"
"sub main";
grid-template-columns: 100px auto;
/*grid-templateで書くと
 grid-template:
    "title title"
    "sub main"/
    100px auto;
 */
}
.wrap .title {
grid-area: title;
text-align: center;
}
.wrap .sub {
grid-area: sub;
}
.wrap .main {
height: 150px;
grid-area: main;
}
<div class="wrap">
    <div class="title">タイトル</div>
    <div class="sub">サブです</div>
    <div class="main">メインです</div>
</div>

*grid-areaで場所を指定できるのは名前をつけた場所だけです

セル間隔・その他

セル間隔
grid-column-gap(column-gap )と grid-row-gap(row-gap)
または一括指定:grid-gap( gap )
*接頭辞付き(grid-)の方がブラウザ対応がいいようです

1
2
3
4
5
6
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 50px;
  grid-column-gap: 10px;
  grid-row-gap: 15px;
}

入れ子
グリッドアイテムはグリッドコンテナーにもなれます

1の1
1の2
2
.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 100px;
}
.grid .grid__item:nth-child(1) {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  padding:1em;
}
.grid .grid__item >div{
  border:solid 1px red;  
}
<div class="grid">
  <div class="grid__item">
    <div>1の1</div>
    <div>1の2</div>
  </div>
  <div class="grid__item">2</div>
</div>

グリッドアイテムが同じセルを占有する場合はz-indexで重ね合わせの順を指定できます
*z-indexがなければ後に書かれた方が上になります
例では
2番目のアイテムは「列が1と2のセル」「行が1と2のセル」のエリアに配置
1番目のアイテムは「列が2のセル」「行が1のセル」で「z-index」で2番目のアイテムの上に重なっています
3番目のアイテムアイテムは「列が1のセル」「行が2のセル」で後に書かれているのでそのまま2番目のアイテムの上に重なっています

1
2
3
.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 50px;
}
.grid .grid__item:nth-child(1) {
  grid-column: 2;
  grid-row: 1;
  z-index: 1;
}
.grid .grid__item:nth-child(2) {
  grid-column: 1/3;
  grid-row: 1/3;
}
.grid .grid__item:nth-child(3) {
  grid-column: 1;
  grid-row: 2;
}
<div class="grid">
  <div class="grid__item">1</div>
  <div class="grid__item">2</div>
  <div class="grid__item">3</div>
</div>

自動配置アルゴリズムの動作を制御
grid-auto-flow : デフォルトはrowです
grid-auto-flow : column; アイテムは各列を順番に埋めていきます

ああ
ああああ
ああああああああ
ああああああああああああああああ
ああああああああああああああああああああああああああああああああ
.grid {
  display: grid;
  grid-auto-flow: column;
}

Grid LayoutとFlexbox

  • Grid Layoutは列と行の2次元で子要素を管理
    まずはレイアウトを作成
  • Flexboxは縦か横の1次元で子要素を管理
    内容物のサイズに合わせて

レスポンシブな動作

ウィンドウのサイズに応じて子要素(アイテム)の数を調整

<div class="wrap">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
</div>

repeat()関数とauto-fillおよびauto-fitを使用してトラックリストを作ります
*あくまでコンテンツは行と列に配置されます

  • auto-fill:グリッドアイテムが1つ以上入るスペースができたら、空のアイテムで埋める
    *余白がある
  • auto-fit:スペースができたら、グリッドアイテムの幅が変わってスペースが埋められます*minmax()関数のmaxの値を1frなどサイズが自動で決まる値であることが大前提

repeat()関数でauto-fillキーワードを使用して
コンテナーに収まるだけの150pxの列トラックを作成します

1
2
3
4
5
6
.wrap{
 display: grid;
  grid-template-columns: repeat(auto-fill, 150px);
}

repeat()関数にauto-fitminmax() 関数を組み合わせてスペースを埋めます

1
2
3
4
5
6
.wrap{
 display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

Flexboxで実装した場合

1
2
3
4
5
6
.wrap{
 display: flex;
 flex-wrap: wrap;
}
.wrap .item{
 flex-basis:150px;
}
1
2
3
4
5
6
.wrap{
 display: flex;
 flex-wrap: wrap;
}
.wrap .item{
 flex-grow:1;
 flex-basis:150px;
}

ボックスの配置

<div class="wrap">
  <div class="item">box1</div>
  <div class="item">box2</div>
  <div class="item">box3</div>
  <div class="item">box4</div>
</div>

Flexbox
フレックスコンテナーの高さ(min-height: 100px;)を決めて
align-itemsの初期値はstreachなので1つ目は高さいっぱいに伸びます
フレックスアイテムのalign-selfを上書きしています
*フレックスアイテムの幅は内容物で自動調整されるので「100%」にして自動で縮小させることで均等に分割しています

box1
box2
box3
box4
.wrap {
  display: flex;  
  min-height: 100px;
}
.wrap .item {
 width:100%
}
.wrap .item:nth-child(2) {
  align-self: flex-start;
}
.wrap .item:nth-child(3) {
  align-self: center;
}
.wrap .item:nth-child(4) {
  align-self: flex-end;
}

Grid Layout
グリッド領域(エリア)の中にアイテムを配置します

box1
box2
box3
box4
.wrap {
  display: grid;
  grid-template-columns: repeat(4,1fr);
  grid-auto-rows: 100px;
}
.wrap .item:nth-child(2) {
  align-self: flex-start;
}
.wrap .item:nth-child(3) {
  align-self: center;
}
.wrap .item:nth-child(4) {
  align-self: flex-end;
}

グリッドアイテムをインライン軸に配置します
justify-items: start / center / end

box1
box2
box3
box1
box2
box3
box1
box2
box3
.wrap {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  grid-auto-rows: 50px;
  justify-items: start;
}

どっちを使う?

アイテムの複雑な配置以外で「どちらでも実装できそうなとき」ってどっちがいいのだろう?
まだ実感がないのでググってみたメモ

Grid Layout

メインとサイドバー
*サイドバーにalign-selfを指定しない場合はコンテンツの長さに関係なくサイドバーの高さはメインと同じになります

<div class="wrapper">
    <aside>Sidebar</aside>
    <main>Main</main>
</div>
@media (min-width: 1024px) {
    .wrapper {
        display: grid;
        grid-template-columns: 300px 1fr;
       /* grid-template-columns: 30% 70%;*/
    }  
    aside {
        align-self: start;
    }
}

コンテンツ全体のレイアウト(入れ替えが簡単にできる)
1024px以上で2カラム(Sideを横に)
600px以上でContentを3列に

[Main]この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。
[Content1]
[Content2]
[Content3]
<div id="container">
  <main id="main">Main</main>
  <aside id="side">Side</aside>
  <section id="section">
    <article>Content1</article>
    <article>Content2</article>
    <article>Content3</article>
 </section>
#container {
    display: grid;
/*side main sectionの位置を変更がすぐできる*/
    grid-template:
      "side" 
      "main"
      "section"/
      1fr;
    grid-gap: 1rem;
   }
 @media (min-width: 600px) {
    #section{
        display: grid;
        grid-template-columns: repeat(3, 1fr);
      /* 複数行になった場合の高さ対策 不要かも*/
        grid-auto-rows: minmax(100px, auto);
        grid-gap: 1rem;
     }
   }
@media (min-width: 1024px) {
  #container {
    display: grid;
    grid-template:
    "side main main main" auto
    "side section section section" 1fr/
    1fr 1fr 1fr 1fr;
    grid-gap: 1rem;
   }
 } 
 #main {
  grid-area: main;
  }
 #side {
   grid-area: side;
  }
 #section {
   grid-area: section;
 } 

カードのグリッド

<div class="wrap">
  <div class="item">あ</div>
  <div class="item">ああ</div>
  <div class="item">ああああ</div>
  <div class="item">ああああああああ</div>
  <div class="item">ああああああああああああああああああああああああああああああああ</div>
</div>

余談:例えば3列並びのレイアウトでアイテム数が3で割り切れないとき
アイテムを左端に揃えたい場合の最終行の対策
Flexboxだと無理やりでした
最後の要素の後ろにafterで空のブロックを作って最終行を左に揃えたりしていた

ああ
ああああ
ああああああああ
ああああああああああああああああああああああああああああああああ
.wrap {
   display:flex;
   flex-wrap: wrap;
   justify-content:space-between;
   gap: 16px;
}
.wrap::after {
    display: block;
    content:"";
    width: 30%;
}
.wrap .item{
    width:30%;
}

Grid Layoutだと
*例では最低横幅を200pxでそれよりも狭くなる場合には列を減らします

ああ
ああああ
ああああああああ
ああああああああああああああああああああああああああああああああ
.wrap{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 16px;
}

例では3列は固定され、高さの最小値(100px)を決めて高さを調整しています

ああ
ああああ
ああああああああ
ああああああああああああああああああああああああああああああああ
.wrap {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(100px, auto);
  grid-gap: 16px;
}

Flexbox

ヘッダー
左にロゴ、右にナビメニューの一般的な例

<header>
  <div class="header__wrapper">
    <a href="#"><img src="logo.svg" alt="" /></a>
    <nav class="nav"></nav>
  </div>
</header>
.header__wrapper{
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
}
.nav{
  margin-left: auto;
}

カードのコンポーネント
*600px以上で横並びに

<div class="card">
  <img src="..." alt="">
  <div class="card-text">
    <h2>見出しが入ります</h2>
    <p>テキストが入ります</p>
  </div>
</div>
.card {
    display: flex;
    flex-direction: column;
}
 
@media (min-width: 600px) {
    .card {
        flex-direction: row;
    }
}

画像を左と右交互に配置

.box {
  display: flex;
}
.box:nth-child(even) {
  flex-direction: row-reverse;
}

コンテンツの中央揃え
Flexbox

.center {
    display: flex;
    flex-direction: column;
 /* 水平方向の中央揃え */
    align-items: center; 
 /* 垂直方向の中央揃え */
    justify-content: center;
}

コンテンツの中央揃え
Grid

.center {
  display: grid;
  place-items: center;
}