IE8互換のレスポンシブデザインを、ワンソース、モバイルファーストで作れるSassミックスイン

一部の公共性の高いWebサイトでは、今だにIE8への対応を行いつつ、同時にレスポンシブデザインも要求されるケースがある。当然IE8はCSSのメディアクエリに対応していない為、メディアクエリ抜きでコーディングをする必要がある。

# 設定する要件

* モダンブラウザに対応し、あるブレークポイントを境にPC版とスマートフォン版のスタイルが切り替わる。レスポンシブデザインとすること
* モダンブラウザのPC版とほぼ同等の外観でIE8にも対応すること。ただし、ブレークポイントを越えてもレイアウトが変わる必要はない

# 実装方針

もしPCファーストの設計であれば、IE8のことを気にせずに実装できる。

1.サイズ共通のスタイルを書く
2.PC専用スタイルを記述する
2.SP専用のスタイルをメディアクエリの内側で上書きする

しかし、PC用スタイルのオーバーライドは無駄が多くて分かり辛い為、できればモバイルファーストで実装したい。

>たいていの場合、「PC(広い画面)用のデザイン」よりも「モバイル(狭い画面)用のデザイン」の方がシンプルな構成になるでしょう。その場合には、まず、「モバイル(狭い画面)用のデザイン」を全て記述しておき、そこに追加する形で「PC(広い画面)用のデザイン」を作っていくと、記述量を減らせる上に、更新の手間も少なくて済むCSSソースになります。
>
>[モバイルファーストでデザインする際のCSSの書き方 [ホームページ作成] All About](http://allabout.co.jp/gm/gc/451911/)

ところが、モバイルファーストで実装しようとすると、どうしてもIE8用の記述をメディアクエリの外側に記述する必要があり、どこかで2回同じことを書かなければならない。

1.サイズ共通のスタイルを書く
2.SP専用スタイルをメディアクエリの内側に書く[^1]
3.PC専用のスタイルをメディアクエリの内側に書く**(IE8からは認識されない)**
4.IE8専用のスタイルを書く
**↑ 4は3と同じ内容**

*3、4の重複を失くすことは原理的に不可能だと思われる。このことを受け入れた上で、1〜4のコードをスッキリと記述できるSCSSのミックスインを作成した。*

# HTML

まずは、下準備としてIE8を識別するためのコンディショナルコメント[^2]をHTMLへ仕込む。

```html:sample.html
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie"> <![endif]-->
<!--[if IE 7]>    <html class="no-js ie7 oldie"> <![endif]-->
<!--[if IE 8]>    <html class="no-js ie8 oldie"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
  <meta charset="utf-8">
  <title>demo</title>
</head>
<body>
  <div class="my-container">
    <h1 class="my-container__head">IE8互換のレスポンシブデザインを、ワンソースかつモバイルファーストで作れる@mixin</h1>
    <p class="my-container__body">一部の公共性の高いWebサイトでは、今だにIE8への対応を行いつつ、同時にレスポンシブデザインも要求されるケースがあります。当然IE8はCSSのメディアクエリに対応していない為、メディアクエリ抜きでコーディングしてあげる必要があります。</p>
  </div>
  <div class="my-container">
    <h1 class="my-container__head">もしPCファーストの設計であれば、IE8のことを気にせずに実装できる</h1>
    <p class="my-container__body">しかし、PC用スタイルのオーバーライドは無駄が多くて分かり辛い為、できればモバイルファーストで実装したい。ところが、モバイルファーストで実装しようとすると、どうしてもIE8用の記述をメディアクエリの外側に記述する必要があり、どこかで2回同じことを書かなければならない。</p>
  </div>
</body>
</html>
```

# SCSS

```scss:_mixins.scss

$break-point: 600px !default;
$ie-selector: '.oldie' !default;

// 全サイズへの出力
@mixin for-all ($selector) {
  @at-root {
    /* ====================
    #{$selector}
    */
    #{$selector} {
      @content;
    }
  }
}

// モバイル専用出力
@mixin for-mobile-only ($selector) {
  @media only screen and (max-width: $break-point - 1) {
    @content;
  }
}

// モダンブラウザ+IE8用出力
@mixin for-pc-only ($selector) {
  @media only screen and (min-width: $break-point) {
    @content;
  }
  @include for-ie-only($selector) {
    @content;
  }
}

// IE8専用出力
@mixin for-ie-only ($selector) {
  @at-root {
    #{$ie-selector} #{$selector} {
      @content;
    }
  }
}

```

**for-allミックスインが不要に見える**かもしれないが、次のようなメリットがあるのであえて入れている。

* コンパイル後もコメントを挿入してBEMのコンポーネントやエレメントの区切りが分かりやすくなるようにしている
* インデントが揃うので、補完のないエディタ上でもコピペが楽
* `for-`以降を書きかえるだけで、対象となる画面サイズを変更できる

このミックスインを以下のように使用する。なお、CSSの構造化にBEMとSMACSSの考え方を取り入れている。

```scss:style.scss
@import 'mixins';

body {
  background: #eee;
}

$break-point: 694px;

// my-containerコンポーネント
.my-container {
  // 共通
  @include for-all(&) {
    font-size: 0.85em;
    font-family: sans-serif;
    margin: 0;
    padding: 0;
    padding: 1em;
    background: white;
  }
  // モバイル
  @include for-mobile-only(&) {
    border-top: 2px solid pink;
  }
  // PC
  @include for-pc-only(&) {
    text-align: center;
    float: left;
    position: relative;
    width: 300px;
    min-height: 23em;
    margin-right: 10px;
    margin-bottom: 10px;
    border-radius: 16px;
  }

  // 見出しエレメント
  &__head {
    // 共通
    @include for-all(&) {
      margin-top: 0.3em;
      margin-bottom: 0.3em;
      line-height: 1.1;
    }
    // PC
    @include for-pc-only(&) {
      padding-top: 0.5em;
    }
  }

  // 本文エレメント
  &__body {
    // 共通
    @include for-all(&) {
      color: #666;
    }
    &:before {
      // 共通
      @include for-all(&){};
      // PC
      @include for-pc-only(&) {
        content: '*';
        font-family: 'Times New Roman';
        font-size: 3em;
        color: pink;
        position: absolute;
        display: block;
        text-align: center;
        top: 0.1em;
        left: 0;
        right: 0;
      }
    }
  }
}
```

SMACSSでは、メディアクエリは断片的に記述することが推奨されている。

>メディアクエリの宣言はおそらく(ほとんどの場合)何度も記述することになる。しかしこうすることでモジュールについてのすべての情報を1カ所で管理できる。モジュールについての情報を集中管理できる(特に1つのCSSファイル内で)ことはモジュールのテストを分離することを可能にし、(アプリケーションの制作の方法によっては)モジュラー化されたテンプレートとCSSを初期のページ読み込みの後でも呼び出すことができるようになる。
>
> [Scalable and Modular Architecture for CSS 日本語](https://smacss.com/ja)

メディアクエリの宣言が連続する分冗長だが、大きな単位のメディアクエリで区切るよりも管理がしやすく、デメリットよりメリットの方が勝っていると思われる。

# CSS

上記のSCSSをビルドすると次のようなCSSが生成される。IE8での動作確認まではできないが、実際のスタイルは[JSFiddle](https://jsfiddle.net/5L9e75hv/)で参照できる。

```css:style.css
body {
  background: #eee;
}

/* ====================
.my-container
*/
.my-container {
  font-size: 0.85em;
  font-family: sans-serif;
  margin: 0;
  padding: 0;
  padding: 1em;
  background: white;
}
@media only screen and (max-width: 693px) {
  .my-container {
    border-top: 2px solid pink;
  }
}
@media only screen and (min-width: 694px) {
  .my-container {
    text-align: center;
    float: left;
    position: relative;
    width: 300px;
    min-height: 23em;
    margin-right: 10px;
    margin-bottom: 10px;
    border-radius: 16px;
  }
}
.oldie .my-container {
  text-align: center;
  float: left;
  position: relative;
  width: 300px;
  min-height: 23em;
  margin-right: 10px;
  margin-bottom: 10px;
  border-radius: 16px;
}
/* ====================
.my-container__head
*/
.my-container__head {
  margin-top: 0.3em;
  margin-bottom: 0.3em;
  line-height: 1.1;
}
@media only screen and (min-width: 694px) {
  .my-container__head {
    padding-top: 0.5em;
  }
}
.oldie .my-container__head {
  padding-top: 0.5em;
}
/* ====================
.my-container__body
*/
.my-container__body {
  color: #666;
}
/* ====================
.my-container__body:before
*/
@media only screen and (min-width: 694px) {
  .my-container__body:before {
    content: '*';
    font-family: 'Times New Roman';
    font-size: 3em;
    color: pink;
    position: absolute;
    display: block;
    text-align: center;
    top: 0.1em;
    left: 0;
    right: 0;
  }
}
.oldie .my-container__body:before {
  content: '*';
  font-family: 'Times New Roman';
  font-size: 3em;
  color: pink;
  position: absolute;
  display: block;
  text-align: center;
  top: 0.1em;
  left: 0;
  right: 0;
}
```

素のCSSでSMACSSを意識して書いた場合とほぼ同じ、理想的な内容で出力できているのではないだろうか。これなら、納品後に第三者が内容を修正するのも比較的簡単になるだろう。

# Live Template

ミックスインは`{}`の内側が空でも構わないので、例えばJetBrainのIDEに次のようなLive Templateを登録しておけば、サクサクコーディングが進められる。

```scss:abbreviation'@rwd'で呼び出し
// 共通
@include for-all(&){$END$};
// SP
@include for-mobile-only(&){};
// PC
@include for-pc-only(&){};
```

# こちらもどうぞ!

[Sassで調子に乗って@extendしまくった過去を反省し、良い継承の仕方を模索する - Qiita](http://qiita.com/y_hokkey/items/a746219dd688e18ac795)

---

[^1]: 必ずしもメディアクエリ内に書く必要はないが、SP, PCでデザインの差が大きい場合は分けた方が管理しやすい。
[^2]: 最近の[HTML5 Boilerplate](https://html5boilerplate.com)では、もうこのようなコンディショナルコメントは採用されていない。~~なぜ今だにIE8対応が要件としてあるのかは謎である。~~