Sassで使えるIE向けCSSハックまとめ

#Sassで使えるIE hack

皆さんはSassでIE hackを使ったことありますでしょうか？
恐らくコンパイル時のエラーで使用できるCSSハック（以降、面倒なのでhack）が限定され、融通が効かないという理由で、以下のような条件付きコメント（conditional comments）に切り替えた方も多いんじゃないかと思います。

```html:HTML5&nbsp;Boilerplate
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
```

※ HTML5 Boilerplateの条件付きコメントから抜粋（上記はIE9以上、IE8以下、IE7以下、IE6以下のclassによる振り分け）


これから紹介するのは、普段からSassを使いつつも

* hackするとしても1,2箇所あるかないかだから、条件付きコメントが必須ってわけでもないんだよなぁ。（29歳 コーダー）
* 条件付きコメントを使うのは、ソースがごちゃごちゃして嫌なんじゃ...（72歳 デザイナー）
* 見るだけで過酷なレガシーIE対応の日々を思い出して、吐き気がするぜ。（36歳 フロントエンドエンジニア）
* コンパイルエラーをくぐり抜け、Sassでhackをすることこそが至高だ！（18歳 最高技術責任者）

上記のように、条件付きコメントがしっくり来ない方向けです。


##コンパイルエラーの壁

Sassではコンパイル時に特定の箇所にスラッシュなどの記号が入っていると、コンパイルエラーが起きます。
特にスラッシュが使えない関係で、ブログ等でよく紹介されているIE hackがさほど自由に使えない傾向があります。
しかも、割とメジャーなhackばかりなのが痛い。

例えば以下のhackはSassでコンパイルに引っ掛かり使えません。


###Sassで使えないIE hackの例

基本的にスラッシュを利用したhackは使えません。


```scss:hack.scss
// IE7以下向け（スラッシュハック）
body {
	/background-color: #000;
}

// IE8のみ（\0/ハック）
body {
	background: #00b\0/;
}
```
※\0/ハックをIE9ハックとして紹介しているサイトがありますがそれは間違いです。（互換表示でも適応されないため、恐らくプレリリース版の情報？）

###CSSの詳細度（specificity）を変更してしまうIE hackの例

他にもSassで通るけど、詳細度の調整が必要になるhackがあります。
このようなhackに限っては、例えSassを使わない場合でも注意が必要です。

```scss:hack.scss
// IE9以上向けのhack
:root body { background: #00b\0; }

// IE10以上向けのhack
@media all and (-ms-high-contrast:none) {
	body { background: #000\0; }
}

// IE11のみ
@media all and (-ms-high-contrast:none) {
	*::-ms-backdrop, body {
		background-color: #ccc\0;
	}
}
```

上記はIE9以上向けの詳細度が高くなっているので、残り2つのhack（IE10以上向けのhack、IE11のみ）が効かなくなってしまいます。
IE9以上向けが必要ない場合は上記のコードをそのまま使用できますが、このように詳細度が絡んでくるhackは何かと面倒です。

そのため残り2つのhackを効くようにするためには、


```scss:hack.scss
// IE9以上向けのhack
:root body { background: #00b\0; }

// IE10以上向けのhack
@media all and (-ms-high-contrast:none) {
	:root body { background: #000\0; }
}

// IE11向けのhack
@media all and (-ms-high-contrast:none) {
	*::-ms-backdrop, :root body {
		background-color: #000\0;
	}
}
```

このように後から適応するhackの詳細度も変更する必要性が出てきます。（``:root``を残り2つのhackにも追加）

特定のバーションのみ対象の場合は問題ないが、複数のバーションが対象となる場合は詳細度が変更されることで
調整が必要となるhackもあるので、基本的にそのようなhackは避け、詳細度を変更しないものに限定します。


##実用性を考慮したhackの判断項目

そこで以下をクリア出来ていれば、実用的なレベルに達しているだろうと考え、hackの書き方自体を試行錯誤しました。

1. 前提条件として、Sassで使える
2. なるべく特定のバージョンのみに使える
3. 詳細度を調整する必要がない
4. コンパイルした後でもhackした箇所がわかりやすい
5. 可能な限り記述を短くする


##Mixinにしてみる

