一部の公共性の高いWebサイトでは、今だにIE8への対応を行いつつ、同時にレスポンシブデザインも要求されるケースがある。当然IE8はCSSのメディアクエリに対応していない為、メディアクエリ抜きでコーディングをする必要がある。
設定する要件
- モダンブラウザに対応し、あるブレークポイントを境にPC版とスマートフォン版のスタイルが切り替わる。レスポンシブデザインとすること
- モダンブラウザのPC版とほぼ同等の外観でIE8にも対応すること。ただし、ブレークポイントを越えてもレイアウトが変わる必要はない
実装方針
もしPCファーストの設計であれば、IE8のことを気にせずに実装できる。
1.サイズ共通のスタイルを書く
2.PC専用スタイルを記述する
2.SP専用のスタイルをメディアクエリの内側で上書きする
しかし、PC用スタイルのオーバーライドは無駄が多くて分かり辛い為、できればモバイルファーストで実装したい。
たいていの場合、「PC(広い画面)用のデザイン」よりも「モバイル(狭い画面)用のデザイン」の方がシンプルな構成になるでしょう。その場合には、まず、「モバイル(狭い画面)用のデザイン」を全て記述しておき、そこに追加する形で「PC(広い画面)用のデザイン」を作っていくと、記述量を減らせる上に、更新の手間も少なくて済むCSSソースになります。
ところが、モバイルファーストで実装しようとすると、どうしてもIE8用の記述をメディアクエリの外側に記述する必要があり、どこかで2回同じことを書かなければならない。
1.サイズ共通のスタイルを書く
2.SP専用スタイルをメディアクエリの内側に書く1
3.PC専用のスタイルをメディアクエリの内側に書く**(IE8からは認識されない)**
4.IE8専用のスタイルを書く
↑ 4は3と同じ内容
3、4の重複を失くすことは原理的に不可能だと思われる。このことを受け入れた上で、1〜4のコードをスッキリと記述できるSCSSのミックスインを作成した。
HTML
まずは、下準備としてIE8を識別するためのコンディショナルコメント2を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
$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の考え方を取り入れている。
@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を初期のページ読み込みの後でも呼び出すことができるようになる。
メディアクエリの宣言が連続する分冗長だが、大きな単位のメディアクエリで区切るよりも管理がしやすく、デメリットよりメリットの方が勝っていると思われる。
CSS
上記のSCSSをビルドすると次のようなCSSが生成される。IE8での動作確認まではできないが、実際のスタイルはJSFiddleで参照できる。
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を登録しておけば、サクサクコーディングが進められる。
// 共通
@include for-all(&){$END$};
// SP
@include for-mobile-only(&){};
// PC
@include for-pc-only(&){};
こちらもどうぞ!
Sassで調子に乗って@extendしまくった過去を反省し、良い継承の仕方を模索する - Qiita
-
必ずしもメディアクエリ内に書く必要はないが、SP, PCでデザインの差が大きい場合は分けた方が管理しやすい。 ↩
-
最近のHTML5 Boilerplateでは、もうこのようなコンディショナルコメントは採用されていない。
なぜ今だにIE8対応が要件としてあるのかは謎である。↩