Sass/SCSSの@extendは使いすぎるとカオスだった

# [@extend]()のアンチパターン

Sassを導入しはじめた2013年頃の自分は、Sassがもたらしてくれるコード再利用の為の機能に心酔していた。**共通しているコードは全部[@extend]()しないと気が済まない病気**にかかっていたし、SMACSSやBEMといった構造化CSSのことも知らなかった。

当時の自分にとっての「ぼくがかんがえたさいきょうのSCSS」の出力結果は次のような内容だった。

```css
.cf:before, .floatedList:before, .navGlobal ul:before, .book.horizontal .inner > ul:before, .book.horizontal .ulWrap > ul:before, .articleList > .head > .options > ul:before, .bodyPage .pageHeader .bodyHead .articleInfos:before, .archiveModeBtns ul:before, .contentWrapper:before, .breadCrumb > ul:before, .siteHeader:before, .mainNavi:before, .mobileSearchPane:before, .toolHeader:before, .btnsUi:before, .btnsUi dl:before, .mainNavi > .inner:before, .navGlobal:before, .searchBox:before, .searchBox > .inner:before, .mobileMenuPane:before,
.mobileGlobal:before, .mobileMenuPane ul:before,
.mobileGlobal ul:before, .book.horizontal:before, .book.horizontal .inner:before, .book.horizontal .ulWrap:before, .book.horizontal ul:before, .articleList .summary:before, .dir4thList .inner:before, .dir4thList dl:before, .dir2ndList > ul:before, .footerWrapper .row1 .inner:before,
.footerWrapper .mobileFoot:before,
.footerWrapper .col1:before,
.footerWrapper .row2 .inner:before,
.footerWrapper .siteFooter:before, .bodyPage .pageHeader .bodyHead:before, .bodyPage .pageHeader .bodyHead .shareBtns:before, .bodyPage .pageHeader .bodyHead .shareBtns ul:before, .bodyPage .files .hideOnMobile:before,
.cf:after,
.floatedList:after,
.navGlobal ul:after,
.book.horizontal .inner > ul:after,
.book.horizontal .ulWrap > ul:after,
.articleList > .head > .options > ul:after,
.bodyPage .pageHeader .bodyHead .articleInfos:after,
.archiveModeBtns ul:after,
.contentWrapper:after,
.breadCrumb > ul:after,
.siteHeader:after,
.mainNavi:after,
.mobileSearchPane:after,
.toolHeader:after,
.btnsUi:after,
.btnsUi dl:after,
.mainNavi > .inner:after,
.navGlobal:after,
.searchBox:after,
.searchBox > .inner:after,
.mobileMenuPane:after,
.mobileGlobal:after,
.mobileMenuPane ul:after,
.mobileGlobal ul:after,
.book.horizontal:after,
.book.horizontal .inner:after,
.book.horizontal .ulWrap:after,
.book.horizontal ul:after,
.articleList .summary:after,
.dir4thList .inner:after,
.dir4thList dl:after,
.dir2ndList > ul:after,
.footerWrapper .row1 .inner:after,
.footerWrapper .mobileFoot:after,
.footerWrapper .col1:after,
.footerWrapper .row2 .inner:after,
.footerWrapper .siteFooter:after,
.bodyPage .pageHeader .bodyHead:after,
.bodyPage .pageHeader .bodyHead .shareBtns:after,
.bodyPage .pageHeader .bodyHead .shareBtns ul:after,
.bodyPage .files .hideOnMobile:after {
  /* micro clearfix */
  content: "";
  display: table;
}

.cf:after, .floatedList:after, .navGlobal ul:after, .book.horizontal .inner > ul:after, .book.horizontal .ulWrap > ul:after, .articleList > .head > .options > ul:after, .bodyPage .pageHeader .bodyHead .articleInfos:after, .archiveModeBtns ul:after, .contentWrapper:after, .breadCrumb > ul:after, .siteHeader:after, .mainNavi:after, .mobileSearchPane:after, .toolHeader:after, .btnsUi:after, .btnsUi dl:after, .mainNavi > .inner:after, .navGlobal:after, .searchBox:after, .searchBox > .inner:after, .mobileMenuPane:after,
.mobileGlobal:after, .mobileMenuPane ul:after,
.mobileGlobal ul:after, .book.horizontal:after, .book.horizontal .inner:after, .book.horizontal .ulWrap:after, .book.horizontal ul:after, .articleList .summary:after, .dir4thList .inner:after, .dir4thList dl:after, .dir2ndList > ul:after, .footerWrapper .row1 .inner:after,
.footerWrapper .mobileFoot:after,
.footerWrapper .col1:after,
.footerWrapper .row2 .inner:after,
.footerWrapper .siteFooter:after, .bodyPage .pageHeader .bodyHead:after, .bodyPage .pageHeader .bodyHead .shareBtns:after, .bodyPage .pageHeader .bodyHead .shareBtns ul:after, .bodyPage .files .hideOnMobile:after {
  /* micro clearfix */
  clear: both;
}

.cf, .floatedList, .navGlobal ul, .book.horizontal .inner > ul, .book.horizontal .ulWrap > ul, .book.horizontal ul > ul, .articleList > .head > .options > ul, .bodyPage .pageHeader .bodyHead .articleInfos, .archiveModeBtns ul, .contentWrapper, .breadCrumb > ul, .siteHeader, .mainNavi, .mobileSearchPane, .toolHeader, .btnsUi, .btnsUi dl, .mainNavi > .inner, .navGlobal, .searchBox, .searchBox > .inner, .mobileMenuPane,
.mobileGlobal, .mobileMenuPane ul,
.mobileGlobal ul, .book.horizontal, .book.horizontal .inner, .book.horizontal .ulWrap, .book.horizontal ul, .articleList .summary, .dir4thList .inner, .dir4thList dl, .dir2ndList > ul, .footerWrapper .row1 .inner,
.footerWrapper .mobileFoot,
.footerWrapper .col1,
.footerWrapper .row2 .inner,
.footerWrapper .siteFooter, .bodyPage .pageHeader .bodyHead, .bodyPage .pageHeader .bodyHead .shareBtns, .bodyPage .pageHeader .bodyHead .shareBtns ul, .bodyPage .files .hideOnMobile {
  /* micro clearfix */
  zoom: 1;
}

/* この調子であと7000行くらい */

```

