Viewportまとめ

HTML・CSS の viewport がよく分からなかったので、実験・調査した結果をまとめてみた。
モバイルとデスクトップで挙動が異なるので、その違いを中心に書いている。
本来は OS・ブラウザ・バージョンの組み合わせでも多少挙動が異なるが、今回はこの差異は考慮せず、MacBook Pro と iPhone XS に固定して検証した。


## TL;DR
モバイル対応するには、モバイル向けの basic.css とデスクトップ向けの desktop.css を用意し、HTML の `<head>` 内で次のように書く。

```html
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/basic.css" rel="stylesheet">
<link href="css/desktop.css" rel="stylesheet" media="(min-width: 600 px)">
```

または、CSS 内のメディアクエリで次のように書く。

```css
/* モバイル向け CSS 記述 */

@media (min-width: 600 px) {
  /* デスクトップ向け CSS 記述 */
}
```


## 検証環境
- MacBook Pro (15-inch, 2018) 10.14.4 (Mojave), Google Chrome
- iPhone XS (iOS 12.3), Google Chrome


## ピクセル
- 物理ピクセル：ディスプレイの物理的なピクセル
  - e.g., MacBook Pro (15-inch): 2,880 x 1,800
  - e.g., iPhone XS: 2,436 x 1,125
- CSS ピクセル（論理ピクセル）
  - 疑似解像度・スケーリング解像度
  - n^2 個の物理ピクセルで 1 個の CSS ピクセルを構成
  - e.g., 4 個の物理ピクセルで 1 個の CSS ピクセルを構成
  - e.g., MacBook Pro (15-inch): 1,024 x 640 - 1,920 x 1,200
  - e.g., iPhone XS: 812 x 375
- DPR (Device Pixel Ratio)
  - 物理ピクセルと CSS ピクセルの比率
  - e.g., 1 個の CSS ピクセルが 2^2 の物理ピクセルで構成される場合、DPR は 2


## Viewport

|  | モバイル | デスクトップ |
---|---|---
| viewport のデフォルトの横幅 | 980 px | ブラウザの論理ピクセルの横幅 |

- viewport の横幅 = ページ全体の width: 100%


## メディアクエリ

|  | モバイル | デスクトップ |
---|---|---
| width | viewport の横幅 | viewport の横幅 |
| device-width（非推奨） | ディスプレイの論理ピクセルの横幅 | ディスプレイの論理ピクセルの横幅 |


## JavaScript で取得できる横幅

|  | モバイル | デスクトップ |
---|---|---
| screen.width | ディスプレイの論理ピクセルの横幅 | ディスプレイの論理ピクセルの横幅 |
| window.innerWidth | viewport の横幅（？） | viewport の横幅（スクロールバー含） |
| document.documentElement.clientWidth | viewport の横幅 | viewport の横幅 - スクロールバーの横幅 |

- デスクトップの window.innerWidth の横幅はディスプレイ全体でなく、ブラウザが基準
- モバイルの window.innerWidth は viewport の横幅っぽいが、少し挙動が怪しい


## 一致するもの
- 共通
  - ディスプレイの論理ピクセルの横幅 = device-width = screen.width
- モバイル
  - viewport の横幅 = width = document.documentElement.clientWidth
- デスクトップ
  - viewport の横幅 = width = window.innerWidth


## \<meta name="viewport" content=""\> の content 属性の値
### width
>- 値：正の整数または文字列 `device-width`
>- ウェブサイトを描画したいビューポートの幅をピクセル数で定義します。

- モバイルでのデフォルトは 980 px


### initial-scale
>- 値：`0.0` から `10.0` までの、正の数値
>- デバイスの幅 (ポートレートモードでの `device-width` またはランドスケープモードでの `device-height`) とビューポートの寸法との比率を定義します。

- device-width / initial-scale = width
  - e.g., `initial-scale=0.5` の場合、iPhone XS（device-width: 375 px）の（viewport の）width は  
    `375 px / 0.5 = 750 px`
- `initial-scale=1.0` の場合、`width = device-width`
  - e.g., `<meta name="viewport" content="initial-scale=1.0">`


### maximum-scale
>- `0.0` から `10.0` までの、正の数値
>- ズームの最大値を定義します。この値は `minimum-scale` と同じまたはより大きくしなければなりません。そうではないときの動作は未定義です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。

- device-width / maximum-scale の横幅までズームイン可能
- e.g., `maximum-scale=2.0` の場合、iPhone XS（device-width: 375 px）では、`375 px / 2.0 = 188 px` より、横幅 188 px までズームイン可能
  - `maximum-scale=1.0`：横幅 375 px まで（devie-width 以上にズーム不可）
  - `maximum-scale=0.5`：横幅 750 px まで

### minimum-scale

>- `0.0` から `10.0` までの、正の数値
>- ズームの最小値を定義します。この値は `maximum-scale` と同じまたはより小さくしなければなりません。そうではないときの動作は未定義です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。

## 検証

検証に用いた HTML・CSS・JavaScript のソースコードとその表示結果を貼っておきます。

- [ソースコード](https://github.com/haru52/viewport_test)
- [表示結果](https://haru52.com/viewport_test/)

## 感想
かなりややこしい。モバイルでの viewport の横幅がデフォルトで 980 px なのが諸悪の根源な気がする。普通に device-width の値がデフォルトでは駄目なのだろうか。

## 今後の課題

- ~~CSS の [@viewport](https://developer.mozilla.org/ja/docs/Web/CSS/@viewport) の調査~~ ← deprecated になっていました
- モバイルのポートレートモード（縦長）とランドスケープモード（横長）での挙動の調査

## 参考

- Peter Gasston（2015）『CSS3開発者ガイド 第2版』牧野聡訳，オライリー・ジャパン，[https://www.oreilly.co.jp/books/9784873117256/](https://www.oreilly.co.jp/books/9784873117256/)．
- [\<meta\>: 文書レベルメタデータ要素 - HTML: HyperText Markup Language | MDN](https://developer.mozilla.org/ja/docs/Web/HTML/Element/meta)
- [device-width - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/@media/device-width)
- [Configuring the Viewport](https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html)
- [A pixel is not a pixel is not a pixel - QuirksBlog](https://www.quirksmode.org/blog/archives/2010/04/a_pixel_is_not.html)
- [モバイルブラウザでのレイアウトを制御するために meta タグの viewport を使う - Mobile | MDN](https://developer.mozilla.org/ja/docs/Mobile/Viewport_meta_tag)