レスポンシブwebデザインのコーディングでやってること

今年、携わった案件のほとんどがレスポンシブWebデザインでした。
今更ですが、私がレスポンシブWebデザインのコーディングでやっていることや気を付けていることを整理してみたいと思います。

User Agent Style Sheetの対応

ブラウザのデフォルトで設定されているスタイルシートの差異を埋めるCSSは「Normalize.css」をよく使います。
デザインによっては、Eric Meyer’s的なRest CSSの方がやりやすい場合もあるので、そこは見た目に臨機応変に対応してます。

CSS Flexible Box

私が受け持つ案件では、最新ブラウザ、及びInternet Explorer 11(IE11)のみの対応が増えたため、近年では、Flexible Boxをカジュアルに使用できるようになりました。
個人的には、広い画面では横並びだったものを、displayプロパティを変更するだけで、狭い画面で縦に並べられるなど、見た目の切り替えが非常に楽になりました。

便利なFlexible Boxですが、min-heightプロパティを使う時はバグに気を付けています。
Flexible Boxが指定されている要素に、min-heightプロパティを指定すると、IE11で要素の高さが保てない時があったり、「 align-items」プロパティを併用時に上下中央揃えが効かない場合があります。

CSS

.foo {
  display: flex;
  min-height: 200px; /* IEでは鬼門 */
  align-items: center;
}

モバイル対応などであれば問題ありませんが、IE11も含めて対応する場合は、CSSの書き方を状況にあわせて変える必要があります。
上記のコードのように、子の要素を上下中央揃えしたい場合は、Flexible Boxを指定した要素に擬似要素を作成し、その要素にmin-heightプロパティを指定する方法もあります。

HTML

<ul class="parent">
  <li>demo</li>
  <li>demo</li>
</ul>

CSS

.parent {
  display: flex;
  align-items: center;
}
.parent::before {
  content: '';
  min-height: 200px;
}

ただし上記のコードは「justify-content: space-between;」宣言を使用した場合、有効に働きません。
擬似要素も要素の1つとして、スペースが均等割されてしまうからです。

Flexible Boxとmin-heightプロパティの併用が必要な時はバグの発生を想定しつつ、状況に応じてCSSを書き換えています。

開発環境

開発環境は、数年前までgulpを使っていましたが、最近はnpm-scriptsでbuildやwatchを行なっています。

そしてCSSプリプロセッサーは、Sass。
CSSメタ言語は、SCSS。
またautoprefixerCSS MQPackerなどのPostCSSも併用してコーディングしています。

Sassでやってること

Sassの汎用的な使い方は、色やz-indexの管理、ひとつのmap型の変数から複数のボタン生成などを行っています。

Sassを使ったレスポンシブwebデザインの対応では、Media Queriesや流動的なサイズ調整が主です。

Media Queries

Media Queriesは、Mixinを使って実装しています。
mapのキーを使ってメディアの指定を行っているので、素早く記述できます。

使用例

.foo {
  @include mq(sm) {
    content: 'sm';
  }
}

以下、Media QueriesのSassの記述を解説します。

Media Queries: 変数

変数は、「$screen」というmapの型を用意します。
またSassの独自関数の下準備のため、 「$css-default-font-size」変数も用意します。

/// CSS Default Font Size
$css-default-font-size: 16px;

/// Breakpoint
$screen: (
  sm: 544px,
  md: 749px,
  lg: 992px
) !default;

Media Queries: 関数

Media QueriesのMixinsで使うための、独自関数も用意しておきます。
下記に表示されているのは「数値から単位を除外」「pxからrem」「pxからem」などに変換する関数です。

/// 数値から単位を除外
///
/// @param {Number} $number - 数値
///
/// @example scss
///   .foo {
///     content: strip-unit(14px); // 14
///   }
@function strip-unit($number) {
  @if (type-of($number) == 'number' and not unitless($number)) {
    @return $number;
  }

  @return $number / ($number * 0 + 1);
}

/// `px` から `rem` に変換
///
/// @param {Number} $number - 単位付き数値
///
/// @example scss
///   .foo() {
///     content: px2rem(16px); // 1rem
///     content: px2rem(50%); // 50%
///   }
@function px2rem($number) {
  $result: $number;

  // 単位がpx以外なら値をそのまま返す
  @if (not(unit($number) == 'px')) {
    @return $result;
  }

  $result: strip-unit($number / $css-default-font-size) * 1rem;

  @return $result;
}

/// `px` から `em` に変換
///
/// @param {Number} $number - 単位付き数値
/// @param {Number} $parent-number - 親の単位付き数値
///
/// @example scss
///   .foo {
///     content: px2em(16px); // 1em
///     content: px2em(16px, 18px); // 0.89em
///     content: px2em(50%); // 50%
///   }
@function px2em($number, $parent-number: $css-default-font-size) {
  $result: $number;

  // 単位がpx以外なら値をそのまま返す
  @if (not(unit($number) == 'px')) {
    @return $result;
  }

  $result: strip-unit($number / $parent-number) * 1em;

  @return $result;
}

「数値から単位を除外」は、px単位から別の単位に変換するときに利用します。