* clearfixが必要な度に@extend
* 深いインデントを持ったクラスの@extendにより、大量のセレクタが生成された
* HTML要素への依存性が非常に高い
* 先方に「こんなものは今まで見たことがない」という驚きの声をいただく
* IE9でセレクタ数制限にひっかかり、blessで分割する破目になった
* コンパイル前のソース無しでは、内容の理解も改修もほぼ不可能

自分自身は**「SCSSの中は綺麗だし、HTML側のクラス名はクリーンでセマンティックだし、手元にSCSSがあるんだから、出力後のCSSがどうなろうと知ったことじゃないよ」**という気持ちだった。[^1]

# HTMLが綺麗なら、CSSはカオスでもいいのか?

>マークアップをシンプルに保ち、かつスタイルを変更する際になるべく影響を受けないよいうに、あらかじめマークアップの各要素というかモジュールごとにスタイルとは切り離した適切な名前をつけておいて、スタイルはスタイルでオブジェクトとして体系化してマークアップから分離する、みたいな理想にオブジェクト指向な Sass で少し近づけるのかも… とか考えるんですが、どうでしょう。
>
> [Sass の @extend はどこがすごいのか · terkel.jp](about:reader?url=http%3A%2F%2Fterkel.jp%2Farchives%2F2011%2F12%2Fsass-extend%2F)


[@extend]()が魅力的に思えるのは、上記引用のように、HTMLをクリーンに保ちつつCSS内では継承ができるという点であった。しかし、いざ使いまくってみると以下のような問題があった。

* CSSの容量増大
* CSSの複雑さが大幅に増し、元ソースなしでは管理不可能になる
* セレクタ数を常に気にしないとIE9で壊れる

明らかに得たものよりも失ったものの方が多い。

「HTMLとCSSのどちらに複雑さを持たせるべきか」という二元論はそれほど重要ではなく、できるだけHTMLもCSSも複雑にすることなく、管理と修正のしやすいコーディングをすることの方が大事なことだったのだと思う。

# 綺麗に出力できる継承の書き方

最近ではBEMとSMACSSの考え方を導入しつつ、HTMLとCSSのどちらでも継承に対応でき、Sassのメリットを活かせるような書き方を考えている。

例として、BEMっぽく書かれた共通の`my-componentスタイル`へ、継承する`your`スタイルと、`my`・`your`の両方を多重継承する`our`スタイルを新たに定義したいとして、異なる2つアプローチを紹介する。

```scss
.my-component {
  &__head {
    // ...
  }
  &__body {
    // ...
  }
}
```
```html
<div class="my-component clearfix">
  <div class="my-component__head">...</div>
  <div class="my-component__body">...</div>
</div>
```