対象となるバージョンはIE8-11です。
[IE8以下はあと1ヶ月程で終了](http://qiita.com/sawadays0118/items/41bab56358742fe2a646)するので今更感ありますが、一応IE8用のhackも入れておきます。


```scss:_mixin.scss
// * hack IE
$hack-ie11: "*::-ms-backdrop";
@mixin hack-ie($hack-ver:'') {
	// * IE8 hack
	@if $hack-ver == "ie8" {
		@media \0screen {
			@content;
		}
	}
	// * IE9-10 hack(add propary \9)
	@else if $hack-ver == "ie9-10" {
		@media all and (min-width:0\0) {
			@content;
		}
	}
	// * IE10 hack(add propary \9)
	@else if $hack-ver == "ie10" {
		@media all and (-ms-high-contrast:none\0) {
			@content;
		}
	}
	// * IE11 hack(add selector *::-ms-backdrop)
	@else if $hack-ver == "ie11" {
		@media all and (-ms-high-contrast:none\0) {
			@content;
		}
	}
}
```

厳密に言えば、IE10,11の部分は同じコードなので切り分ける必要がないんですが、
実際にMixinで指定するとき、どっちのバージョンに対するhackなのかがわかりにくくなるので、上記の形にしています。
必要であれば、Firefox,Chrome等のハックを一緒にMixinにしても良いと思います。

ちなみにIE11の部分で以下のような書き方ができるかと思ってたんですが、実際は出来なくて残念。。。
何かいい方法あったら教えてください。

```scss:_mixin.scss
// * hack IE
$hack-ie11: "*::-ms-backdrop";
@mixin hack-ie($hack-ver:'') {
	// * IE11 hack(add selector *::-ms-backdrop)
	@else if $hack-ver == "ie11" {
		@media all and (-ms-high-contrast:none\0) {
			#{$hack-ie11},
			@content;
		}
	}
}
```


##使い方

Mxinの使い方です。
以下のように``hack-ie()``のカッコ内にバージョンを指定します。


```scss:hack.scss
body {
	// * IE8 hack
	// * 使用時の注意：なし
	@include hack-ie(ie8) {
			background-color: #c00;
	}
	// * IE9-10 hack
	// * 使用時の注意：プロパティの最後に\9を入れる
	@include hack-ie(ie9-10) {
			background-color: #00b\9;
	}
	// * IE10 hack
	// * 使用時の注意：プロパティの最後に\9を入れる
	@include hack-ie(ie10) {
			background-color: #000\9;
	}
	// * IE11 hack
	// * 使用時の注意：セレクタ*::-ms-backdropを追加する必要があるので、$hack-ie11を突っ込む
	// * セレクタを継承する&も忘れずに
	@include hack-ie(ie11) {
		#{$hack-ie11}, & {
			background-color: #ccc;
		}
	}
}


// * 以下のように書いてもOK
// * IE8 hack
@include hack-ie(ie8) {
	body {
		background-color: #c00;
	}
}

// * IE9-10 hack
@include hack-ie(ie9-10) {
	body {
		background-color: #00b\9;
	}
}

// * IE10 hack
@include hack-ie(ie10) {
	body {
		background-color: #000\9;
	}
}

// * IE11 hack
@include hack-ie(ie11) {
	#{$hack-ie11}, body {
		background-color: #ccc;
	}
}
```


##コンパイル後のCSS

こんな感じでCSSが出力されます。（``output_style = :compact``）
``\0``で検索かけることでhackした場所を特定できます。

```css:hack.css
/* IE8 hack */
@media \0screen { body { background-color: #c00; } }

/* IE9-10 hack */
@media all and (min-width: 0 \0) { body { background-color: #00b\9; } }

/* IE10 hack */
@media all and (-ms-high-contrast: none\0) { body { background-color: #000\9; } }

/* IE11 hack */
@media all and (-ms-high-contrast: none\0) { body *::-ms-backdrop, body { background-color: #ccc; } }

/* IE11 hack 以下のように書いてもOKをコンパイルした場合。こっちのほうが短くなるが、書いててラクなのは前者 */
@media all and (-ms-high-contrast: none\0) { *::-ms-backdrop, body { background-color: #ccc; } }
```

IE8の``\0screen``hackですが、``screen``のみにならず``all``と同等に機能するようです。
（以前、IE8の印刷プレビューで``print``に反映されるのを確認したことがある）


##あとがき

ということで、実用性を考慮したhackの判断項目ですが、

1. 前提条件として、Sassで使える<br>**使えなかったらタイトル詐欺だよ！**
2. なるべく特定のバージョンのみに使える<br>**IE9以外は特定のバージョンのみに使用可！**
3. 詳細度を調整する必要がない<br>**調整しなくていいよ！**
4. コンパイルした後でもhackした箇所がわかりやすい<br>**\0で検索すればhackした場所がわかるよ！**
5. 可能な限り記述を短くする<br>**Sassで使用可のhackという条件では、そこそこ短い？**


だいたいこんな感じで、達成できたんじゃないでしょうか。

Mixinまで制作しといて言うことじゃないですが、実務でIE9以上のhackを使ったことはまだ一度もありません。
（IE8以下は腐るほどあります。特にスラッシュハック、スターハック、アンダースコアハック、!importantハックとか。）
が、未だにレガシー対応の案件がありますし、必要になってから慌てて対応するのはダルいので作った次第です(・ω・)/

[2016/1/13でIE8以下はサポート切れ](http://qiita.com/sawadays0118/items/41bab56358742fe2a646)になることもあり、フロントエンドしてる側としてはうれしい限りですが、個人的にはIE9の``overflow-y: auto/scroll;``と``box-sizing: border-box;``を同時指定したときに起こるボックス計算のバグも、爆破ボタン押したくなるぐらい嫌いです。
さっさとIEは駆逐されるべき。(｀・ω・´)