safe-area-handling
Skill75 starsChanged 33 days agoMITView on GitHub
Fixes notch, Dynamic Island, home indicator, display cutout, and system bar overlap in Capacitor apps. Covers `viewport-fit=cover` + `env(safe-area-inset-*)`, the Capacitor 8 SystemBars core plugin (`insetsHandling`, injected `--safe-area-inset-*` CSS variables for Android WebView < 140, removal of `android.adjustMarginsForEdgeToEdge`), Android 15/16 enforced edge-to-edge (`overlaysWebView` and `backgroundColor` of @capacitor/status-bar no longer work at targetSdk 36), iOS `contentInset`, and insets that change at runtime on rotation, iPad/foldable resizable windows. Use when headers sit under the status bar, tab bars hide behind the gesture bar, `env()` returns 0 on Android, or layout broke after upgrading to Capacitor 8 / targetSdk 35+. Do not use for keyboard overlap (capacitor-keyboard), Ionic/Konsta component theming (ionic-design, konsta-ui), Tailwind utility setup (tailwind-capacitor), or iOS multi-window/resizability adoption (capacitor-ios-resizability).
1 other project uses a version of this.
Adopted by
Most recently taken up by
- Aver005/agent-skillstheir own version, first seen Feb 14, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- Aver005/agent-skills★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository56 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- capacitor-accessibilitySkill
- capacitor-app-intentsSkill
- capacitor-apple-review-preflightSkill
- capacitor-app-storeSkill
- capacitor-app-upgradesSkill
- capacitor-app-upgrade-v4-to-v5Skill
- capacitor-app-upgrade-v5-to-v6Skill
- capacitor-app-upgrade-v6-to-v7Skill
- capacitor-app-upgrade-v7-to-v8Skill
- capacitor-app-upgrade-v8-to-v9Skill
- capacitor-best-practicesSkill
- capacitor-ci-cdSkill
- capacitor-deep-linkingSkill
- capacitor-ios-resizabilitySkill
- capacitor-ios-security-hardeningSkill
- capacitor-keyboardSkill
- capacitor-mcpSkill
- capacitor-offline-firstSkill
- capacitor-performanceSkill
- capacitor-plugin-spm-supportSkill
- capacitor-pluginsSkill
- capacitor-plugin-upgradesSkill
- capacitor-plugin-upgrade-v4-to-v5Skill
- capacitor-plugin-upgrade-v5-to-v6Skill
- capacitor-plugin-upgrade-v6-to-v7Skill
- capacitor-plugin-upgrade-v7-to-v8Skill
- capacitor-plugin-upgrade-v8-to-v9Skill
- capacitor-push-notificationsSkill
- capacitor-securitySkill
- capacitor-splash-screenSkill
- capacitor-testingSkill
- capacitor-uiscene-migrationSkill
- capawesome-live-update-migrationSkill
- capgo-cli-usageSkill
- capgo-cloudSkill
- capgo-live-updatesSkill
- capgo-native-buildsSkill
- capgo-organization-managementSkill
- capgo-release-managementSkill
- capgo-release-workflowsSkill
- cocoapods-to-spmSkill
- cordova-to-capacitorSkill
- debugging-capacitorSkill
- framework-to-capacitorSkill
- ionic-appflow-migrationSkill
- ionic-designSkill
- ionic-enterprise-sdk-migrationSkill
- ios-android-logsSkill
- konsta-uiSkill
- skill-creatorSkill
- sqlite-to-fast-sqlSkill
- subscription-app-revenueSkill
- tailwind-capacitorSkill
- webapp-to-capacitorSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 42
Remove legacy config: delete android.adjustMarginsForEdgeToEdge.
- Distinctive: line 46
Only load a reference when its topic is in play:
- Very specific: line 79
Do not combine ios.contentInset: 'always' with CSS safe-area padding: insets apply twice.
- Very specific: line 80
Do not hardcode 20/44/47/59 px status bar heights.
- Very specific: line 89
Check Android WebView version: adb shell dumpsys package com.google.android.webview | grep versionName (< 140 relies on injected variables).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Safe Area Handling in Capacitor
When to Use
TRIGGER when:
- Content renders under the status bar, notch, Dynamic Island, cutout, home indicator, or Android navigation/gesture bar
env(safe-area-inset-*)is0pxon Android, or correct only after rotation- Layout broke after Capacitor 8 upgrade or raising
targetSdkVersionto 35/36 StatusBar.setOverlaysWebView/setBackgroundColorstopped working on Android- Landscape / split-screen / iPad windowed layouts have wrong side padding
Do not use:
- Keyboard covering inputs:
capacitor-keyboard - Ionic
ion-header/ion-toolbartheming:ionic-design; KonstasafeAreas:konsta-ui - Tailwind plugin/utility setup:
tailwind-capacitor - iOS scene sizing,
UIScreen.main, multi-window:capacitor-ios-resizability - Splash screen colors:
capacitor-splash-screen
Facts that drive every decision
| Platform / version | Behavior |
| iOS (all) | WebView is full-screen. env(safe-area-inset-*) works only with viewport-fit=cover. ios.contentInset defaults to never. |
| Android, Capacitor <= 7 | android.adjustMarginsForEdgeToEdge config could add native margins. |
| Android, Capacitor 8+ | adjustMarginsForEdgeToEdge removed. SystemBars core plugin (in @capacitor/core) handles insets. |
| Android 15 (targetSdk 35) | Edge-to-edge enforced; opt-out via windowOptOutEdgeToEdgeEnforcement still possible. |
| Android 16 (targetSdk 36, Capacitor 8 default; 37 on Capacitor 9) | Opt-out ignored. @capacitor/status-bar overlaysWebView and backgroundColor have no effect. |
| Android WebView < 140 | env(safe-area-inset-*) wrong/0 (Chromium bug). SystemBars injects --safe-area-inset-* variables (default insetsHandling: 'css'). |
How SystemBars behaves on Android (from core source): if the page has viewport-fit=cover and WebView >= 140, insets pass through to env() and the WebView is edge-to-edge. Otherwise Capacitor pads the WebView natively so content sits between the bars and env() reports 0. --safe-area-inset-* variables carry correct values in both cases when insetsHandling is css.
Workflow
- Inspect:
index.htmlviewport meta,capacitor.config.*(ios.contentInset,android.adjustMarginsForEdgeToEdge,plugins.SystemBars,plugins.StatusBar),@capacitor/core+@capacitor/status-barversions,android/variables.gradletargetSdkVersion, global CSS that setspadding-top, UI framework (Ionic / Konsta handle insets themselves). - Decide the design: full-bleed (draw under bars, pad content) vs. contained (content between bars). Ask the user if mockups are unclear.
- Full-bleed (recommended):
viewport-fit=cover+ the CSS fallback chain below, applied once at the layout shell, not per component. - Remove legacy config: delete
android.adjustMarginsForEdgeToEdge; drop Android uses ofStatusBar.setOverlaysWebView/setBackgroundColor(keep for iOS if wanted); preferSystemBars.setStylefor icon color. - Framework check: Ionic already applies
--ion-safe-area-*; Konsta usessafeAreas. Do not double-pad. - Verify on devices (below).
Only load a reference when its topic is in play:
- references/android-edge-to-edge.md - SystemBars config, status-bar migration, nav bar color, WebView version checks
- references/css-patterns.md - layout shell, tab bars, reading insets in JS, debug overlay, resizable windows
Baseline
<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); }The --safe-area-inset-* names are injected by Capacitor's SystemBars on Android; on iOS and web they are undefined, so the env() fallback applies.
import { SystemBars, SystemBarsStyle } from '@capacitor/core';
await SystemBars.setStyle({ style: SystemBarsStyle.Dark }); // light icons on dark backgroundSystemBarsStyle.Dark = light content for dark backgrounds; Light = dark content. iOS needs UIViewControllerBasedStatusBarAppearance = YES (template default).
Traps
- Missing
viewport-fit=coveris the #1 cause ofenv()= 0 on iOS and of Capacitor padding the WebView on Android. - Do not combine
ios.contentInset: 'always'with CSS safe-area padding: insets apply twice. - Do not hardcode 20/44/47/59 px status bar heights; heights differ per device, orientation, and window size.
- Insets change at runtime (rotation, iPad windowed mode, foldables, Android split screen, keyboard). Use CSS, or re-read on
resize; never cache once at startup. - On iOS the bottom inset stays while the keyboard is visible; use
max()with keyboard height (seecapacitor-keyboard). 100vhincludes the area under system bars; use100dvhor flex layout with a fixed shell.- Ionic:
ion-contentwithfullscreen+ion-headeralready pads; adding body padding shifts everything twice.
Verification
npx cap sync, run on: iPhone with Dynamic Island, iPhone SE-class (home button, small top inset), iPad (rotate + Stage Manager/windowed), Android 15/16 device or emulator in gesture and 3-button navigation, landscape.- Check Android WebView version:
adb shell dumpsys package com.google.android.webview | grep versionName(< 140 relies on injected variables). - In Safari Web Inspector /
chrome://inspect, run:
getComputedStyle(document.documentElement).getPropertyValue('--sat');
document.querySelector('meta[name=viewport]').content.includes('viewport-fit=cover');- Confirm no element overlaps bars; tab bar taps work above the gesture bar; no double padding.
- Grep for leftovers:
grep -rn "adjustMarginsForEdgeToEdge\|setOverlaysWebView\|setBackgroundColor\|windowOptOutEdgeToEdgeEnforcement" capacitor.config.* src android/app/src/main/res 2>/dev/nullError Handling
| Symptom / message | Cause | Fix |
Header under status bar on iOS, env() = 0 | No viewport-fit=cover | Add to viewport meta |
Android: content correct but no edge-to-edge, env() = 0 | No viewport-fit=cover or WebView < 140, so Capacitor pads natively | Add viewport-fit=cover; use --safe-area-inset-* fallback |
| Android: double gap at top | Native padding + your CSS padding with insetsHandling: 'disable' or custom MainActivity insets code | Remove custom setDecorFitsSystemWindows / inset listeners; keep SystemBars default |
StatusBar.setBackgroundColor / setOverlaysWebView no effect on Android | targetSdk 35+/36 edge-to-edge | Draw a background under the bar in CSS; use SystemBars.setStyle for icon color |
android.adjustMarginsForEdgeToEdge still in config after Capacitor 8 upgrade, margins gone | Option removed and ignored | Delete it; rely on SystemBars + CSS |
Logcat Unknown insetsHandling value '...'. Falling back to 'css'. | Invalid SystemBars.insetsHandling | Use a value listed in the current docs (css, disable; newer cores also accept native) |
| Status bar text invisible (white on white) | Style mismatch | SystemBars.setStyle({ style: SystemBarsStyle.Light }) |
| Layout jumps on first paint (Android) | SystemBars detects viewport-fit after page commit | Set plugins.SystemBars.initialViewportFitValueHint: 'cover' (in core config declarations; verify in your version) |
Resources
- System Bars API: https://capacitorjs.com/docs/apis/system-bars
- Status Bar API (Android 16 note): https://capacitorjs.com/docs/apis/status-bar
- Updating to 8.0: https://capacitorjs.com/docs/updating/8-0
- Android edge-to-edge: https://developer.android.com/develop/ui/views/layout/edge-to-edge
- WebKit viewport-fit / env(): https://webkit.org/blog/7929/designing-websites-for-iphone-x/
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