なお、両方に共通するルールとして以下も前提とする。

* HTML要素に依存しない
* `clearfix`など、コンポーネントと関係ない汎用的なルールは、OOCSSで書く
* 子孫セレクタはできるだけ使わない

## .my-componentを継承する.your-componentと、その2つを継承する.our-componentを定義する場合

継承がCSS内で行われるケース。

BEMスタイルのコンポーネントは@extendできないので、セレクタを繋げて継承を行う。必然的にコードも近い場所にまとめざるを得ないし、継承のし過ぎも回避できる。

HTML側のクラス名をセマンティックに保てるというメリットがあるが、生成されるCSSに無駄な行ができてしまう欠点もある。(Sass側で対応してくれないものか…)

ソース:

```scss:demo.scss
// myスタイル
.my-component,
.your-component,
.our-component {
  &__head {
    /* #{&} */
  }
  &__body {
    /* #{&} */ 
  }
}
// myスタイルを継承するyourスタイル
.your-component,
.our-component {
  &__head {
    /* #{&} */ 
  }
}
// myスタイルとyourスタイルを多重継承するourスタイル
.our-component {
  &__body {
  /* #{&} */ 
  }
  &__foot {
  /* #{&} */ 
  }
}
```

コンパイル結果:

```css:demo.css
.my-component__head,
.your-component__head,
.our-component__head {
  /* .my-component__head, .your-component__head, .our-component__head */
}
.my-component__body,
.your-component__body,
.our-component__body {
  /* .my-component__body, .your-component__body, .our-component__body */
}
.your-component__head,
.our-component__head {
  /* .your-component__head, .our-component__head */
}
.our-component__body {
  /* .our-component__body */
}
.our-component__foot {
  /* .our-component__foot */
}
```

使い方:

```html
<!-- myスタイルを継承するyourスタイル -->
<div class="your-component clearfix">
  <div class="your-component__head">...</div>
  <div class="your-component__body">...</div>
</div>

<!-- myスタイルとyourスタイルを継承するourスタイル -->
<div class="our-component clearfix">
  <div class="our-component__head">...</div>
  <div class="our-component__body">...</div>
  <div class="our-component__foot">...</div>
</div>
```

## .my-componentにサブクラス.my-component--yours、.my-component--oursを定義する場合

継承がHTML内で行われるケース。

1つのインデントにサブクラスを定義し、HTML側で複数のクラスを挿入する。SMACSSではこの考え方を推奨している。

継承する項目が増えるとHTMLのクラスが複雑になっていく可能性があるが、継承の数が少ない場合はCSSもHTMLも簡潔にまとまるメリットがある。

ソース:

```scss:demo2.scss
.my-component {
  $self: &;
  
  &__head {
    /* #{&} */
  }
  &__body {
    /* #{&} */
  }
  // サブクラスを定義
  &--yours {
    #{$self}__head {
      /* #{&} */
    }
  }
  // サブクラスを定義
  &--ours {
    #{$self}__body {
      /* #{&} */
    }
    #{$self}__foot {
      /* #{&} */
    }
  }
}
```

コンパイル結果:

```css:demo2.css
.my-component__head {
  /* .my-component__head */
}
.my-component__body {
  /* .my-component__body */
}
.my-component--yours .my-component__head {
  /* .my-component--yours .my-component__head */
}
.my-component--ours .my-component__body {
  /* .my-component--ours .my-component__body */
}
.my-component--ours .my-component__foot {
  /* .my-component--ours .my-component__foot */
}
```

使い方:

```html

<!-- yourスタイル -->
<div class="my-component my-component--yours clearfix">
  <div class="my-component__head">...</div>
  <div class="my-component__body">...</div>
</div>

<!-- ourスタイル -->
<div class="my-component my-component--yours my-component--ours clearfix">
  <div class="my-component__head">...</div>
  <div class="my-component__body">...</div>
  <div class="my-component__foot">...</div>
</div>
```

# こちらもどうぞ!

[IE8互換のレスポンシブデザインを、ワンソース、モバイルファーストで作れるSassミックスイン - Qiita](http://qiita.com/y_hokkey/items/4b3fbfb62b2f2e2cafd1)

[^1]: このようなセレクタのまとめ方を積極的に行う「DRY CSS」という手法を検索していて見つけたが、国内で紹介されているのは聞いたことがない。[DRY CSS: Don't Repeat Your CSS - Vanseo Design](http://vanseodesign.com/css/dry-principles/)![](http://www.vanseodesign.com/blog/wp-content/uploads/2012/04/dry-steps.jpg)