Grid LayoutのIE向けベンダープレフィックスをAutoprefixerに任せるなら「自動配置」に注意する

# はじめに

CSSベンダープレフィックスは手打ちでやろうとすると割と厄介ですが、[Autoprefixer](https://github.com/postcss/autoprefixer)は自動でこのベンダープレフィックスを付与してくれるため、コーディング量を少なくしメンテナンス性も高めることができるとても便利なものです。

しかし、Autoprefixerの挙動を理解していないと、IEと従来版Edge対応時に「自動配置」に振り回されるかもしれないので備忘録を残します。
決して、Autoprefixerが悪いという話ではなく、**使うときはここに気をつけよう**という趣旨になります。

なお、Autoprefixerの導入やCSS Grid Layoutの詳しい使い方等については、本記事では扱いません。

CSS Grid Layoutについては以下の記事がとても参考になると思います。
[CSS Grid Layout を極める！（基礎編）](https://qiita.com/kura07/items/e633b35e33e43240d363)
本記事でも**この記事の例として出ているレイアウトを元に**進めていきます。

([Autoprefixer](https://github.com/postcss/autoprefixer) version: 10.2.5)

# 結論

- Grid Layoutでグリッドを手動配置し、Autoprefixerで`-ms-`ベンダープレフィックスをつけるときは、エリア名をつけて配置する
- 本当にGridでないといけないのか再考する


# CSS Grid LayoutのIEベンダープレフィックス

CSS Grid Layoutは縦横の2次元レイアウトを簡単に作成できる便利なCSSの機能です。

[Can I Use](https://caniuse.com/?search=grid)によると、IEでのサポートはこんな感じ。
![スクリーンショット 2021-03-22 11.20.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/924363/3a93e6fb-e4c0-5250-1cff-7a03319595e5.png)

> Partial support with prefix: `-ms-`

`-ms-`というベンダープレフィックスを使えば、一部使用できます。
*Partial support*とは古いバージョンのgridには対応してるよーという意味とのこと。

# Autoprefixerでgridに自動で`-ms-`プレフィックスを付与する

## Autoprefixerのgridのプレフィックスオプションを有効にする

※筆者の環境ではgulpを導入しているため、gulpでの説明となります。

Autoprefixerでは、gridのIE向けベンダープレフィックスはデフォルトで無効になっています。
まず、それを有効にする必要があるので、`grid: 'autoplace'`を指定します。

```javascript:公式Readmeに付け足し
gulp.task('autoprefixer', () => {
  const autoprefixer = require('autoprefixer')
  const sourcemaps = require('gulp-sourcemaps')
  const postcss = require('gulp-postcss')

  return gulp.src('./src/*.css')
    .pipe(sourcemaps.init())
    .pipe(postcss([ autoprefixer({ grid: 'autoplace' }) ]))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./dest'))
})
```

なぜデフォルト無効なのかについては[Readme](https://github.com/postcss/autoprefixer#does-autoprefixer-polyfill-grid-layout-for-ie)で言及されており、

> Autoprefixer can be used to translate modern CSS Grid syntax into IE 10 and IE 11 syntax, but this polyfill will not work in 100% of cases. This is why it is disabled by default.

今のCSS Gridの記法をIE用に変換するpolyfillが完璧ではないため、とのことです。

そもそもIE対応が必要な上、Grid Layoutを使用する時は、明確な目的がある場合に絞るべきでしょう。

## CSS Grid Layoutを適用する

ここでは、例えば以下のようなレイアウトを実現したいとします。

![sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/924363/3de16251-2059-b7e0-f333-a84e053955da.jpeg)

以下のHTMLにGrid Layoutを当てていきます。

```html:grid_sample.html
<div class="bl_grid">
  <div class="bl_grid_item">A</div>
  <div class="bl_grid_item">B</div>
  <div class="bl_grid_item">C</div>
</div>
```

必ず指定するのはもちろん`display: grid;`です。
また、幅と高さも指定してしまいます。

```css:style.css
.bl_grid {
  display: grid;
  grid-template-rows: 100px 50px;
  grid-template-columns: 100px 1fr;
}
```

問題はここからです。

今回は自動配置ではなく、以下のように配置を指定する必要があります。

```
A|B
A|C
```

方法は2つあります。

1. `grid-row`と`grid-column`で行列番号を指定する
2. エリアに名前をつけて指定する

### 配置指定：行列番号指定

では、まず1つ目の方法でやってみます。

```css:style.css
.bl_grid {
  display: grid;
  grid-template-rows: 100px 50px;
  grid-template-columns: 100px 1fr;
}

.bl_grid_item:first-child {
  grid-row: 1 / 3;
  grid-column: 1 / 2;
}

.bl_grid_item:nth-child(2) {
  grid-row: 1 / 2;
  grid-column: 2 / 3;
}

.bl_grid_item:nth-child(3) {
  grid-row: 2 / 3;
  grid-column: 2 / 3;
}
```

いよいよ、Autoprefixerの出番です。
これでAutoprefixerを走らせてみると、以下のように出力されます。

```css:dest/style.css
.bl_grid {
  display: -ms-grid;
  display: grid;

  -ms-grid-rows: 100px 50px;
  grid-template-rows: 100px 50px;
  -ms-grid-columns: 100px 1fr;
  grid-template-columns: 100px 1fr;
}

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

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

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

.bl_grid > *:nth-child(4) {
  -ms-grid-row: 2;
  -ms-grid-column: 2;
}

.bl_grid_item:first-child {
  -ms-grid-row: 1; 
  -ms-grid-row-span: 2; 
  grid-row: 1 / 3;

  -ms-grid-column: 1;
  -ms-grid-column-span: 1;
  grid-column: 1 / 2;
}

.bl_grid_item:nth-child(2) {
  -ms-grid-row: 1; 
  -ms-grid-row-span: 1; 
  grid-row: 1 / 2;

  -ms-grid-column: 2;
  -ms-grid-column-span: 1;
  grid-column: 2 / 3;
}

.bl_grid_item:nth-child(3) {
  -ms-grid-row: 2; 
  -ms-grid-row-span: 1; 
  grid-row: 2 / 3;

  -ms-grid-column: 2;
  -ms-grid-column-span: 1;
  grid-column: 2 / 3;
}
```

全体的に`-ms-`が付与されました。

`.bl_grid_item:first-child`の部分を見てみます。

```css
.bl_grid_item:first-child {
  -ms-grid-row: 1; 
  -ms-grid-row-span: 2; 
  grid-row: 1 / 3;

  -ms-grid-column: 1;
  -ms-grid-column-span: 1;
  grid-column: 1 / 2;
}
```

`-ms-grid-row-span`が指定していなかったオプションですが、その**グリッドエリアをどれくらい引き伸ばすか**を指定することが出来ます。
`-ms-grid-row-span`と`-ms-grid-column-span`はIEと従来版Edgeにのみ存在するオプションで、他のブラウザでは`grid-row: 1 span 2;`のように指定することで同じ効果が得られます。

さて、いい感じに指定できたように見えますが、実はこれ、**うまく機能しませず**、↓こうなります。（AとCが重なっている）

![Untitled Diagram.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/924363/ce8ab873-dfbb-3342-917f-54701a6ffb65.jpeg)


なぜならば、以下の記述が追加されているためです。

```css:自動追加された部分
.bl_grid > *:nth-child(1) {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}

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

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

.bl_grid > *:nth-child(4) {
  -ms-grid-row: 2;
  -ms-grid-column: 2;
}
```

なんぞこれ。

コードを読むと、`.bl_grid`の子要素を左上から順番に4つ並べているようです。
これはAutoprefixerの**IEのグリッド自動配置機能**によるものです。

これは、先程指定したオプションの`grid: 'autoplace'`によって有効になり、`grid-template-rows`と`grid-template-columns`が指定されているときに自動配置を行うという機能になります。

この機能によって自分で書いた記述ではなく**自動追加された方が参照されるので、意図した配置になりません**。（下に自分が記述したほうが優先されない理由は、CSSの優先順位によるものです。）

Autoprefixerのオプション`  /* autoprefixer grid: no-autoplace */`をグリッドで指定して、**自動配置を無効にする**ことで回避できます。

```css
.bl_grid {
  /* autoprefixer grid: no-autoplace */
  display: grid;
  grid-template-rows: 100px 50px;
  grid-template-columns: 100px 1fr;
}
```

しかし、CSS内にAutoprefixerのオプションを記述するのはあまりスマートではない気がしてしまいます。

### 配置指定： 【推奨】 エリア名指定

Readmeにも以下のように書いてあります。

> If manual cell placement is required, we recommend using `grid-template` or `grid-template-areas`
訳：手動でセルを配置する場合は、`grid-template`もしくは`grid-template-areas`の使用を推奨します。

また、

> If `grid-template` or `grid-template-areas` has been set, Autoprefixer will use area based cell placement instead.
訳：`grid-template`か`grid-template-areas`が設定されている場合、エリアに基づくセル配置を行います。

ということで、手動で配置したい場合は大人しく**エリアに名前をつけて指定**するほうがベターでしょう。

幅と高さは指定していましたので、`grid-template-areas`を使用してみます。

```css:エリア名指定
.bl_grid {
  display: grid;
  grid-template-rows: 100px 50px;
  grid-template-columns: 100px 1fr;
  grid-template-areas:
  'gridA gridB'
  'gridA gridC';
}

.bl_grid_item:first-child {
  grid-area: gridA;
}

.bl_grid_item:nth-child(2) {
  grid-area: gridB;
}

.bl_grid_item:nth-child(3) {
  grid-area: gridC;
}
```

もしくは、短縮プロパティ`grid-template`を用いると少し短く記述することが出来ます。

```css:エリア名指定（短縮プロパティ）
.bl_grid {
  display: grid;
  grid-template: 
    'gridA gridB' 100px
    'gridA gridC' 50px
    / 100px 1fr;
}

.bl_grid_item:first-child {
  grid-area: gridA;
}

.bl_grid_item:nth-child(2) {
  grid-area: gridB;
}

.bl_grid_item:nth-child(3) {
  grid-area: gridC;
}
```

さて、Autoprefixerくんを走らせると、、、

```css:dest/style.css
.bl_grid {
  display: -ms-grid;
  display: grid;

  -ms-grid-rows: 100px 50px;
  grid-template-rows: 100px 50px;
  -ms-grid-columns: 100px 1fr;
  grid-template-columns: 100px 1fr;
  grid-template-areas:
    "gridA gridB"
    "gridA gridC";
}

.bl_grid_item:first-child {
  -ms-grid-row: 1;
  -ms-grid-row-span: 2;
  -ms-grid-column: 1;

  grid-area: gridA;
}

.bl_grid_item:nth-child(2) {
  -ms-grid-row: 1;
  -ms-grid-column: 2;

  grid-area: gridB;
}

.bl_grid_item:nth-child(3) {
  -ms-grid-row: 2;
  -ms-grid-column: 2;

  grid-area: gridC;
}
```

自動配置の部分がまるっとなくなりました。

`grid-template-areas`や`grid-area`にプレフィックスがありませんが、IEと従来版Edgeにはないので正常な動きです。
代わりに`-ms-grid-row`、`-ms-grid-column`、`-ms-grid-row-span`、`-ms-grid-column-span`で実現しています。
ここを自動でやってくれるのはやっぱり便利ですね。

# まとめ

Grid Layoutを使うときは、今後はエリアに名前をつける方で実装しようと思います。

grid内のアイテムもclassの`nth-child`で指定していたので今回問題に気づきましたが、idで指定していればそちらが優先されて気付かなかったということもあり得たかもしれません。（実証していませんが）

このようなプラグインは便利ですが、ちゃんとReadmeを読んで挙動を確認して、過信しすぎず出力されたものをちゃんと確認しようという学びになりました。