CSSのスコープ

この記事の内容についてのほとんどが対応ブラウザの少ないものなので、現段階でそのまま実際に使えるものではない。Polymerで使用されているCSSを初見でわりと理解できなかったかつての自分用メモです。Polymerの解説ではなくて仕様の整理なので注意。

## scoped属性

これは直感的でわかりやすくて、HTML内部に直接`style`要素を挿入して`scoped`属性をつけるだけ。`scoped`属性をもつ要素はフローコンテンツが許容される最初の子要素である必要があって、場合によってはその`style`要素をラップする親要素が必要になることもある。

```html
<p>この文章にはスタイルが適用されません。</p>
<div>
  <style scoped>
    p { color: red; }
  </style>
  <p>この文章は文字が赤くなります。</p>
</div>
```

嫌がらせ以外で誰が使うんだろうかとおもっていたんだけども、後日[CSSに死を！これはJSerの叫び！](http://0-9.sakura.ne.jp/pub/kbkz_tech/start.html)のスライドを見ていて、JavaScriptの中にスタイルを埋め込んでしまいたいときに使いたくなるということがわかった。

## CSS Scoping Module

Polymerを使ってる人はすでに使っているはず。`core-*`に含まれるCSSの`:host`とか`/deep/`がそれ。念のためすべて確認しておきます。

### Scoped Styles

仕様書の順番通りにいくとまずは`@scope`。これは`scoped`属性と似たようなものなのだけれど、HTMLにそれほど干渉せずに指定できる。

```html
<p>この文章にはスタイルが適用されません。</p>
<div>
  <p>この文章は文字が赤くなります。</p>
</div>
```

このHTMLのdivでスコープを作りたいようなときには以下のようにCSSを記述する。

```css
@scope div {
  p { color: red; }
}
```

`class`や`id`つければいいだけなんじゃないかといわれるとそこまでなんだけど、そのスタイルを作成した作者の*このスタイルはそのコンテンツ内でしか使用しないですよ*という意図を明示することに意味があるのかもしれない。あとはCSSでネストされてると見通しがいいような気はするんだけど、いまどきCSS直接書く人なんていないだろうからそれがメリットになることはないよね。

それとこれは以下のようにも書けるということらしいんだけど、Issueになっていて色々な記法が提案されてるみたいだから、仕様だけみても今後どうなるかはわからないっぽい。

```css
p:scope-context(div) {
  color: red;
}
```

`:scope`についてはよくわからない。[CSS Selector 4](http://www.w3.org/TR/selectors4/)にあるらしいので、気になる人は確認してみるといいと思う。確認してないけど、たぶんスコープをもっている要素を選択できるとかそんな感じじゃないかな。

これらはどうせ対応ブラウザはないだろうから覚えておく必要なんてない。そもそも対応したってScoped Stylesでわざわざこんなスコープ作る人なんていないんじゃないだろうか、少なくとも自分は絶対に使わない。

## Shadow Encapsulation

これ以降の内容はShadow DOMだけでなくWeb Componentsについて理解していないと、何が書いてあるのかさっぱり理解できないかもしれない。

まずは`:host`。これはshasow treeのコンテキストで評価されたときhost elementと一致する。言葉でみると何を言っているのかよくわからないんだけど、仕様書のサンプルをみるとそんなに難しいことを言っているわけでもないことがわかる。

```html
<x-foo class="foo">
  <"shadow tree">
    <div class="foo">...</div>
  </>
</x-foo>
```

コンポーネントがこのようなshadow treeを持つときには以下のようになる。

```less
:host {}       // <x-foo>を選択する
x-foo {}       // 何とも一致しない
.foo {}        // <div>要素のみを選択する
.foo:host {}   // 何とも一致しない
:host(.foo) {} // <x-foo>を選択する
```

簡単に要約してしまうとshadow treeの外側にある`<x-foo>`選択できないから`:host`って書けばいいよ、ということ。

`:host-context()`ではshadow treeの外の祖先の要素を指定することもできて、これは仕様いわくテーマで配色を変更したりするようなときに便利ということらしいです。

`:shadow`はセレクタで使用するためのshadow treeのルートを選択するもので、shadow treeを複数持っていた場合にはすべてのshadow treeを選択する。つまり`:host::shadow div`みたいなことをすると、そのホスト要素内にあるすべてのshadow tree内の`div`を選択することになる。

`::content`についても仕様のサンプルがわかりやすい。

```html
<x-foo>
  <div id="one" class="foo">...</div>
  <div id="two">...</div>
  <div id="three" class="foo">
    <div id="four">...</div>
  </div>
  <"shadow tree">
    <div id="five">...</div>
    <div id="six">...</div>
    <content select=".foo"></content>
  </"shadow tree">
</x-foo>
```

`::content div`とすると選択されるのは`#one`, `#three`, `#four`になって、`::content > div`とすると`#four`のみが選択される。Polymerを使うなら`polyfill-next-selector`。

`/deep/`はshadow rootをまたいで要素を指定できる。

```html
<x-foo>
  <"shadow tree">
    <div>
      <span id="not-top">...</span>
    </div>
    <span id="top">...</span>
    <x-bar>
      <"shadow tree">
        <span id="nested">...</span>
      </>
    </x-bar>
  </>
</x-foo>
```

`x-foo /deep/ span`とすると選択されるのは`#top`, `#not-top`, `#nested`になる。最新のEDを確認してみると、`/deep/`は`>>>`になっていたのでそのうち変わるのかも。

## 参照文献

* [HTML 5.1](http://www.w3.org/TR/html51/)
* [CSS Scoping Module](http://www.w3.org/TR/css-scoping-1/)