画面の下に固定したナビゲーションを作ったら、iPhone のホームバー(画面下の横棒)と重なってしまった。ノッチのある端末を横向きにしたら、テキストの左端が切れてしまった。スマートフォン向けのレイアウトでよくあるトラブルです。
こうした「表示はできるが、実際には使えない領域」を避けるために用意されているのが、CSS の env() 関数と safe-area-inset-* という環境変数です。端末ごとに違うノッチやホームバーの大きさを、ブラウザーが数値として教えてくれます。
この記事では、env() の書き方と4つの safe-area-inset、値を有効にするために欠かせない viewport-fit=cover の指定、そして実際のレイアウトへの組み込み方を解説します。
目次
env() はブラウザーが用意した環境変数を読む関数
env() は、ブラウザー側が持っている値を CSS から読み出すための関数です。書き方は var() とよく似ていますが、参照するのは自分で定義したカスタムプロパティではなく、ブラウザーがあらかじめ用意した環境変数です。
.footer-nav {
/* 下端のセーフエリアぶんだけ内側に余白を作る */
padding-bottom: env(safe-area-inset-bottom);
}
/* 第2引数はフォールバック値。環境変数が無いときに使われる */
.footer-nav {
padding-bottom: env(safe-area-inset-bottom, 0px);
}
最もよく使われるのが、セーフエリア(安全領域)を表す4つの変数です。セーフエリアとは、ノッチ・角丸・ホームバーなどに隠されない、コンテンツを置いても安全な範囲を指します。
| 環境変数 | 意味 |
|---|---|
safe-area-inset-top | 上端の安全でない領域の高さ(ノッチやステータスバー) |
safe-area-inset-right | 右端の安全でない領域の幅(横向き時のノッチなど) |
safe-area-inset-bottom | 下端の安全でない領域の高さ(ホームバー) |
safe-area-inset-left | 左端の安全でない領域の幅 |
値はブラウザーが端末と画面の向きに応じて決めます。ノッチのない端末では 0px、iPhone を縦向きで表示している場合は下端に 34px 程度、といった具合です。開発者が端末ごとに条件分岐を書く必要はありません。
viewport-fit=cover を指定しないと値は 0 のまま
ここが最初の関門です。env(safe-area-inset-*) を書いても効かない場合、ほとんどはこの指定が抜けています。
既定では、ブラウザーはノッチなどを避けた範囲にページを表示します。この状態では避ける必要がないので、セーフエリアの値はすべて 0 です。画面いっぱいまでページを広げる viewport-fit=cover を指定して初めて、「はみ出した部分の大きさ」として値が入ります。
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
つまり viewport-fit=cover と env(safe-area-inset-*) はセットです。前者で画面の端まで背景を伸ばし、後者でコンテンツだけを内側に寄せる、という役割分担になります。背景は端まで塗りたいが、文字やボタンは隠れてほしくない、という要求をこの2つで実現します。
固定フッターを安全な位置に置く
典型的な使い方が、画面下部に固定したナビゲーションです。単に padding-bottom: 12px と書くと、ホームバーのある端末ではボタンが重なって押しにくくなります。
そこで、もともと欲しい余白にセーフエリアの値を足します。足し算には calc() を使います。
.footer-nav {
position: fixed;
inset-inline: 0;
bottom: 0;
background-color: #fff;
border-top: 1px solid #ddd;
/* 上は通常どおり、下は 12px + セーフエリア */
padding-top: 12px;
padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}
背景色と border-top は要素全体に効くので、画面の一番下まで白く塗られます。そのうえで中身だけがホームバーの上に来るため、見た目は自然なまま操作性が確保できます。
固定フッターを置いたときは、本文の最後がフッターに隠れないよう、ページ側にも同じ高さの余白を確保しておきます。
:root {
/* フッターの高さをまとめて管理する */
--footer-height: 56px;
}
.page-content {
padding-bottom: calc(var(--footer-height) + env(safe-area-inset-bottom, 0px));
}
カスタムプロパティと env() は組み合わせられます。ただし逆に、--x: env(safe-area-inset-bottom) のようにカスタムプロパティへ入れてから使う場合は、そのまま値として扱われるだけなので計算には calc() が必要な点は変わりません。
横向きと左右のセーフエリア
端末を横向きにすると、ノッチは画面の左右どちらかに来ます。このとき safe-area-inset-left または safe-area-inset-right に値が入り、もう片方は 0 になります。どちら側に来るかは持ち方次第なので、左右の両方に指定しておくのが基本です。
.container {
/* 最低 16px は確保しつつ、ノッチ側はその分だけ広げる */
padding-left: max(16px, env(safe-area-inset-left));
padding-right: max(16px, env(safe-area-inset-right));
}
ここでは calc() ではなく max() を使っています。左右の余白は「最低でも 16px、ノッチがあるならその分」という考え方が自然だからです。calc(16px + env(...)) だと、ノッチのある側だけ余白が広くなりすぎて左右非対称に見えることがあります。
上端についても同様で、固定ヘッダーがある場合は padding-top: calc(12px + env(safe-area-inset-top, 0px)) のように書きます。ステータスバーの下に文字が潜り込む問題は、これで解消します。
safe-area 以外の環境変数
env() で読めるのはセーフエリアだけではありません。仕様には次のような環境変数も定義されています。ただし対応状況はまちまちなので、使うときは必ずフォールバック値を添えてください。
| 環境変数 | 意味 |
|---|---|
safe-area-inset-* | ノッチやホームバーを避けた安全領域(対応が広い) |
titlebar-area-x / -y / -width / -height | タイトルバーをカスタマイズした PWA で、操作可能な領域の位置とサイズ |
keyboard-inset-top ほか | 画面に表示されたソフトウェアキーボードが占める領域 |
実務でまず使うのは safe-area-inset-* です。他のものは PWA など用途が限られるので、必要になったときに調べれば十分です。
指定したのに余白が付かないとき
viewport-fit=cover を書いていない
最も多い原因です。前述のとおり、この指定がないとセーフエリアの値はすべて 0 になります。<meta name="viewport"> の content に viewport-fit=cover が入っているか確認してください。WordPress のテーマなどでは、wp_head 内で出力している viewport タグを書き換える必要があります。
そもそも値が 0 の端末で確認している
PC のブラウザーや、ノッチのない端末では safe-area-inset-* は 0 です。指定が効いていないのではなく、避けるべき領域が無いだけです。実機がない場合は、開発者ツールのデバイスエミュレーションで iPhone を選ぶと、セーフエリアを含む表示を確認できます。
動作確認のときは、一時的に padding-bottom: calc(12px + env(safe-area-inset-bottom, 40px)) のようにフォールバック値を大きくしてみると、指定そのものが効いているかを切り分けられます。
constant() という書き方を見かけた
古い記事に constant(safe-area-inset-bottom) という書き方が出てくることがあります。これは iOS 11 の初期に使われていた旧仕様で、現在は env() に置き換えられています。いま新しく書くなら env() だけで問題ありません。
計算せずに書いてしまっている
padding-bottom: 12px + env(safe-area-inset-bottom); は無効です。CSS で足し算をするには calc() が必要で、これを忘れると宣言ごと無視されます。エラーにはならず静かに効かなくなるため、気づきにくい書き間違いです。
まとめ
env() は、ブラウザーが用意した環境変数を CSS から読み出す関数です。なかでも safe-area-inset-top / -right / -bottom / -left は、ノッチやホームバーに隠れない安全領域の大きさを教えてくれます。
使うときの前提は、viewport の meta タグに viewport-fit=cover を入れることです。これがないと値は常に 0 で、指定しても何も起きません。そのうえで padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)) のように、本来欲しい余白へ足し込むのが基本形になります。
左右のように「最低限の余白は確保したい」箇所では max() と組み合わせると自然な見た目になります。画面下に固定するナビゲーションやボタンを作るときは、忘れずに指定しておきましょう。