mcpField
S
safe-area-handling

safe-area-handling

github.com/cap-goデザイン・UI731.3k 回インストール2 週間前に更新
リポジトリ

safe-area-handling について

safe-area-handlingは、iOS、Android、ウィンドウ表示のCapacitorプロジェクトでシステムのセーフエリアを診断・処理する開発スキルです。バージョン差、CSSのインセット、ステータスバー移行、端末での検証を扱います。

主な機能

  1. 01ビューポートとCapacitor設定の確認
  2. 02iOSとAndroidのインセット処理
  3. 03横向きとウィンドウ表示への対応
  4. 04Androidステータスバー処理の移行
  5. 05端末での検証とトラブルシューティング

活用シーン

  1. 01ノッチやシステムバーに隠れる表示の修正
  2. 02セーフエリア値がゼロまたは不正な原因調査
  3. 03Capacitor更新後のレイアウト問題への対応

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/cap-go/capgo-skills --skill safe-area-handling

インストール後、Claude Code で/safe-area-handlingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.json
references/
android-edge-to-edge.md
css-patterns.md

詳細

Capacitor でのセーフエリア処理

使用するタイミング

次の場合に使用します:

  • コンテンツがステータスバー、ノッチ、Dynamic Island、カットアウト、ホームインジケーター、Android のナビゲーションバー/ジェスチャーバーの下に表示される
  • Android で env(safe-area-inset-*) が 0px になる、または回転後にしか正しくならない
  • Capacitor 8 へのアップグレード、または targetSdkVersion を 35/36 に引き上げた後にレイアウトが崩れた
  • Android で StatusBar.setOverlaysWebView / setBackgroundColor が動作しなくなった
  • 横向き/分割画面/iPad のウィンドウ表示レイアウトで左右のパディングが正しくない

使用しないケース:

  • キーボードが入力欄にかぶさる場合:capacitor-keyboard
  • Ionic の ion-header/ion-toolbar のテーマ設定:ionic-design、Konsta の safeAreas:konsta-ui
  • Tailwind のプラグイン/ユーティリティの設定:tailwind-capacitor
  • iOS のシーンサイズ、UIScreen.main、マルチウィンドウ:capacitor-ios-resizability
  • スプラッシュ画面の色:capacitor-splash-screen

すべての判断の基準となる事実

プラットフォーム/バージョン動作
iOS(すべて)WebView は全画面表示です。env(safe-area-inset-*) が機能するのは viewport-fit=cover の場合のみです。ios.contentInset のデフォルトは never です。
Android、Capacitor <= 7android.adjustMarginsForEdgeToEdge 設定でネイティブのマージンを追加できます。
Android、Capacitor 8 以降adjustMarginsForEdgeToEdge は削除されました。SystemBars コアプラグイン(@capacitor/core 内)がインセットを処理します。
Android 15(targetSdk 35)エッジツーエッジが強制されます。windowOptOutEdgeToEdgeEnforcement によるオプトアウトは引き続き可能です。
Android 16(targetSdk 36、Capacitor 8 のデフォルト。Capacitor 9 では 37)オプトアウトは無視されます。@capacitor/status-bar の overlaysWebView と backgroundColor は効果がありません。
Android WebView < 140env(safe-area-inset-*) の値が誤っている、または 0 になります(Chromium のバグ)。SystemBars は --safe-area-inset-* 変数を挿入します(デフォルトの insetsHandling: 'css')。

Android での SystemBars の動作(コアソースより):ページに viewport-fit=cover があり、かつ WebView がバージョン 140 以上の場合、インセットは env() に渡され、WebView はエッジツーエッジになります。それ以外の場合、Capacitor は WebView にネイティブのパディングを追加するため、コンテンツはバーの間に収まり、env() は 0 を返します。insetsHandling が css の場合、どちらのケースでも --safe-area-inset-* 変数に正しい値が設定されます。

ワークフロー

  1. 確認:index.html の viewport meta、capacitor.config.*(ios.contentInset、android.adjustMarginsForEdgeToEdge、plugins.SystemBars、plugins.StatusBar)、@capacitor/core と @capacitor/status-bar のバージョン、android/variables.gradle の targetSdkVersion、padding-top を設定するグローバル CSS、UI フレームワーク(Ionic/Konsta はそれぞれ独自にインセットを処理します)。
  2. デザインを決定:フルブリード(バーの下まで描画し、コンテンツにパディングを設定)にするか、コンテインド(コンテンツをバーの間に配置)にするかを決めます。モックアップが不明瞭な場合はユーザーに確認します。
  3. フルブリード(推奨):viewport-fit=cover と以下の CSS フォールバックチェーンを、コンポーネントごとではなくレイアウトシェルに一度だけ適用します。
  4. 古い設定を削除:android.adjustMarginsForEdgeToEdge を削除します。Android での StatusBar.setOverlaysWebView / setBackgroundColor の使用をやめます(必要なら iOS では維持します)。アイコンの色には SystemBars.setStyle を優先して使います。
  5. フレームワークを確認:Ionic はすでに --ion-safe-area-* を適用します。Konsta は safeAreas を使用します。パディングを二重に適用しないでください。
  6. デバイスで検証します(下記参照)。

