
viewportは、スマホなどでページの表示幅を決める仮想的な画面幅(実際の画面解像度とは別に、ブラウザが表示計算に使う値)のことです。この指定を誤ると、文字が小さすぎて読みにくくなったり、逆に横に間延びしてレイアウトが崩れたりすることがあります。そうならないために設定するのが、viewportメタタグです。
viewportメタタグの基本形
シンプルなviewport設定。
<meta name="viewport" content="width=device-width, initial-scale=1.0">スマホのブラウザは、何も指定しないとPC向けのレイアウト幅(980px程度)でページを読み込み、それを画面に収まるよう縮小して表示します。この状態だと文字が小さくなりすぎるため、次の一行をhead内に入れて、スマホの画面幅に合わせて表示するのが基本形です。
【width】デバイス幅に合わせるか固定値にするか
widthは、ビューポートの基準値を指定するプロパティです。指定できる値は主に width=device-width と width=360 のような固定値の2パターンあります。
width=device-width は、端末が持つCSS上の画面幅(iPhoneなら390pxなど、機種ごとに異なる値)をそのまま使うことです。レスポンシブデザインで機種ごとに最適な幅を使いたい場合はこちらが基本です。
固定値は、機種によらず同じ幅でレイアウトを組みたい場合に設定します。大画面の端末では相対的に文字や要素が小さく表示されます。
固定値を使うケースは限られますが、たとえば「デザインカンプの幅(375pxなど)に厳密に合わせたい」「広告枠のサイズをどの端末でも統一したい」といった場合に使用されます。特別な理由がなければ device-width を使うのが無難です。
極端に小さい値(width=240など)を指定すると、ブラウザ側が「これは表示上おかしい」と判断して幅を自動調整することがあります。狙った表示にならない場合は、まずこの部分を疑ってみてください。
【initial-scale】初期のズーム倍率
initial-scale は、ページを開いたときの初期ズーム倍率を指定します。1(または1.0)を指定すると、CSSピクセルと実際の表示が1:1になり、これが基本的な指定値です。
initial-scaleを1未満にすると、ページを開いた瞬間に縮小された状態で表示されます。あえて全体を見渡せるようにしたい特殊なケース以外では避けたほうが無難で、値によっては再読み込みのたびに表示倍率が微妙にずれることもあるようです。
widthとinitial-scaleの組み合わせが噛み合っていない(たとえば固定幅の指定と極端な倍率を同時に指定する)場合も、想定外の拡大・縮小につながることがあるため、基本形から外れた指定をするときほど実機での確認を行いましょう。
避けたい組み合わせ例
width=device-width, initial-scale=0.5
画面幅とビューポートの幅がもともと同じなのに縮小表示を強制する形になり、ブラウザがビューポートの幅を勝手に広げてしまうwidth=320, initial-scale=0.5
320はほとんどの端末のdevice-widthより小さいため、上と同様の予期しない拡大が起きるwidth=240, initial-scale=1.0widthが画面幅より小さい場合、ブラウザは本来ズームアップして表示しようとするが、そこにinitial-scale=1.0を指定すると、今度は逆にビューポートの幅を広げて帳尻を合わせようとするwidth=1280, initial-scale=0.5
一見問題なさそうでも、機種やブラウザによってビューポートの値が書き換えられ、リロードのたびにwindow.innerWidthの値が不安定になることがある
「縮小・拡大の計算を自分で行って、その分widthを大きく(小さく)指定する」という指定で起きやすい問題です。width=device-width, initial-scale=1、または固定値を使う場合もその固定値とinitial-scale=1の組み合わせが安全です。
【minimum-scale・maximum-scale・user-scalable】
minimum-scale(最小ズーム倍率)、maximum-scale(最大ズーム倍率)、user-scalable(ピンチズームの可否、yes/no)は、ユーザーによる拡大縮小を制限するためのプロパティです。現在は非推奨となっています。
以前は、次のような理由でuser-scalable=noやmaximum-scale=1を指定するケースがよく見られました。
- 古いAndroid(2.x系)のCSS
position: fixedの不具合を回避するため - 古いiOS Safariで、画面回転時に意図せずズームされてしまう挙動を避けるため
- iOS Safariで、フォーム入力欄をタップした際に自動でズームされる挙動を避けるため
これらはいずれも当時のブラウザ側の不具合への対処であり、現在は解消済みか、font-sizeを16px以上にするといった別の方法で回避できます。一方で、ズームを禁止すること自体はアクセシビリティ上の問題であり、WCAG(Web Content Accessibility Guidelines)でも、最低200%・推奨500%まで拡大できることが求められています。MDNの公式ドキュメントでも、user-scalable=noの指定やズーム倍率を3倍未満に制限するmaximum-scaleの指定はアクセシビリティ上の問題があると明記されています。
実際のブラウザの挙動としても、iOS SafariやSamsung Internetはすでにこれらの制限指定を無視してユーザーのズーム操作を優先するようになっていますが、Android版のChromeやFirefoxでは指定どおりズームが制限されてしまう場合があります。つまり「どのみち一部のブラウザには無視されるから害はない」とは言えず、環境によっては実際にズームできなくなるユーザーが出てしまいます。minimum-scale・maximum-scale・user-scalableはいずれも指定せず、ズームはユーザーの自由に任せておくのが安全な選択です。
【viewport-fit】ノッチ・セーフエリアへの対応
viewport-fit は、iPhone Xシリーズ以降で採用されているノッチ(画面上部の切り欠き部分)や、画面端が丸まったディスプレイを持つ端末で、コンテンツをどこまで画面いっぱいに表示するかを指定するプロパティです。
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">値は3種類です。
auto(初期値):従来どおりの安全な領域内にレイアウトするcontain:セーフエリア(ノッチなどにコンテンツが隠れないよう確保された表示可能領域)の内側に収めるcover:画面全体を使い、ノッチの周辺までコンテンツを描画する
背景色や画像を画面いっぱいに表示させたい場合はcoverを指定しますが、その場合ボタンやテキストがノッチやホームインジケーターの下に隠れてしまうことがあります。
そのため、viewport-fit=coverとあわせて、CSS側でenv(safe-area-inset-top)などの値を使い、ノッチなどを避けた余白を確保するのが基本の組み合わせです。固定ヘッダー・固定フッターを画面いっぱいに配置したいときの定番の書き方です。
body {
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
}【interactive-widget】仮想キーボード表示時の挙動を指定する
interactive-widgetは比較的新しいプロパティで、チャット画面や検索窓のように、画面下部に固定した入力欄が仮想キーボードの表示でどう動くかを制御します。
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">値は3種類です。
resizes-visual(初期値):ビジュアルビューポート(実際に画面に見えている領域)だけが縮み、レイアウト自体は変わらないresizes-content:レイアウトビューポート(CSSのレイアウト計算に使われる領域)ごと縮む。100vhを使った要素なども、キーボード表示に合わせて縮小されるoverlays-content:ビューポートのサイズは変えず、キーボードがコンテンツの上に重なって表示される
チャットアプリのように、キーボード表示時に入力欄の位置をキーボードのすぐ上まで追従させたい場合はresizes-content、逆に背後のレイアウトを一切動かしたくない場合はoverlays-contentを選ぶ、という使い分けになります。
対応状況にはまだ差があり、Chrome・EdgeなどChromium系ブラウザは2022年ごろから対応済み、Firefoxもバージョン131(2024年)以降で対応していますが、iOSのSafari(WebKit)は2026年9月時点でも未対応です。指定しても対応していないブラウザでは単純に無視されるだけなので、指定すること自体に害はありませんが、Safariでも崩れないレイアウトを前提に設計しておく必要があります。
JavaScriptでviewportを動的に切り替える
ページを開いたあとに、contentの内容をJavaScriptで書き換えることもできます。「スマホでもPC版のレイアウトのまま見たい」というボタンを用意したい場合などに使われる方法です。
document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=1080, initial-scale=1');この方法自体は主要ブラウザで問題なく動作しますが、setAttributeで値を書き換えただけではメディアクエリの再評価やレイアウトの再計算がすぐに反映されないことがあります。確実に反映させたい場合は、既存のviewportメタタグを一度削除してから同じ内容で挿入し直します。
const viewport = document.querySelector('meta[name="viewport"]');
viewport.remove();
document.head.appendChild(viewport);このような「PC版・スマホ版で表示を丸ごと切り替える」やり方は、PC用とスマホ用で別々のデザインを用意するサイトでよく使われてきたものです。
レスポンシブデザインが主流になった今は出番自体は減っていますが、ECサイトなどで「PC版サイトを見る」といったリンクを用意したい場合には現在でも有効な手段です。
viewportを設定したのに表示が崩れる
width=device-width, initial-scale=1.0を指定したはずなのに、余白ができたり文字が小さいままだったりする場合に確認したいポイントを3つご紹介します。
- メタタグ自体の記述ミス
viewportのスペルミス、contentの値を囲むクォートの閉じ忘れ、<head>の外に書いてしまっている、といった単純な記述ミスがないか確認します。 - メディアクエリ側の記述ミス
@media (max-width: 768px)のような指定で、単位(px)の書き忘れや余分な半角スペースが入っていると、意図した条件でスタイルが適用されません。 - キャッシュが古いままになっている
ブラウザキャッシュやCDN、WordPressのキャッシュ系プラグインなどにより、修正前のHTMLがそのまま配信され続けているケースもあります。スーパーリロード(キャッシュを無視した再読み込み)や、キャッシュのクリアで解消するか確認します。
実際の見え方を確認するときは、Chromeの開発者ツールにあるデバイスツールバー(端末ごとの画面幅をシミュレートできる機能)や、Lighthouse(Chrome DevTools内、またはPageSpeed Insightsから実行できる診断ツール)のSEO項目にある「viewportタグの有無」のチェックを使うと確認しやすくなります。
目的別のcontent指定
通常のレスポンシブサイト
<meta name="viewport" content="width=device-width, initial-scale=1.0">ノッチ(切り欠き)のある端末で画面いっぱいに背景を敷きたい
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">あわせてCSS側でenv(safe-area-inset-*)の余白も確保します。
body {
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
}チャットのように入力欄が仮想キーボードに追従してほしい
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">避けたい指定
<meta name="viewport" content="user-scalable=no, maximum-scale=1">user-scalable=noや3未満のmaximum-scaleなど、ズームを制限するものは全般的に避けます。
カテゴリー : HTML-CSS