flexboxで1行7カラムのカレンダー表示

# 背景
多数のdiv要素をカレンダー様表示（1行当たり7カラム表示）にしたい。

しかし、グリッドレイアウトの基本数字である12カラムと7カラムは相性が悪いため、bootstrap以外の方法が必要。


# 目的
* cssにある基本機能を使い、div要素をカレンダーのように横7カラム表示する
![スクリーンショット 2016-06-29 10.40.25.png](https://qiita-image-store.s3.amazonaws.com/0/124445/801c0083-b4b7-da6b-5bcf-32f5ecf97e67.png "スクリーンショット 2016-06-29 10.40.25.png")

* レスポンシブも対応する

# 方法
* カラム表示にflexboxを使い、レスポンシブのためにメディアクエリを追加する。

# 注意
* flexboxが対応するブラウザの割合については以下のページ参照のこと。

	http://caniuse.com/#search=flex


# 1行3カラムレイアウトを作る
## いつもの方法（bootstrap）
* bootstrapで3カラムならばdivのclassに``col-xx-4``と書く。（今回はcol-sm-4）
* 本質的にCSSは不要。だたし、枠が合った方がカラム数が明確になるためCSSによりborderを指定した。

### コード
```bootstrap_test.html
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- 文字コード指定 -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- bootstrap読み込み -->
<link rel="stylesheet" type="text/css" href="custom_bs.css">
<!-- css読み込み -->

<div class="row">
  <div class="col-sm-4">
    <p>
      1日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      2日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      3日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      4日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      5日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      6日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      7日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      8日目のメニュー
    </p>
  </div>
  <div class="col-sm-4">
    <p>
      9日目のメニュー
    </p>
  </div>
</div>
```

```custom_bs.css
div.row > div {
  border: 1px solid black;
  /* 飾り */
}
```
### 結果
* デスクトップ表示
![スクリーンショット 2016-06-29 10.33.47.png](https://qiita-image-store.s3.amazonaws.com/0/124445/053f7e30-82c7-ac8a-83d9-c4fc5f2c764c.png "スクリーンショット 2016-06-29 10.33.47.png")

* スマホ表示（イメージ）
![スクリーンショット 2016-06-29 11.56.50.png](https://qiita-image-store.s3.amazonaws.com/0/124445/0d2c44dd-e8e0-1068-96af-08514b215d50.png "スクリーンショット 2016-06-29 11.56.50.png")


## cssの機能を用いた方法（flexbox）
* 解説は後で。

### コード

```flexbox_test.html
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- 文字コード指定 -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- bootstrap読み込み。bootstrapと見た目を一緒にするために加えている。flexboxはこれが無くても動作する -->
<link rel="stylesheet" type="text/css" href="custom_fb.css">
<!-- css読み込み -->

<div class="cont">
  <!-- flexboxのコンテナ -->
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      1日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      2日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      3日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      4日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      5日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      6日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      7日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      8日目のメニュー
    </p>
  </div>
  <div class="item">
    <!-- flexboxのアイテム-->
    <p>
      9日目のメニュー
    </p>
  </div>
</div>
```

```custom_fb.css
* {
  box-sizing: border-box;
  /* widthにpaddingとborderを含めて計算を簡単にするため */
}
div.cont {
  display: flex;
  /* flexboxレイアウトを使うための命令 */
  flex-wrap: wrap;
  /* 上記で囲われたflexアイテムを折り返すように設定する */
}
div.item {
  width: 33.3%;
  /* 100%を1行当たりのカラム数（今回は3）で割った数値 */
  border: 1px solid black;
  /* 飾り */
  margin: 0;
  /* widthの計算簡単にするため（marginも使うための拡張は後で） */
}
@media (max-width: 767px) {
  /*レスポンシブ用*/
  div.cont {
    display: block;
  }
  div.item {
    width: 100%;
  }
}
```

### 結果
* デスクトップ表示
![スクリーンショット 2016-06-29 10.34.58.png](https://qiita-image-store.s3.amazonaws.com/0/124445/14b25751-f71c-4ad4-cc80-b44763291e5a.png "スクリーンショット 2016-06-29 10.34.58.png")

* スマホ表示（イメージ）
![スクリーンショット 2016-06-29 11.57.38.png](https://qiita-image-store.s3.amazonaws.com/0/124445/28c2394a-4178-b912-b2ff-16e51f715ed9.png "スクリーンショット 2016-06-29 11.57.38.png")


# 本題：1行7カラムレイアウトを作る
bootstrapでやる方法はわからなかったので、flexboxの例のみ。

## flexbox
### コード
* flexbox_test.htmlは上記と同じ。
* custom_fb.cssの変更は **1行だけ** 。div.itemのwidthを33.3%から14.28%に書き換える。それだけ。
	* 100 / 7 = 14.28

```custom_fb.css
* {
  box-sizing: border-box;
  /* widthにpaddingとborderを含めて計算を簡単にするため */
}
div.cont {
  display: flex;
  /* flexboxレイアウトを使うための命令 */
  flex-wrap: wrap;
  /* 上記で囲われたflexアイテムを折り返すように設定する */
}
div.item {
  width: 14.28%;
  /* 100%を1行当たりのカラム数（今回は7）で割った数値 */
  border: 1px solid black;
  /* 飾り */
  margin: 0;
  /* widthの計算簡単にするため（marginも使うための拡張は後で） */
}
@media (max-width: 767px) {
  /*レスポンシブ用*/
  div.cont {
    display: block;
  }
  div.item {
    width: 100%;
  }
}
```

## 結果
* デスクトップ表示
![スクリーンショット 2016-06-29 10.40.25.png](https://qiita-image-store.s3.amazonaws.com/0/124445/801c0083-b4b7-da6b-5bcf-32f5ecf97e67.png "スクリーンショット 2016-06-29 10.40.25.png")

* スマホ表示（イメージ）
省略

# 解説
* flexboxの基本は配置したい要素（今回の例ではdiv.item）をdiplay: flex;が指定された要素（div.cont）で囲うこと。

	http://shanabrian.com/web/css3/flexible-box-layout.php

```xxx.html
<div class="cont">
  <div class="item">
  </div>
  <div class="item">
  </div>
</div>
```

```yyy.css
div.cont {
  display: flex;
}
```

* このままだとdiv.itemが横に並ぶだけなので折り返すためにflex-wrap: wrap;を追加する

```yyy.css
div.cont {
  display: flex;
  flex-wrap: wrap;
}
```

* 任意のカラム数で折り返してもらうためdiv.itemのwidthを指定する。このときwidthは%で指定し、100%を目的とするカラム数で割った値に設定する。7カラムであれば14.28%。

```yyy.css
div.cont {
  display: flex;
  flex-wrap: wrap;
}
div.item {
  width: 14.28%
}
```

* レスポンシブになるように、幅が狭い場合（本例では767px以下）ではflexboxを使わない様にする

```yyy.css
div.cont {
  display: flex;
  flex-wrap: wrap;
}
div.item {
  width: 14.28%
}
@media (max-width: 767px) {
  div.cont {
    display: block;
  }
}
```

# 応用(marginも設定したい場合)
* 単純にdiv.itemにmarginを指定すると1行当たりに表示したいカラム数が減ってしまいます。
* div.itemのwidthはmarginの合計を差し引いた値をカラム数で割った値にします。
* widthの計算の単位を揃えるため、div.itemにmarginも設定したい場合は、%単位で指定してください。
	* (100% - margin (%) * (カラム数 - 1) * 2 + margin (%) * 2}) / カラム数}
* 例えば、margin: 1%;でカラム数7の場合は以下のかんじです。

```yyy.css
div.item {
  width: 12.28% /* = 100 - 1 * (7 - 1) * 2 + 1 * 2  */
  margin: 1%
}
```

* scssだったら変数を使うと一般化できます

```zzz.scss
$n: 7; //1行当たりのカラム数
$m: 1%; //カラム左右のマージン
// 省略
div.item {
  width: calc((100% - #{$m * ($n - 1) * 2 + $m * 2}) / #{$n});
  margin: $m;
}
// 省略
```

## 結果
![スクリーンショット 2016-06-29 11.14.22.png](https://qiita-image-store.s3.amazonaws.com/0/124445/edaa810d-79f0-f36b-611e-ef34d445471d.png "スクリーンショット 2016-06-29 11.14.22.png")


# その他
* paddingを使う時は、widthの計算を簡単にするため、``* {box-sizing: border-box;}``を使うと楽

	http://phiary.me/css3-box-sizing/

* 今回の例では効果がわからないけど、flexboxでレイアウトしたitemの高さはデフォルトで __揃います__ 。floatでこれをやるとすごい面倒。

	http://shanabrian.com/web/html-css-js-technics/css-equal-height.php

* コードは省略するけど、こんな感じに高さが揃った表示もflexboxだとすごい簡単だった。

	![スクリーンショット 2016-06-29 11.27.11.png](https://qiita-image-store.s3.amazonaws.com/0/124445/4498f1e3-2715-5330-2450-f97da47ab9bb.png "スクリーンショット 2016-06-29 11.27.11.png")


# 最後に
* bootstrapだとhtmlに見た目も書くことになって気持ち悪かったのが、flexboxだと楽々すっきり。