Edge のバグと戦った話


この記事は、<a href="https://qiita.com/advent-calendar/2019/cam-inc">CAM Advent Calendar</a> 25日目の記事です。:golf:
前回は <a href="https://qiita.com/Jung0">Jung0</a> さんの <a href="https://qiita.com/Jung0/items/50d41f8f9bf021c2ee57">Androidアプリでのエラー処理について</a> を紹介しました。

## :cat: 自己紹介

今年の4月からフロントエンジニアとして働いています、kidoyuna です。
学生時代はデータ分析を勉強していたのですが、アイドルが好きすぎたため、
アーティストファンサイトを展開している今の会社に入社することを決めました。
Web 未経験でしたが、周りに助けられながら日々奮闘中です。
## :cat: 前提条件

自分用メモでもある、Edge 戦記です。

ちなみに、Webcomponents を使用したプロジェクトでのお話です。
これについては、2日目 <a href="https://qiita.com/MuuKojima">MuuKojima</a> さんの
<a href="https://qiita.com/MuuKojima/items/c8eddbf1a577d898fd8d">Web Components入門: フルに導入されたプロジェクトに入った結果とサンプル実装</a>をご覧ください。

ShadowDOM が関係してくるので、まずは各ブラウザの<a href="https://caniuse.com/#feat=shadowdomv1">対応状況</a>から。
2019年12月現在 Firefox, Chrome, Opera は対応済み。Safari は一部バグあり。IE と Edge は未対応です。
そのため、幅広いブラウザに対応するには Webcomponents の <a href="https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs">Polyfill</a> の利用が必要となります。

## :cat: Polyfill について

Polyfill とは、Webcomponents のような最新技術に未対応のブラウザのために、
現在搭載されている機能でほぼ同等の機能を展開してくれるライブラリです。ありがたい...

Webcomponents の大きな特徴の1つである ShadowDOM とは、DOM を外から見えないようにカプセル化するというものです。
これを Polyfill で再現したものが ShadyDOM です。

ShadyDOM も ShadowDOM 同様、カプセル化されているので普通に考えれば外からスタイルを当てることはできません。
それを可能にする Polyfill が、<a href="https://github.com/webcomponents/polyfills/tree/master/packages/shadycss">ShadyCSS</a>です。


## :bug: バグ1つ目

上記の ShadyCSS を使用してバグが発生しました。

公式の <a href="https://github.com/webcomponents/polyfills/tree/master/packages/shadycss#shadycss">README</a> には以下のようにあります。（日本語のコメントは筆者が追加しました）
>```
<my-element>
  <!-- shadow-root -->
  <style>
    :host {
      display: block;
    }
    #container slot::slotted(*) {
      color: gray;
    }
    #foo {
      color: black;
    }
  </style>
  <div id="foo">Shadow</div>
  <div id="container">
    <slot>
      <!-- span distributed here -->
    </slot>
  </div>
  <!-- /shadow-root -->
  <span>Light</span> // このspanごと、上のslotタグが置き換わる
</my-element>
```
becomes:
>
```
<style scope="my-element">
  my-element {
    display: block;
  }
  #container.my-element > * {
    color: gray;
  }
  #foo.my-element {
    color: black;
}
</style>
<my-element>
  <div id="foo">Shadow</div>
  <div id="container">
    <span>Light</span> // slotがあったところにspanが入った状態
  </div>
</my-element>
```
```
Selector scoping
You must have a selector for ascendants of the <slot> element when using the ::slotted pseudo-element.
You cannot use any selector for the <slot> element. Rules like .foo .bar::slotted(*) are not supported.
```

と、あるので
`slot` されたあとのものにスタイルを当てようと思ったら

```
親Element slot::slotted(*) {
```

のようにすべきだと思いました。公式が言ってるんだもん。
しかし、スタイルが当たらない...。なんでやねん！


そこで、同プロジェクトの2年目の先輩が
>「レンダリングされたあと slot タグないし、もしかしたらさあ...」

と以下の記述を提案しました。


```
親Element ::slotted(*) {
```

<strong>効きました！</strong>

ちなみに、標準で ShadowDOM が実装されている Chrome などでは

```
::slotted(*) {
```

のみで問題ないです。はぁ...



## :bug: バグ2つ目

次からは ShadyDOM はおそらく関係ないバグです。