関連するトピックがある場合にのみ、該当するリファレンスを読み込みます:

  • references/android-edge-to-edge.md - SystemBars の設定、ステータスバーの移行、ナビゲーションバーの色、WebView バージョンの確認
  • references/css-patterns.md - レイアウトシェル、タブバー、JS でのインセットの取得、デバッグオーバーレイ、サイズ変更可能なウィンドウ

基本設定

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
:root {
  --sat: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
  --sar: var(--safe-area-inset-right, env(safe-area-inset-right, 0px));
  --sab: var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
  --sal: var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
}
.app-header { padding-top: var(--sat); padding-inline: var(--sal) var(--sar); }
.app-tabbar { padding-bottom: var(--sab); padding-inline: var(--sal) var(--sar); }

--safe-area-inset-* という名前の変数は、Android では Capacitor の SystemBars によって挿入されます。iOS と Web では未定義のため、env() のフォールバックが適用されます。

import { SystemBars, SystemBarsStyle } from '@capacitor/core';
await SystemBars.setStyle({ style: SystemBarsStyle.Dark }); // light icons on dark background

SystemBarsStyle.Dark は暗い背景向けの明るいコンテンツ、Light は暗いコンテンツを意味します。iOS では UIViewControllerBasedStatusBarAppearance = YES が必要です(テンプレートのデフォルト)。

注意点

  • viewport-fit=cover がないことは、iOS で env() が 0 になる原因の第 1 位であり、Android で Capacitor が WebView にパディングを追加する原因にもなります。
  • ios.contentInset: 'always' と CSS のセーフエリアパディングを併用しないでください。インセットが二重に適用されます。
  • ステータスバーの高さを 20/44/47/59 px に固定しないでください。高さはデバイス、向き、ウィンドウサイズによって異なります。
  • インセットは実行中に変化します(回転、iPad のウィンドウ表示モード、折りたたみ式デバイス、Android の画面分割、キーボード)。CSS を使用するか、resize 時に再読み込みしてください。起動時に一度だけ取得してキャッシュしないでください。
  • iOS ではキーボード表示中も下部のインセットが残ります。キーボードの高さと max() を使用してください(capacitor-keyboard を参照)。
  • 100vh にはシステムバーの下の領域も含まれます。100dvh を使用するか、固定シェルを使ったフレックスレイアウトにしてください。
  • Ionic:ion-content と fullscreen、ion-header の組み合わせでは、すでにパディングが追加されています。body にパディングを追加すると、すべてが二重にずれます。

検証

  1. npx cap sync を実行し、次の環境でテストします。Dynamic Island 搭載の iPhone、iPhone SE クラス(ホームボタン、小さな上部インセット)、iPad(回転+Stage Manager/ウィンドウ表示)、ジェスチャーナビゲーションと 3 ボタンナビゲーションの両方を使用する Android 15/16 のデバイスまたはエミュレーター、横向き表示。
  2. Android WebView のバージョンを確認します:adb shell dumpsys package com.google.android.webview | grep versionName(140 未満では注入された変数に依存します)。
  3. Safari Web インスペクターまたは chrome://inspect で、次を実行します:
getComputedStyle(document.documentElement).getPropertyValue('--sat');
document.querySelector('meta[name=viewport]').content.includes('viewport-fit=cover');
  1. 要素がバーに重なっていないこと、ジェスチャーバーの上でタブバーをタップできること、パディングが二重になっていないことを確認します。
  2. 残存箇所を grep で検索します:
grep -rn "adjustMarginsForEdgeToEdge\|setOverlaysWebView\|setBackgroundColor\|windowOptOutEdgeToEdgeEnforcement" capacitor.config.* src android/app/src/main/res 2>/dev/null

エラー処理

症状/メッセージ原因対処法
iOS でヘッダーがステータスバーの下に入り込む、env() = 0viewport-fit=cover がないviewport meta に追加する
Android:コンテンツは正しいがエッジツーエッジにならず、env() = 0viewport-fit=cover がない、または WebView < 140 のため、Capacitor がネイティブでパディングを追加しているviewport-fit=cover を追加する。--safe-area-inset-* のフォールバックを使用する
Android:上部に二重の隙間があるinsetsHandling: 'disable' またはカスタム MainActivity のインセットコードによるネイティブパディングと CSS パディングの併用カスタムの setDecorFitsSystemWindows/インセットリスナーを削除し、SystemBars のデフォルト設定を維持する
Android で StatusBar.setBackgroundColor/setOverlaysWebView が効かないtargetSdk 35+/36 のエッジツーエッジCSS でバーの下に背景を描画し、アイコンの色には SystemBars.setStyle を使用する
Capacitor 8 へのアップグレード後も android.adjustMarginsForEdgeToEdge が設定に残り、マージンがなくなったオプションが削除され、無視されている削除し、SystemBars と CSS に任せる
Logcat に Unknown insetsHandling value '...'. Falling back to 'css'. と表示されるSystemBars.insetsHandling が無効現行ドキュメントに記載されている値(css、disable。新しいコアでは native も使用可能)を使う
ステータスバーの文字が見えない(白地に白)スタイルの不一致SystemBars.setStyle({ style: SystemBarsStyle.Light })
初回描画時にレイアウトが跳ねる(Android)ページのコミット後に SystemBars が viewport-fit を検出するplugins.SystemBars.initialViewportFitValueHint: 'cover' を設定する(コアの設定宣言を確認し、使用中のバージョンで利用可能か検証してください)

リソース

コメント