yasuda

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

レスポンシブWebデザインではメディアクエリ(media queries)を書くことが多くなります。通常のCSSではブレイクポイントを変更したくなったときに、すでに書いてしまった箇所を直していくのはとても大変です。

Sass(scss記法)を使えば、変数や@mixinを使うことで1箇所で管理することが容易になります。今回はSassでメディアクエリを管理する方法を紹介します。

ブレイクポイントを変数(マップ型)で定義する

メディアクエリにはブレイクポイントと呼ばれる処理を変更する横幅を決めます。通常のSassの変数でブレイクポイントを定義した場合は以下のようになります。

$breakpoint-sm: 400px !default;
$breakpoint-md: 768px !default;
$breakpoint-lg: 1000px !default;

今回は変数は変数でも、マップ型の変数で定義をします。

$breakpoints: (
  'sm': 'screen and (min-width: 400px)',
  'md': 'screen and (min-width: 768px)',
  'lg': 'screen and (min-width: 1000px)',
  'xl': 'screen and (min-width: 1200px)',
) !default;

マップ型は変数の中に1つ以上のキーと値のペアを定義できるデータタイプです。上記の場合では$breakpointsという変数の中にsmというキーとscreen and (min-width: 400px)という値のペアがあり、合計で4つのペアが定義されています。

詳しい説明は割愛しますが、マップ型で変数を定義しておくとSassの@eachで繰り返しの処理をすることができるので、このブレイクポイント用の変数を使って色々な@mixinを作ることができるようになります。

@mixinでメディアクエリを呼び出す

ブレイクポイントを定義した変数をmq()という@mixinで呼び出せるようにします。

@mixin mq($breakpoint: md) {
  @media #{map-get($breakpoints, $breakpoint)} {
    @content;
  }
}

map-get()の第一引数に変数名、第二引数にマップ型のキーを渡すと、マップ型の値が返ります。#{}はインターポーレーションと呼ばれるもので、通常はエラーになってしまう箇所でも出力できるようになります。
@mixinの引数$breakpointには初期値としてmdを設定しています。ここには案件ごとのメインで使うブレイクポイントを指定しておきます。メインで使うブレイクポイントのキーを変更したくなった場合でも、引数を省略していれば@mixinの定義側だけで対応することができます。

@mixinを呼び出すときは以下のようになります。

.foo {
  color: blue;
  @include mq() { // 引数を省略(初期値はmdの768px)
    color: yellow;
  }
  @include mq(lg) { // 引数を個別に指定
    color: red;
  }
}

このように出力(コンパイル)されます。

.foo {
  color: blue;
}
@media screen and (min-width: 768px) { /* `mq()` */
  .foo {
    color: yellow;
  }
}
@media screen and (min-width: 1000px) { /* `mq(lg)` */
  .foo {
    color: red;
  }
}

min-widthのメディアクエリだけを作っているので、max-widthのパターンも作る場合は以下のようになります。$breakpoint-up$breakpoint-downの値はいつでも変更することができます。@mixinを変更する必要はありません。

// 変数の定義
// min-width
$breakpoint-up: (
  'sm': 'screen and (min-width: 400px)',
  'md': 'screen and (min-width: 768px)',
  'lg': 'screen and (min-width: 1000px)',
  'xl': 'screen and (min-width: 1200px)',
) !default;

// max-width
$breakpoint-down: (
  'sm': 'screen and (max-width: 399px)',
  'md': 'screen and (max-width: 767px)',
  'lg': 'screen and (max-width: 999px)',
  'xl': 'screen and (max-width: 1199px)',
) !default;

// @mixinの定義
@mixin mq-up($breakpoint: md) {
  @media #{map-get($breakpoint-up, $breakpoint)} {
    @content;
  }
}

@mixin mq-down($breakpoint: md) {
  @media #{map-get($breakpoint-down, $breakpoint)} {
    @content;
  }
}

応用編:レスポンシブ対応のクラスを生成する@mixin

応用編としてマップで定義した変数を使って、レスポンシブに対応したクラスを生成する@mixinを作ります。
簡単に処理を説明すると、マップ型の変数からキーと値をもってきて、メディアクエリの条件#{$value}とクラス名の接尾辞#{$suffix}として書き出します。メディアクエリなしから始まり、マップ型変数で定義した数だけブレイクポイントを追加します。

@mixin responsive($class, $bp: $breakpoint-up) {
  #{$class} {
    @content;
  }
  @each $suffix, $value in $bp {
    @media #{$value} {
      #{$class}-#{$suffix} {
        @content;
      }
    }
  }
}

呼び出し方は@mixinの第一引数にクラス名を指定、@includeの中に指定したいスタイルを記述します。

@include responsive('.display-none') {
  display: none;
}

このようにCSSが出力されます。

.display-none {
  display: none;
}

@media screen and (min-width: 400px) {
  .display-none-sm {
    display: none;
  }
}

@media screen and (min-width: 768px) {
  .display-none-md {
    display: none;
  }
}

@media screen and (min-width: 1000px) {
  .display-none-lg {
    display: none;
  }
}

@media screen and (min-width: 1200px) {
  .display-none-xl {
    display: none;
  }
}

@mixinの第二引数に$breakpoint-downを渡すとmax-widthのパターンにすることもできます。

@include responsive('.display-none', $breakpoint-down) {
  display: none;
}

まとめ

Sassのマップ型変数と@mixinを使うことで、ブレイクポイントの変更を変数や@mixinの修正だけで完結させることができるようになります。
変数の値を変更するだけで、どんな案件にも使い回すこともできますし、ブレイクポイントの変更が簡単にできるようになります。

今回紹介した機能については、以下のリンク先で詳しく説明されています。

お問い合わせはこちら