「pxからrem」に変換する関数は、最新ブラウザ対応だけど、文字変更が可能なブラウザ対応が求められる場合に利用します。
いわゆるアクセシビリティ的な要件が求められる場合です。
ただその場合は、postcss-pxtoremも併用することが多いです。

「pxからem」に変換する関数は、メディア特性の単位変換時によく利用しています。
例えば、「@media (min-width: 400px)」と指定するのではなく、「@media (min-width: 25em)」になるようにemに変換します。

メディア特性の単位にemを使う理由としては、下記のURLを参照。
PX, EM or REM Media Queries? | Zell Liew

Media Queries: Mixin

変数と関数を用意したら、あとはMixinを用意するだけで使えます。

/// Media Queries
@mixin mq($size, $width: max) {
  $result: 0;
  @if(type-of($size) == number) { 
    $result: $size;
  } @else {
    @each $key, $value in $screen {
      @if($key == $size) {
        $result: $value;
      }
    }
  }
  @if($width == min) {
    $result: $result + 1;
  }
  @media (#{$width}-width: #{px2em($result)}) {
    @content;
  }
}

定義した「mq」Mixinの第1引数は「$screen」変数のキーを定義し、第2引数はメディア特性の「max-width」か「min-width」のどちらを設定するか選択できます。

このMixinを利用した例は下記の通りです。

SCSSの記述

.foo {
  @include mq(sm) {
    content: 'sm';
  }
  @include mq(sm, min) {
    content: 'sm, min';
  }
  @include mq(lg) {
    content: 'lg';
  }
  @include mq(1340px) {
    content: '1340px';
  }
}

出力されたCSSの記述

@media (max-width: 34em) { /* 544px相当 */
  .foo {
    content: "sm";
  }
}
@media (min-width: 34.0625em) { /* 545px相当 */
  .foo {
    content: "sm, min";
  }
}
@media (max-width: 62em) { /* 992px相当 */
  .foo {
    content: "lg";
  }
}
@media (max-width: 83.75em) { /* 1340px相当 */
  .foo {
    content: "1340px";
  }
}

Sassが動いているコードは下記。
Media Queries Mixins | SassMeister | The Sass Playground!

「$screen」変数だけで管理できればいいのですが、レスポンシブのデザイン上、微妙なメディア特性の値も必要になるケースもあるので、「1340px」などの数値も通るようにしています。

流動的なサイズ調整

ブレイクポイントの切り替わりでフォントサイズなどが、ガタッとなるアレを解消するアレです。

ガタッてなるアレ

ガタッてならないアレ

この流動的なサイズ調整は、グラフにすると分かりやすいでしょう。
ガタッてなるアレは、ブレイクポイントの切り替わりでフォントサイズが大きく変化します。

しかし流動的なサイズ調整は特定のウィンドウサイズだけサイズが相対的に変化し、それ以外のウィンドウサイズでは一定のフォントサイズを保ちます。

ガタッてなるアレのグラフ

ガタッてならないアレのグラフ

この流動的なサイズ調整のMixinの計算式は、Fluid-responsive font-size calculatorを元に作成しています。

Mixin

@mixin flsz($max-font-size, $min-font-size, $max-viewport, $min-viewport, $propaty: 'font-size', $px2rem: false) {
  
  $maxvw: 0;
  $minvw: 0;

  @if(type-of($max-viewport) == number) {
    $maxvw: $max-viewport;
  }
  @if(type-of($min-viewport) == number) {
    $minvw: $min-viewport;
  }

  @each $key, $value in $screen {
    @if(($key == $max-viewport) and ($maxvw == 0)) {
      $maxvw: $value;
    }
    @if(($key == $min-viewport) and ($minvw == 0)) {
      $minvw: $value;
    }
  }

  $x: $minvw / 100;
  $y: 100 * ($max-font-size - $min-font-size) / ($maxvw - $minvw);
  $z: $min-font-size;

  @if($px2rem) {
    $z: px2rem($z);
  }

  #{$propaty}: $z;

  $maxmq: px2em($maxvw + 1);
  $minmq: px2em($minvw + 1);

  @media (min-width: #{$minmq}) {
    #{$propaty}: calc(#{$z} + ((1vw - #{$x}) * #{strip-unit($y)}));
  }
  @media (min-width: #{$maxmq}) {
    #{$propaty}: $max-font-size;
  }
}

上記のMixinは、Media Queriesで使用した変数や独自関数もそのまま使っています。
そのため「$screen」変数の「sm」や「lg」といったキーもそのまま利用でき、メディア特性のemへの変換なども自動で行います。

「flsz」Mixinを使ったSCSSの記述

.fluid-font-size {
  @include flsz(48px, 24px, lg, sm);
}
.fluid-font-size2 {
  @include flsz(16px, 32px, lg, 500px);
}

.fluid-padding {
  @include flsz(32px, 4px, lg, 500px, padding-top);
  background-color: #aaa;
}

Fluid Sizeの実際の動き - CodePen

ヒーロー画像の上に置く文字など流動的に変化してほしい箇所で使ってます。

最後に

今年もなんとかブログを更新できました。
来年も最低1回は更新したいです。