CSSプロパティの `color` や `background-color` などに使用できる値 `currentColor` についてです。


### :cat: currentColor

こちらは、親ブロックで指定した色がそのまま使える便利なやつです。
例えば、アイドルのメンバーカラーによって文章の枠色を変えたいとき、ありますよね。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="html,result" data-user="kidoyuna" data-slug-hash="XWJREEK" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="currentColor">
  <span>See the Pen <a href="https://codepen.io/kidoyuna/pen/XWJREEK">
  currentColor</a> by Kido Yuna (<a href="https://codepen.io/kidoyuna">@kidoyuna</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

こういったときに便利です。


`color: currentcolor;` の記述は今回の構造だと必要ありませんが、ShadowDOM でどんどん値を受け渡していく際は必要になることがあります。

これを、CSS で変数を使って書いてみます。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="html,result" data-user="kidoyuna" data-slug-hash="oNgWqMo" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="currentColor x custom property">
  <span>See the Pen <a href="https://codepen.io/kidoyuna/pen/oNgWqMo">
  currentColor x custom property</a> by Kido Yuna (<a href="https://codepen.io/kidoyuna">@kidoyuna</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

同じ意味なので、もちろん見た目は変わりませんね。
ですが、これを Edge で見ると...

![codepen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534968/2fb86b3f-f17f-a4c8-7fac-1f1ba86cc9d4.png)


<strong>白...</strong>

しかしこちら、`color: "currentcolor";` とすると、理想の挙動になる模様。
（ちなみにそうすると border がバグるので構造を変える必要あり）

参考：<a href="https://stackoverflow.com/questions/53447355/currentcolor-set-as-a-custom-property-doesnt-work-in-edge">currentColor set as a custom property doesn't work in Edge</a>


## :bug: バグ3つ目

`font-size` を変数で指定して、その `font-size` に合わせて `padding` をつけてみます。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="css,result" data-user="kidoyuna" data-slug-hash="RwNVXmb" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="font">
  <span>See the Pen <a href="https://codepen.io/kidoyuna/pen/RwNVXmb">
  font</a> by Kido Yuna (<a href="https://codepen.io/kidoyuna">@kidoyuna</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

こうすると、全体的に `font-size` を変更したくなったとき
`--base-font-size` を変えるだけで、 `--font-big` と `--font-small` の比率は変わらないので管理が楽になりますね。

また、 description は `font-size` の半分の値で `padding` をとっていますが
これによって、 `font-size` が変わっても見た目のスペース比率は変わりません。楽。


ですが、これが Edge だと...
![font.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534968/f080de64-7909-0f46-badb-9bf1cd22dc37.png)

ぎゃー。 `padding` 消えた。

これは、ブラウザ側で

```
padding: calc(var(--font-small) / 2);
```

の記述が

```
padding: calc(calc(var(--base-font-size) * 0.8) / 2);
```

と解釈されて、さらに

```
padding: calc(calc(16px * 0.8) / 2);
```

と解釈されているためです。
これが Edge は理解できないみたいです。

ちなみに、試しに

```
padding: calc((var(--base-font-size) * 0.8) / 2);
```

と直書きしてみたら、理想通りの挙動になりました。
うーん... `calc` って文字がネストするとわかんなくなっちゃうんだなぁ。

<a href="https://caniuse.com/#feat=css-variables">カスタムプロパティ</a>も <a href="https://caniuse.com/#feat=calc">`calc`</a> も Edge 対応してるのに。

工夫すれば実現はできますが、ちょっと不便...。



# :sunrise: Edge との戦いを終えて

（正確に言うとまだ戦っている最中なのですが...）

「バグかも？」と思ったときに詳細を調べる速度が、先輩方はめちゃめちゃ早かったです。

私はとりあえず「Edge calc」で検索して出てきた、それっぽい Qiita の記事を読み漁るなどしてましたが恐らく NG です。

基本中の基本ではありますが、まず公式ドキュメントをじっくり読む。
ブラウザ間で揺れがあるバグなら、<a href="https://caniuse.com/">caniuse</a> で対応状況確認。そこに issue が載ってることもあるので探す。
それから、 Polyfill 等ライブラリについてなら公式 Github の issue で同様のことを嘆いている人はいないか探す。



英語の文献はついつい避けがちな私ですが、これを機に「まず公式ドキュメント！！」を身につけようと思います。:bow: