Site icon Tips Note by TAM

Sassのメディアクエリmixinの決定版ライブラリ「Sass MQ」

以前、Sassの変数(Map)とmixinでメディアクエリを管理する記事を書きました。

Sassの変数とmixinで変更に強いメディアクエリをつくる

単純なmixinで導入はしやすいですが、min-widthmax-widthのどちらも使いたい場合は変数とmixinを増やす必要があったり、@media (min-width: 768px) and (max-width: 1023px)のような条件には対応していませんでした。

Sass MQ」はこれらの機能を備えている高機能で軽量なライブラリです。
inuitcss」で採用されていたり、「Sass Guidelines」で紹介されていたりしています。GitHubで1578のスターも付けられています(記事執筆時点)。

Sass MQをインストールする

npm・yarn・Bowerからインストールできます。

bower install sass-mq --save
npm install sass-mq --save
yarn add sass-mq

_mq.scssのようにパーシャルファイルを作成して、How to use itに用意されているテンプレートをペーストして設定をしていきます。
他の変数ファイルの前後でインポートしておきます。

// To enable support for browsers that do not support @media queries,
// (IE <= 8, Firefox <= 3, Opera <= 9) set $mq-responsive to false
// Create a separate stylesheet served exclusively to these browsers,
// meaning @media queries will be rasterized, relying on the cascade itself
$mq-responsive: true;

// Name your breakpoints in a way that creates a ubiquitous language
// across team members. It will improve communication between
// stakeholders, designers, developers, and testers.
$mq-breakpoints: (
    mobile:  320px,
    tablet:  740px,
    desktop: 980px,
    wide:    1300px,

    // Tweakpoints
    desktopAd: 810px,
    mobileLandscape: 480px
);

// Define the breakpoint from the $mq-breakpoints list that should
// be used as the target width when outputting a static stylesheet
// (when $mq-responsive is set to 'false').
$mq-static-breakpoint: desktop;

// If you want to display the currently active breakpoint in the top
// right corner of your site during development, add the breakpoints
// to this list, ordered by width. For example: (mobile, tablet, desktop).
$mq-show-breakpoints: (mobile, mobileLandscape, tablet, desktop, wide);

// If _mq.scss is in your project:
@import 'path/to/mq';
// With eyeglass:
@import 'sass-mq';
// With webpack (and boilerplates such as create-react-app)
@import '~sass-mq';

下記もsass-mqのインポートの前に設定しておくといいかなと思います。

/// Base font size on the `<body>` element
/// @type Number (unit)
$mq-base-font-size: 16px !default;

/// Customize the media type (for example: `@media screen` or `@media print`)
/// By default sass-mq uses an "all" media type (`@media all and …`)
///
/// @type String
/// @link https://github.com/sass-mq/sass-mq#changing-media-type Full documentation and examples
$mq-media-type: all !default;

ここから6つの変数とインポート方法について説明します。
詳しい仕様は公式サイトのVariablesを確認してください。

$mq-base-font-size

/// Base font size on the `<body>` element
/// @type Number (unit)
$mq-base-font-size: 16px !default;

ルート要素のフォントサイズを10pxにしている場合などは$mq-base-font-size: 10px !default;にします。デフォルトは16pxです。

$mq-media-type

/// Customize the media type (for example: `@media screen` or `@media print`)
/// By default sass-mq uses an "all" media type (`@media all and …`)
///
/// @type String
/// @link https://github.com/sass-mq/sass-mq#changing-media-type Full documentation and examples
$mq-media-type: all !default;

メディアクエリの条件を@media print, screen and (min-width: 768px)のようにしている場合は$mq-media-type: print, screen !default;のようにします。デフォルトはallです。

$mq-responsive

// To enable support for browsers that do not support @media queries,
// (IE <= 8, Firefox <= 3, Opera <= 9) set $mq-responsive to false
// Create a separate stylesheet served exclusively to these browsers,
// meaning @media queries will be rasterized, relying on the cascade itself
$mq-responsive: true;

メディアクエリに対応していない古いブラウザ用の設定です。基本的にtrueのままで大丈夫です。

$mq-breakpoints

// Name your breakpoints in a way that creates a ubiquitous language
// across team members. It will improve communication between
// stakeholders, designers, developers, and testers.
$mq-breakpoints: (
    mobile:  320px,
    tablet:  740px,
    desktop: 980px,
    wide:    1300px,

    // Tweakpoints
    desktopAd: 810px,
    mobileLandscape: 480px
);

ブレイクポイントのpx値と、keyになるキーワードを設定します。
このpx値がmin-widthとして使用され、max-widthの時は0.01em引いた数値が自動で計算されます。
keyはsppcでもいいですし、smmd(SmallとMedium)などでもOKです。チームメンバーと相談をして、ユビキタス言語(同意を得た用語)を設定してくださいと説明されています。
個人的にデバイスではなく、相対的な名前をよく使っているので、以下のようにしています。

$mq-breakpoints: (
  sm:  375px,
  md:  768px,
  lg: 1024px,
  xl: 1440px,
);

$mq-static-breakpoint

// Define the breakpoint from the $mq-breakpoints list that should
// be used as the target width when outputting a static stylesheet
// (when $mq-responsive is set to 'false').
$mq-static-breakpoint: desktop;

$mq-responsivetrueにしている場合は、変更の必要は必要ありません。

$mq-show-breakpoints

// If you want to display the currently active breakpoint in the top
// right corner of your site during development, add the breakpoints
// to this list, ordered by width. For example: (mobile, tablet, desktop).
$mq-show-breakpoints: (mobile, mobileLandscape, tablet, desktop, wide);

Sass MQではブレイクポイントをmq-px2em()でpxからemに変換されています。デベロッパーツールでブレイクポイントのpx値が判断しにくい場合は、$mq-show-breakpoints: (sm, md, lg, xl);のようにkeyを渡すとbodyセレクタにCSSが追加されて、画面の右上にmd ≥ 768px (48em)のようなラベルが表示されるようになります。

以下は出力されるCSSのサンプルです。

body:before {
  background-color: #fcf8e3;
  border-bottom: 1px solid #fbeed5;
  border-left: 1px solid #fbeed5;
  color: #c09853;
  font: small-caption;
  padding: 3px 6px;
  pointer-events: none;
  position: fixed;
  right: 0;
  top: 0;
  z-index: 100
}
@media (min-width:23.4375em) {
  body:before {
    content: "sm ≥ 375px (23.4375em)"
  }
}
@media (min-width:48em) {
  body:before {
    content: "md ≥ 768px (48em)"
  }
}
@media (min-width:64em) {
  body:before {
    content: "lg ≥ 1024px (64em)"
  }
}
@media (min-width:90em) {
  body:before {
    content: "xl ≥ 1440px (90em)"
  }
}

@import

// If _mq.scss is in your project:
@import 'path/to/mq';
// With eyeglass:
@import 'sass-mq';
// With webpack (and boilerplates such as create-react-app)
@import '~sass-mq';

テンプレートの上記の箇所で、Sass MQをインポートする3つの方法があげられています。

  1. ローカルのnode_modulesのファイルをインポートする(@import 'path/to/mq';
  2. eyeglassというツールを使ってインポートする(@import 'sass-mq';
  3. webpackの記法でインポートする(@import '~sass-mq';

いずれかの方法でインポートします。使わないものは削除してしまって大丈夫です。
ここではローカルのファイルを相対パスでインポートしています。

@import "../../../../../node_modules/sass-mq/_mq.scss";

Sass MQの機能

Sass MQはいくつかのfunctionとmixinのライブラリですが、ここではメディアクエリのmixinであるmq()の使い方を説明します。
詳しい仕様は公式サイトのmq()を確認してください。

4つの引数($from$until$and$media-type)の組み合わせによって出力が変わります。使う頻度が少なそうなので、今回は$andの使い方は載せていません。

Name parameterDescription parameterType parameterDefault value
$from (false) - One of $mq-breakpoints String or Boolean —none
$until (false) - One of $mq-breakpoints String or Boolean —none
$and (false) - Additional media query parameters String or Boolean —none
$media-type ($mq-media-type) - Media type: screen, print… String

$frommin-width$untilmax-widthと読み替えてください。
$from$untilの「parameterDescription」を見ると、初期値はfalseになっていることが分かります(実際のコードはこちら)。$from$until、あるいは両方を指定することで出力も変わります。

  • ($from: md) mdmin-widthで出力
  • ($until: md) mdを(0.01em引いた)max-widthで出力
  • ($from: md, $until: lg) mdmin-widthlgmax-widthandで繋げて出力

@media (min-width: em)

Sass MQはモバイルファーストで設計されているので、min-widthの場合は第一引数にkeyを渡すだけです($from: mdなどとする必要はありません)。

.min-width {
  @include mq(md) {
    content: "@media (min-width:48em)";
  }
}
@media (min-width: 48em) {
  .min-width {
    content: "@media (min-width:48em)"
  }
}

@media (max-width: em)

max-widthの場合は、mq($until: md)のように$untilに直接渡すか、mq(false, md)のようにして第二引数に渡します。

.max-width {
  @include mq($until: md) {
    content: "@media (max-width:47.99em)";
  }
}

.max-width {
  @include mq(false, md) {
    content: "@media (max-width:47.99em)";
  }
}
@media (max-width:47.99em) {
  .max-width {
    content: "@media (max-width:47.99em)"
  }
}

@media (min-width: em) and (max-width: em)

以上と未満を組み合わせる場合は、第一引数に「以上」を、第二引数に「未満」を渡します。
下記のmq(md, lg)は引数の順番を変えていないので、mq($from: md, $until: lg)と指定しているのと同じことになります。

.min-and-max-width {
  @include mq(md, lg) {
    content: "@media (min-width:48em) and (max-width:63.99em)";
  }
}
@media (min-width:48em) and (max-width:63.99em) {
  .min-and-max-width {
    content: "@media (min-width:48em) and (max-width:63.99em)"
  }
}

@media print、@media screen and (-ms-high-contrast: active), print

印刷時や、印刷時とハイコントラストモードの場合は$media-typeに条件を直接渡します。

.print {
  @include mq($media-type: "print") {
    content: "@media print";
  }
}

.screen-and-high-contrast-and-print {
  @include mq($media-type: "screen and (-ms-high-contrast: active), print") {
    content: "@media screen and (-ms-high-contrast:active), print";
  }
}
@media print {
  .print {
    content: "@media print"
  }
}

@media screen and (-ms-high-contrast:active), print {
  .screen-and-high-contrast-and-print {
    content: "@media screen and (-ms-high-contrast:active), print"
  }
}

emではなくpxを使うには

PX, EM or REM Media Queries?という記事で検証されているように、メディアクエリの単位はemを使うことがベターとされています。
検証結果は以下のようなものでした。

  • pxは、ズームしている時とユーザーがフォントサイズを変更している時にSafariでズレる。
  • remは、html { font-size: 200%; }などとした時にSafariでズレる(モバイルも含む)。

Sass MQもそれにならってpxではなくemを使っています。どうしてもpxを使いたい場合は、emの代わりにpxを使用しているforkを使うことを推奨しています(Issueはこちら)。