Pleeease の出力する media queries のオーダーを制御するには

/ Text by

レスポンシブデザインのサイトの構築を行うには何しろ Sass の利用が便利です。CSS は手打ちに限る、と老舗の蕎麦屋みたいなことを考えていた時期もありましたが、兎角レスポンシブデザインについて言えば Sass の利用無しに構築を考えるのは今となっては苦行以外の何物でもありません。

そして Sass を使ってレスポンシブな設計を行う際、その記述の方法は様々あると思いますが、自分の中では要素ごとに・・・・・ media queries の指定をするのが一番分かりやすいように思えます。

// sample.scss

.c-container {
    width: 800px;
    @media screen and (max-width: 640px) {
        width: 540px;
    }
}

h1 {
    font-size: 2.5em;
    @media screen and (max-width: 960px) {
        font-size: 2em;
    }
    @media screen and (max-width: 800px) {
        font-size: 1.5em;
    }
    @media screen and (max-width: 640px) {
        font-size: 1em;
    }
}

実際には Sass の @mixin なども使いもう少し効率良く書きますが、上記のような書き方が見通しが良くて分かりやすい。しかし、この記述方法で進めていくと弊害もあり、media queries の記述量が多くなるためコンパイルされる CSS のファイルサイズも大きくなりがちです。

そこで登場するのがポスト・プロセッサとしての “Pleeease” で、多くの便利機能がありますが、その中に CSS に含まれる media queries をファイルの後方にまとめて整理してくれる “mqpacker” というものがあります。

Sass から CSS へコンパイルした後、その CSS をさらに Pleeease で後処理を行うことで、ファイルサイズを縮小できるメリットがあるわけです。Grunt 用のパッケージも用意されているので、自動処理のワークフローに取り入れてしまば Sass の保存と同時に最適化された CSS を得られてたいへん便利です。

ですが。何も考えず CSS を Pleeease に通してしまうと罠があり、期待される動作がなされない CSS が仕上がってしまうので注意が必要。

上記の Sass から最終的に得られる CSS として期待されるものは

// Pleeease を通して得られる CSS として期待されるもの

.c-container {
    width: 800px;
}

h1 {
    font-size: 2.5em;
}

@media screen and (max-width: 960px) {
    h1 {
        font-size: 2em;
    }
}

@media screen and (max-width: 800px) {
    h1 {
        font-size: 1.5em;
    }
}

@media screen and (max-width: 640px) {
    .c-container {
        width: 540px;
    }
    h1 {
        font-size: 1em;
    }
}

であると思いますが、実際にはこのようにはならず

// Pleeease を通して得られる実際の CSS

.c-container {
    width: 800px;
}

h1 {
    font-size: 2.5em;
}

@media screen and (max-width: 640px) {
    .c-container {
        width: 540px;
    }
    h1 {
        font-size: 1em;
    }
}

@media screen and (max-width: 960px) {
    h1 {
        font-size: 2em;
    }
}

@media screen and (max-width: 800px) {
    h1 {
        font-size: 1.5em;
    }
}

となります。media queries の出力の順番が、(max-width: 640px) → (max-width: 960px) → (max-width: 800px) となってしまっていますね。これは Pleeease が CSS ファイルを走査していった際に、「登場した media queries の順」で CSS をまとめてしまうためです。

CSS は上方からの継承モデルですから、media queries の順番が変わってしまうとデザインの再現に不具合が生じます。上の例で言えば、Sass 上ではウィンドウ幅が 640px より小さくなった際に h1 のフォントサイズが 1em の大きさとになるように書いていますが、出力された CSS では media queries の順番が変わっているために最後の (max-width: 800px) の分が適用され、1.5em の大きさになってしまうわけです。

こうなることを避けるには、 Pleeease の処理の特性を考慮して Sass の記述を以下のようにすればOKです。

// Pleeease で後処理されることを考慮した sample.scss

body {
    width: 100%;
    @media screen and (max-width: 960px) {
        width: 100%;
    }
    @media screen and (max-width: 800px) {
        width: 100%;
    }
    @media screen and (max-width: 640px) {
        width: 100%;
    }
}

.c-container {
    width: 800px;
    @media screen and (max-width: 640px) {
        width: 540px;
    }
}

h1 {
    font-size: 2.5em;
    @media screen and (max-width: 960px) {
        font-size: 2em;
    }
    @media screen and (max-width: 800px) {
        font-size: 1.5em;
    }
    @media screen and (max-width: 640px) {
        font-size: 1em;
    }
}

body の指定内容は一見無駄でしかありませんが、Sass の最上部で、最終的な出力順として期待する順番通りに media queries を記述しておけば良いという訳です。こうすることで Pleeease で処理を行った後も media queries の順番が入れかわってしまうようなことはなくなります。