IE11 と CSS Grid の 7 つの約束

## はじめに

> *2020 年 7 月現在、私のシェア率は国内でだいたい10% 弱です。
どうか、私への対応を諦める前にそのことを覚えておいて欲しいのです。
- Internet Explorer 11*

[WebブラウザシェアランキングTOP10(日本国内・世界)](https://webrage.jp/techblog/pc_browser_share/)

CSS Grid を IE11 で使用する上での注意点を確認していきます。
※ Gulp および Autoprefixer を使用した IE11 対応を前提とします。また環境構築の手法については本記事では触れないものとします。

## CSS Grid が私に何を求めているのか、私がそれを理解できるように記述してほしいのです

> *私はモダンなプロパティをベンダープレフィックス無しで理解することができません。
CSS Grid も例外ではないため、私の友人である Autoprefixer を介してそれを理解できるように変換して欲しいのです*

ひとつひとつベンダープレフィックスを付与していくのはかなり工数がかかってしまうため、本記事では `Gulp` と `Autoprefixer` によって自動での付与・変換を行なうことを前提とします。

前述したように npm および Gulp による環境構築については触れませんが、Autoprefixer のバージョンは最低限気にしておくのが良いかと思います。
すでに環境構築されている場合でも、Autoprefixer のバージョンが古いとそもそも対応ができません。

[Autoprefixerが進化してCSS GridのIE11 対応がバリ楽になった（2017年〜2018年）](https://qiita.com/tonkotsuboy_com/items/bce73685cd6b1ea511dc)

Autoprefixer のリリースノートを確認したところ、 **2020.7.6 現在での最新バージョンは [9.8.3](https://github.com/postcss/autoprefixer/releases/tag/9.8.3)** ですので、本記事ではこのバージョンを使用していきます。
自動配置の仕様変更や警告の追加などが更新されているようですので、可能であれば最新版にしておくのが良いでしょう。

### 補足：browserlist の記述方法も確認しておきましょう

[Autoprefixer 9.6 でのバージョンアップ](https://github.com/postcss/autoprefixer/releases/tag/9.6.0) により、ターゲットブラウザを指定する `browsers` オプションが非推奨となっています。
Autoprefixer の最新版を使用する場合、こちらも留意しておきましょう。
ざっくり書いておくと、「ターゲットブラウザの指定を他のツールでも共有したいので、`package.json` などに記述してツール間で共有する」というような内容になります。

参考記事：[ハローハリネズミ - Browserslist でターゲットブラウザの設定](https://qiita.com/toshifumiimanishi/items/1e68d8229dddef0895ce)

## 要素の数が決まっていない時は、おとなしく Flexbox を使用してください

> *私が CSS Grid に対して、他のモダンブラウザと同様の挙動でないことを知っておいてください。
要素の数がグリッドで定義されている個数を超えた場合、モダンブラウザではグリッドが自動で追加生成されるようですが、私にはできません。*

### 挙動確認

4 行 2 列のグリッドコンテナと、内包される 10 個の子要素を用意して挙動確認を行なっていきます。

```html
<div class="gridContainer">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 5"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 6"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 7"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 8"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 9"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 10"></div>
</div>
```

```css:Autoprefixerによる変換前
.gridContainer {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10px;
  margin: 10px;
}

.girdItem {
  border: 1px solid #000;
}
```

IE11 では Autoprefixer によって以下のようなプロパティが適用されます。

<details><summary>Autoprefixer による変換後コード</summary><div>

```css:Autoprefixerによる変換後
.gridContainer {
  display: -ms-grid;
  -ms-grid-columns: 1fr 10px 1fr 10px 1fr 10px 1fr;
  -ms-grid-rows: 1fr 10px 1fr;
  margin: 10px;
}

.girdItem {
  border: 1px solid #000;
}

.gridContainer > *:nth-child(1) {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}

.gridContainer > *:nth-child(2) {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
}

.gridContainer > *:nth-child(3) {
  -ms-grid-row: 1;
  -ms-grid-column: 5;
}

.gridContainer > *:nth-child(4) {
  -ms-grid-row: 1;
  -ms-grid-column: 7;
}

.gridContainer > *:nth-child(5) {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
}

.gridContainer > *:nth-child(6) {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
}

.gridContainer > *:nth-child(7) {
  -ms-grid-row: 3;
  -ms-grid-column: 5;
}

.gridContainer > *:nth-child(8) {
  -ms-grid-row: 3;
  -ms-grid-column: 7;
}
```

</div></details>


こちらの挙動確認を行なった画面のキャプチャが以下になります。

- Google Chrome
<img width="840" alt="スクリーンショット 2020-07-05 15.29.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390806/2e8e37fa-0b60-e2b6-4155-b49b1adb95f2.png">

- IE11
<img width="823" alt="スクリーンショット 2020-07-05 15.29.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390806/5c283b35-77d8-14f6-162f-929b73b79a45.png">


Google Chrome では、9, 10 個目の要素を配置するための グリッドが自動生成されますが、IE11 では 8 個目までしか表示されていません。
コンパイル後の CSS を確認したところ、グリッド外の 9, 10 個目の要素には配置に関するプロパティが Autoprefixer で生成されないため、一つ目の子要素と同じ位置に重なってしまうようです。

### 対応

もし要素の数が定まっているのであれば、グリッドコンテナを 4 行 × 3 列に変更してしまえば解決しますが、例えば 「**同じコンテナを、要素数の異なる複数箇所で使用する**」 ということを考えてみましょう。
行数または列数が異なれば、その度に別々のグリッドコンテナを用意する必要が出てきます。

同じような要素を内包するグリッドコンテナであっても、コンテナごとに要素数が定まっていないような場合は Flexbox を使用した実装の方が柔軟に対応できるかと思います。
**本当に CSS Grid を使用するべきかどうか、実装前に一度じっくり考えてみるようにしましょう。**

### 補足：gap プロパティの挙動

`gap` プロパティ（旧 `grid-gap` プロパティ）は IE11 非対応ですので、Autoprefixer によってグリッドエリアの一つの行または列として変換されます。
上記の例でも、以下のように変換されていることが確認できます。

```css:Autoprefixerによる変換前
.gridContainer {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10px;
  margin: 10px;
}
```

```css:Autoprefixerによる変換後
.gridContainer {
  display: -ms-grid;
  -ms-grid-columns: 1fr 10px 1fr 10px 1fr 10px 1fr;
  -ms-grid-rows: 1fr 10px 1fr;
  margin: 10px;
}
```

なお IE11 での自動配置は、上記変換後の行列数を考慮した上で `-ms-grid-row` および `-ms-grid-column` による配置を明示的に追加することで実現されているようです。


## `grid: true` オプションが何を求めているのか、私の友人には分かりません

以前の Autoprefixer では `grid: true` オプションを Autoprefixer に設定することによって IE11 での自動配置を行なっていました。
しかしこのプロパティは現在 `no-autoplace` のエイリアスであり、廃止予定のようです。

参考記事：[autoprefixerでgridを有効にしているのにIE11でレイアウトが崩れる](https://ja.stackoverflow.com/questions/55200/)

```js:gulpfile.js
gulp.task("default", () => {
  return gulp.src("src/style.css")
    .pipe(autoprefixer({ grid: true })) // grid: true の設定による自動配置の設定（現在は非推奨）
    .pipe(gulp.dest("dist"));
});
```

### 対応

正しく自動配置を設定するためには `grid: 'autoplace'` プロパティをオプションとして設定します。

```js:gulpfile.js
gulp.task("default", () => {
  return gulp.src("src/style.css")
    .pipe(autoprefixer({ grid: 'autoplace' })) // 現在の CSS Grid 自動配置のオプション設定
    .pipe(gulp.dest("dist"));
});
```


## `grid-template` での自動配置の使用をする前に覚えて置いて欲しいのです。その記述は、自動配置では使うべきでないということを。

> *私の友人は grid-template での自動配置を私に伝えることはできません。*

### 挙動確認

`grid-template` を使用して 4 列 2 行の グリッドコンテナを作成します。

```css:Autoprefixerによる変換前
.gridContainer {
  display: grid;
  grid-template:
  "areaA areaB areaC areaD" 1fr
  "areaE areaF areaG areaH" 1fr /
  1fr 1fr 1fr 1fr;
  gap: 10px;
  margin: 10px;
}

.girdItem {
  border: 1px solid #000;
}
```

子要素に `grid-area` プロパティを指定しなかった場合、IE11 では以下のように変換されます。

```css:Autoprefixerによる変換後
.gridContainer {
  display: -ms-grid;
  -ms-grid-columns: 1fr 10px 1fr 10px 1fr 10px 1fr;
  -ms-grid-rows: 1fr 10px 1fr;
  margin: 10px;
}
```

グリッドコンテナの変換はうまくいっているようですが、この記述では**子要素の自動配置が行われません**。
また、以下のようなエラーがコンソールに出力されます。

```console
gulp-postcss: css/top.css
autoprefixer: xxx/top.css:16:3: Can not find grid areas: areaA, areaB, areaC, areaD, areaE, areaF, areaG, areaH
```

「**グリッドコンテナでエリアが指定されているのに使われていないよ！**」ということのようです。
こちらのエラーは、`.` を使用してエリアを指定することで回避できます（MDN のサンプルで使用されており、grid-template で明示的にエリアを指定しない方法のようです）が、このエラー対策をしたところで結局子要素の自動配置までは行われません。

```css:
.gridContainer {
  display: grid;
  grid-template:
  ". . . ." 1fr
  ". . . ." 1fr /
  1fr 1fr 1fr 1fr;
  gap: 10px;
  margin: 10px;
}
```

### 対応

自動配置を前提として CSS Grid を使用する場合、`grid-template` は使用しない方が良さそうです。
`grid-template-columns` および `grid-template-rows` による記述を行いましょう。

## CSS Grid の行と列を、必ずどちらも記述してください

> *私が言うことを聞かないだとか、頑固だとか、怠けているからといって叱る前に、私が何故その記述を理解できないか気づいて下さい。もしかしたら、記法に問題があるかもしれないし、私の友人が変換できていないのかもしれない。*

[Autoprefixer 9.4](https://github.com/postcss/autoprefixer/releases/tag/9.4.0)  でのバージョンアップ以降の自動配置機能を使用するには、グリッドコンテナの宣言時に**行と列の両方を定義する必要があります。**

### 挙動確認

```css:Autoprefixerによる変換前
.gridContainer {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin: 10px;
}
```

```css:Autoprefixerによる変換後
.gridContainer {
  display: -ms-grid;
  -ms-grid-columns: 1fr 10px 1fr 10px 1fr 10px 1fr;
  margin: 10px;
}
```

こちらもコンテナの変換はうまくいっているようですが、子要素の自動配置は行われません。
また、片方のみの記述でコンパイルを行なった場合 Autoprefixer 9.8.3 では以下のようなエラーが出力されます。

```console
gulp-postcss: css/top.css
autoprefixer: xxx/top.css:16:3: Autoplacement does not work without grid-template-rows property
```

[Autoprefixer 9.4 のリリースノート](https://github.com/postcss/autoprefixer/releases/tag/9.4.0) でも、以下のような記載があります。

> In order to use the new autoplacement feature, you must define both rows and columns when declaring the grid template.

## 行と列の定義は同じ宣言ブロック内に記述してください

> *あなたにとって良い CSS Grid の扱い方でも、方法によっては私には決して伝わりません。*

### 挙動確認

同じようなコンテンツを内包し、行数の異なるグリッド（例えば 2 行 3 列、2 行 3 列、2 行 4 列の 3 パターン）があったと考えます。

```html
<div class="gridContainer-2x2">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
</div>

<div class="gridContainer-3x2">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 5"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 6"></div>
</div>

<div class="gridContainer-4x2">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 5"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 6"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 7"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 8"></div>
</div>
```

![スクリーンショット 2020-07-07 17.22.50（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390806/d8434837-0201-1b62-5223-eb2d004aee09.png)


『要素の数が決まっていない時は、おとなしく Flexbox を使用してください。』で述べたように、**IE11 では 要素数がグリッドコンテナからはみ出た場合にグリッドコンテナの自動生成が行われません。**
一つ一つ別々のコンテナを記述していくと下記のようになります。

```scss
.gridContainer-2x2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10px;
  margin: 10px;
  text-align: center;
}

.gridContainer-3x2 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10px;
  margin: 10px;
  text-align: center;
}

.gridContainer-4x2 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10px;
  margin: 10px;
  text-align: center;
}
```

同じような記述が多くなってしまい、追加や変更のたびに全て書き換えなければ行けないのは少し手間です。
`grid-template-columns` と `grid-template-rows` を切り離し、以下のようにリファクタリングしたとします。

```html
<div class="gridContainer -col2 -row2">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
</div>

<div class="gridContainer -col3 -row2">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 5"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 6"></div>
</div>

<div class="gridContainer -col4 -row2">
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 1"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 2"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 3"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 4"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 5"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 6"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 7"></div>
  <div class="girdItem"><img src="/img/doggo.png" alt="イッヌ 8"></div>
</div>
```

```scss:Autoprefixerによる変換前
.gridContainer {
  display: grid;
  gap: 10px;
  margin: 10px;
  text-align: center;

  &.-col2 {
    grid-template-columns: repeat(2, 1fr);
  }

  &.-col3 {
    grid-template-columns: repeat(3, 1fr);
  }

  &.-col4 {
    grid-template-columns: repeat(4, 1fr);
  }

  &.-row2 {
    grid-template-rows: repeat(2, 1fr);
  }
}
```

共通部分が `gridContainer` にまとまり、SCSS での記述はスッキリした気がしますが、残念ながらこの記述では IE11 での自動配置は行なわれません。

```css:Autoprefixerによる変換後
.gridContainer {
  display: -ms-grid;
  display: grid;
  gap: 10px;
  margin: 10px;
  text-align: center;
}

.gridContainer.-col2 {
  -ms-grid-columns: (1fr)[2];
  grid-template-columns: repeat(2, 1fr);
}

.gridContainer.-col3 {
  -ms-grid-columns: (1fr)[3];
  grid-template-columns: repeat(3, 1fr);
}

.gridContainer.-col4 {
  -ms-grid-columns: (1fr)[4];
  grid-template-columns: repeat(4, 1fr);
}

.gridContainer.-row2 {
  -ms-grid-rows: (1fr)[2];
  grid-template-rows: repeat(2, 1fr);
}
```

### 対応

Autoprefixer が自動配置に対応するためには、同じ宣言ブロック内で `grid-template-columns` と `grid-template-rows` が定義されている必要があるようです。
また、 `gap` プロパティを使用する場合は『補足：gap プロパティの挙動』で記述しているような振る舞いとなりますので、こちらも同じ宣言ブロック内に記述しておく必要があります。
正直なところあまり綺麗な書き方だとは思えませんが、以下のようにリファクタリングを行います。

```scss:Autoprefixerによる変換前
.gridContainer {
  display: grid;
  margin: 10px;
  text-align: center;

  &.-col2.-row2 {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 10px;
  }

  &.-col3.-row2 {
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 10px;
  }

  &.-col4.-row2 {
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: 10px;
  }
}
```

この記述では Autoprefixer による IE11 向けグリッドの再生成、および自動配置が行なわれます。

<details><summary>Autoprefixer による変換後コード</summary><div>


```css:Autoprefixerによる変換後
.gridContainer {
  display: -ms-grid;
  display: grid;
  margin: 10px;
  text-align: center;
}

.gridContainer.-col2.-row2 {
  -ms-grid-columns: 1fr 10px 1fr;
  -ms-grid-rows: 1fr 10px 1fr;
}

.gridContainer.-col2.-row2 > *:nth-child(1) {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}

.gridContainer.-col2.-row2 > *:nth-child(2) {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
}

.gridContainer.-col2.-row2 > *:nth-child(3) {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
}

.gridContainer.-col2.-row2 > *:nth-child(4) {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
}

.gridContainer.-col3.-row2 {
  -ms-grid-columns: 1fr 10px 1fr 10px 1fr;
  -ms-grid-rows: 1fr 10px 1fr;
}

.gridContainer.-col3.-row2 > *:nth-child(1) {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}

.gridContainer.-col3.-row2 > *:nth-child(2) {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
}

.gridContainer.-col3.-row2 > *:nth-child(3) {
  -ms-grid-row: 1;
  -ms-grid-column: 5;
}

.gridContainer.-col3.-row2 > *:nth-child(4) {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
}

.gridContainer.-col3.-row2 > *:nth-child(5) {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
}

.gridContainer.-col3.-row2 > *:nth-child(6) {
  -ms-grid-row: 3;
  -ms-grid-column: 5;
}

.gridContainer.-col4.-row2 {
  -ms-grid-columns: 1fr 10px 1fr 10px 1fr 10px 1fr;
  -ms-grid-rows: 1fr 10px 1fr;
}

.gridContainer.-col4.-row2 > *:nth-child(1) {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}

.gridContainer.-col4.-row2 > *:nth-child(2) {
  -ms-grid-row: 1;
  -ms-grid-column: 3;
}

.gridContainer.-col4.-row2 > *:nth-child(3) {
  -ms-grid-row: 1;
  -ms-grid-column: 5;
}

.gridContainer.-col4.-row2 > *:nth-child(4) {
  -ms-grid-row: 1;
  -ms-grid-column: 7;
}

.gridContainer.-col4.-row2 > *:nth-child(5) {
  -ms-grid-row: 3;
  -ms-grid-column: 1;
}

.gridContainer.-col4.-row2 > *:nth-child(6) {
  -ms-grid-row: 3;
  -ms-grid-column: 3;
}

.gridContainer.-col4.-row2 > *:nth-child(7) {
  -ms-grid-row: 3;
  -ms-grid-column: 5;
}

.gridContainer.-col4.-row2 > *:nth-child(8) {
  -ms-grid-row: 3;
  -ms-grid-column: 7;
}
```

</div></details>


## 最後のその時まで、できるだけ IE11 対応をして欲しいのです

> *このようなことは言わないで下さい。
｢もう見てはいられない。｣｢居たたまれない。｣などと。
あなたが IE11 対応をしてくれるから、最後の日までコンテンツを多くの人に届けることができるのです。*

> *忘れないで下さい、私は生涯ユーザーを一番愛しているのです。*