vwとremを組み合わせて、アスペクト比を維持したサイズ可変レイアウトを実装する

[un-T factory! XA Advent Calendar](https://qiita.com/advent-calendar/2018/unt-xa) 10日目の記事です。

# リキッドレイアウトの葛藤

リキッドレイアウトによるWebページ制作の際、表示端末の画面幅（viewport）の違いによって各要素のサイズ比率が変わり、デザイン時の意図と異なる見た目になってしまうことがあります。

特にスマートフォンやタブレットでは端末によってブラウザ幅が固定されるため、テキストの改行のされ方や周辺要素とのレイアウトバランスなど、ちょっとした違いでページの印象が大きく変わることもあります。

![qiita_02.png](https://qiita-image-store.s3.amazonaws.com/0/25032/c629bf7b-8ba4-8968-600e-796e6d946dff.png)

「さまざまな解像度の端末があるのだから、そのような違いが出ることも許容するべき」という考え方にはもちろん同意ですが、今回は、CSSのサイズ指定を少し工夫して**アスペクト比を維持したサイズ可変レイアウトを手軽に実装する**方法を紹介します。

# サイズ可変レイアウトの実装

一つの例として、**画面幅（viewport）が320px〜480pxの範囲で、アスペクト比を維持したままページ内コンテンツのサイズを変化させる**場合を考えます。

なお、**基準となるデザインカンプは320px幅で作成**されているものとします。

## 実装イメージ

![qiita_181210.gif](https://qiita-image-store.s3.amazonaws.com/0/25032/c2ef3cc7-bc8d-ce5a-9ca1-c75861e565f6.gif)

画面幅を変更した場合も、ページ内コンテンツは比率を保ったまま拡大縮小し、テキストの改行位置も維持されています。

## vwとremによるfont-size指定

```css:CSS
@media screen and (min-width: 320px) and (max-width: 480px) {
  html {
    font-size: 3.125vw;
    /*
      計算式：　10(px) / 320(px) * 100 = 3.125
      画面幅320pxの時に "font-size: 10px" 相当になる
    */
  }

  body {
    font-size: 1.6rem;
    /*
      html要素で指定されたfont-sizeの1.6倍になる
    */
  }
}
```

サイズ計算をしやすくするため、まずはhtml要素に対して `font-size: 3.125vw` を指定します。
`vw` は**画面幅を基準とした相対サイズ単位**であるため、**画面幅が320pxの時には `font-size: 10px` 相当**になります。

次にbody要素に対して `font-size: 1.6rem` を指定してみましょう。
`rem` は**html要素（ルート要素）の font-size を基準とした相対サイズ単位**であるため、結果的にbody要素内のテキストは、**画面幅が320pxの時に `font-size: 16px` 相当**になります。

また、テキストサイズの基準値を `vw` で指定しているため、例えば画面幅が320px→384px（1.2倍）に広がった場合はテキストサイズも同様に1.2倍に（同じ比率で）拡大されます。

| 画面幅 | 倍率 | html要素のfont-size（=1rem） | body要素のfont-size |
|:--|:--|:--|:--|
| 320px | (1) | 10px 相当 | 16px 相当 |
| 360px | 1.125 | 11.25px 相当 | 18px 相当 |
| 384px | 1.2 | 12px 相当 | 19.2px 相当 |
| 400px | 1.25 | 12.5px 相当 | 20px 相当 |
| 480px | 1.5 | 15px 相当 | 24px 相当 |

このようにして、画面幅320px〜480pxの範囲内で、**テキストサイズが画面幅に応じてスムーズに変化する**仕組みを作ることができました。

## レイアウトに関するサイズ指定をすべてremにする

**`rem` によるサイズ指定は `font-size` 以外のプロパティに対しても有効**です。

レイアウトに関するさまざまな数値（`width`, `height`, `margin`, `padding` など）を `rem` で指定することで、画面幅に応じて、アスペクト比を維持したまま各要素のサイズを変化させることが可能になります。

![スクリーンショット-2018-12-09-22.20.48.png](https://qiita-image-store.s3.amazonaws.com/0/25032/25336400-8adb-cd7a-8db6-34bd24256131.png)

320px幅で作成されたデザインカンプを見ながら、各要素のサイズをCSSに落とし込みます。

前述の通りhtml要素（ルート要素）のテキストサイズ（=1rem）は**画面幅320pxの時に `font-size: 10px` 相当**になっています。

よって、**320px幅のデザインカンプ上における10pxを、CSS側で `1rem` として（10分の1にして）指定**していくと、カンプ通りのレイアウトを再現できることになります。

| カンプ上のサイズ | CSSの指定値 |
|--:|--:|
| 5px | 0.5rem |
| 10px | 1rem |
| 100px | 10rem |

```css:CSS
/* サイズに関する記述を抜粋 */

header {
  font-size: 2.2rem;
}

.content {
  padding: 2rem 2rem;
}

figure {
  position: relative;
}

figcaption {
  position: absolute;
  bottom: 1.6rem;
  left: 1.6rem;
  font-size: 1.4rem;
}

.lead {
  margin-top: 1.4rem;
  font-size: 1.2rem;
}
```

サイズに関する数値をすべて `rem` で指定しています。

画面幅を 320px〜480px の間で変化させた場合も、各要素がアスペクト比を維持したまま拡大縮小し、レイアウトバランスが保たれます。

## px指定との併用も可能

**画面幅にかかわらずサイズを固定したいプロパティ**に対しては、`rem` ではなく `px` による指定をすればOKです。

例えばあるレイアウトブロックをサイズ可変にしつつ、境界線（border）は1px幅のままで固定したい場合は、次のように `rem` と `px` を併用することで実現できます。

```css:CSS
.sample-block {
  margin: 3rem;
  padding: 2rem;
  border: 1px solid red; /* border幅のみpxで指定 */
}
```

## 注意点

この設計手法は、**html要素の `font-size` を `vw` で指定する**ことが大前提となります。
何らかの理由でhtml要素のスタイルを変更できない場合は、各レイアウトパーツごとに個別に `vw` 値を計算して指定していく方法を検討しましょう。

# 参考リンク

[length #vw | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/length#vw)
[font-size #rem | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/font-size#Rems)