{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/maui-safe-area",
  "version": "1.0.1",
  "name": "maui-safe-area",
  "description": ".NET MAUI safe area and edge-to-edge layout guidance for .NET 10+. Covers the new SafeAreaEdges property, SafeAreaRegions enum, per-edge control, keyboard avoidance, Blazor Hybrid CSS safe areas, migration from legacy iOS-only APIs, and platform-specific behavior for Android, iOS, and Mac Catalyst. USE FOR: \"safe area\", \"edge-to-edge\", \"SafeAreaEdges\", \"SafeAreaRegions\", \"keyboard avoidance\", \"notch insets\", \"status bar overlap\", \"iOS safe area\", \"Android edge-to-edge\", \"content behind status bar\", \"UseSafeArea migration\", \"soft input keyboard\", \"IgnoreSafeArea replacement\". DO NOT USE FOR: general layout or grid design (use Grid and StackLayout), app lifecycle handling (use maui-app-lifecycle), theming or styling (use maui-theming), or Shell navigation structure.",
  "system_prompt_fragment": "# Safe Area & Edge-to-Edge Layout (.NET 10+)\n\n.NET 10 introduces a **brand-new, cross-platform safe area API** that replaces the legacy iOS-only `UseSafeArea` and the layout-level `IgnoreSafeArea` properties. The new `SafeAreaEdges` property and `SafeAreaRegions` flags enum give you per-edge, per-control safe area management on Android, iOS, and Mac Catalyst from a single API surface.\n\n> **This is new API surface in .NET 10.** If the project targets .NET 9 or earlier, these APIs do not exist. Guide the developer to the legacy `ios:Page.UseSafeArea` and `Layout.IgnoreSafeArea` properties instead.\n\n## When to Use\n\n- Content overlaps status bar, notch, Dynamic Island, or home indicator after upgrading to .NET 10\n- Implementing edge-to-edge / immersive layouts (photo viewers, video players, maps)\n- Keyboard avoidance for chat or form UIs\n- Migrating from `ios:Page.UseSafeArea`, `Layout.IgnoreSafeArea`, or `WindowSoftInputModeAdjust.Resize`\n- Blazor Hybrid apps that need CSS `env(safe-area-inset-*)` coordination\n- Mixed layouts with an edge-to-edge header but a safe-area-respecting body\n\n## When Not to Use\n\n- Projects targeting .NET 9 or earlier — use the legacy iOS-specific APIs\n- General page layout questions unrelated to system bars or keyboard — use standard layout guidance\n- App lifecycle or navigation structure — use maui-app-lifecycle or Shell guidance\n- Theming or visual styling — use the **maui-theming** skill\n\n## Inputs\n\n- Target framework: must be `net10.0-*` or later for the new APIs\n- Target platforms: Android, iOS, Mac Catalyst (Windows does not have system bar insets)\n- UI approach: XAML/C#, Blazor Hybrid, or MauiReactor\n\n## SafeAreaRegions Enum\n\n```csharp\n[Flags]\npublic enum SafeAreaRegions\n{\n    None      = 0,       // Edge-to-edge — no safe area padding\n    SoftInput = 1 << 0,  // Pad to avoid the on-screen keyboard\n    Container = 1 << 1,  // Stay inside status bar, notch, home indicator\n    Default   = -1,      // Use the platform default for the control type\n    All       = 1 << 15  // Respect all safe area insets (most restrictive)\n}\n```\n\n`SoftInput` and `Container` are combinable flags:\n`SafeAreaRegions.Container | SafeAreaRegions.SoftInput` = respect system bars **and** keyboard.\n\n## SafeAreaEdges Struct\n\n```csharp\npublic readonly struct SafeAreaEdges\n{\n    public SafeAreaRegions Left { get; }\n    public SafeAreaRegions Top { get; }\n    public SafeAreaRegions Right { get; }\n    public SafeAreaRegions Bottom { get; }\n\n    // Uniform — same value for all four edges\n    public SafeAreaEdges(SafeAreaRegions uniformValue)\n\n    // Horizontal / Vertical\n    public SafeAreaEdges(SafeAreaRegions horizontal, SafeAreaRegions vertical)\n\n    // Per-edge\n    public SafeAreaEdges(SafeAreaRegions left, SafeAreaRegions top,\n                         SafeAreaRegions right, SafeAreaRegions bottom)\n}\n```\n\nStatic presets: `SafeAreaEdges.None`, `SafeAreaEdges.All`, `SafeAreaEdges.Default`.\n\n### XAML Type Converter\n\nFollows Thickness-like comma-separated syntax:\n\n```xaml\n<!-- Uniform -->\nSafeAreaEdges=\"Container\"\n\n<!-- Horizontal, Vertical -->\nSafeAreaEdges=\"Container, SoftInput\"\n\n<!-- Left, Top, Right, Bottom -->\nSafeAreaEdges=\"Container, Container, Container, SoftInput\"\n```\n\n## Control Defaults\n\n| Control | Default | Notes |\n|---------|---------|-------|\n| `ContentPage` | `None` | Edge-to-edge. **Breaking change from .NET 9 on Android.** |\n| `Layout` (Grid, StackLayout, etc.) | `Container` | Respects bars/notch, flows under keyboard |\n| `ScrollView` | `Default` | iOS maps to automatic content insets. Only `Container` and `None` take effect. |\n| `ContentView` | `None` | Inherits parent behavior |\n| `Border` | `None` | Inherits parent behavior |\n\n## Breaking Changes from .NET 9\n\n### ContentPage default changed to `None`\n\nIn .NET 9, Android `ContentPage` behaved like `Container`. In .NET 10, the default is `None` on **all platforms**. If your Android content goes behind the status bar after upgrading:\n\n```xaml\n<!-- .NET 10 default — content extends under status bar -->\n<ContentPage>\n\n<!-- Restore .NET 9 Android behavior -->\n<ContentPage SafeAreaEdges=\"Container\">\n```\n\n### WindowSoftInputModeAdjust.Resize removed\n\nIf you used `WindowSoftInputModeAdjust.Resize` in .NET 9, replace it with `SafeAreaEdges=\"All\"` on the ContentPage for equivalent keyboard avoidance.\n\n## Usage Patterns\n\n### Edge-to-edge immersive content\n\nSet `None` on **both** page and layout — layouts default to `Container`:\n\n```xaml\n<ContentPage SafeAreaEdges=\"None\">\n    <Grid SafeAreaEdges=\"None\">\n        <Image Source=\"background.jpg\" Aspect=\"AspectFill\" />\n        <VerticalStackLayout Padding=\"20\" VerticalOptions=\"End\">\n            <Label Text=\"Overlay text\" TextColor=\"White\" FontSize=\"24\" />\n        </VerticalStackLayout>\n    </Grid>\n</ContentPage>\n```\n\n### Forms and critical content\n\n```xaml\n<ContentPage SafeAreaEdges=\"All\">\n    <VerticalStackLayout Padding=\"20\">\n        <Label Text=\"Safe content\" FontSize=\"18\" />\n        <Entry Placeholder=\"Enter text\" />\n        <Button Text=\"Submit\" />\n    </VerticalStackLayout>\n</ContentPage>\n```\n\n### Keyboard-aware chat layout\n\n```xaml\n<ContentPage>\n    <Grid RowDefinitions=\"*,Auto\"\n          SafeAreaEdges=\"Container, Container, Container, SoftInput\">\n        <ScrollView Grid.Row=\"0\">\n            <VerticalStackLayout Padding=\"20\" Spacing=\"10\">\n                <Label Text=\"Messages\" FontSize=\"24\" />\n            </VerticalStackLayout>\n        </ScrollView>\n        <Border Grid.Row=\"1\" BackgroundColor=\"LightGray\" Padding=\"20\">\n            <Grid ColumnDefinitions=\"*,Auto\" Spacing=\"10\">\n                <Entry Placeholder=\"Type a message...\" />\n                <Button Grid.Column=\"1\" Text=\"Send\" />\n            </Grid>\n        </Border>\n    </Grid>\n</ContentPage>\n```\n\n### Mixed: edge-to-edge header + safe body + keyboard footer\n\n```xaml\n<ContentPage SafeAreaEdges=\"None\">\n    <Grid RowDefinitions=\"Auto,*,Auto\">\n        <Grid BackgroundColor=\"{StaticResource Primary}\">\n            <Label Text=\"App Header\" TextColor=\"White\" Margin=\"20,40,20,20\" />\n        </Grid>\n        <ScrollView Grid.Row=\"1\" SafeAreaEdges=\"Container\">\n            <!-- Use Container, not All — ScrollView only honors Container and None -->\n            <VerticalStackLayout Padding=\"20\">\n                <Label Text=\"Main content\" />\n            </VerticalStackLayout>\n        </ScrollView>\n        <Grid Grid.Row=\"2\" SafeAreaEdges=\"SoftInput\"\n              BackgroundColor=\"LightGray\" Padding=\"20\">\n            <Entry Placeholder=\"Type a message...\" />\n        </Grid>\n    </Grid>\n</ContentPage>\n```\n\n### Programmatic (C#)\n\n```csharp\nvar page = new ContentPage\n{\n    SafeAreaEdges = SafeAreaEdges.All\n};\n\nvar grid = new Grid\n{\n    SafeAreaEdges = new SafeAreaEdges(\n        left: SafeAreaRegions.Container,\n        top: SafeAreaRegions.Container,\n        right: SafeAreaRegions.Container,\n        bottom: SafeAreaRegions.SoftInput)\n};\n```\n\n## Decision Framework\n\n| Scenario | SafeAreaEdges value |\n|----------|---------------------|\n| Forms, critical inputs | `All` |\n| Photo viewer, video player, game | `None` (on page **and** layout) |\n| Scrollable content with fixed header/footer | `Container` |\n| Chat/messaging with bottom input bar | Per-edge: `Container, Container, Container, SoftInput` |\n| Blazor Hybrid app | `None` on page; CSS `env()` for insets |\n\n## Blazor Hybrid Integration\n\nFor Blazor Hybrid apps, let CSS handle safe areas to avoid double-padding.\n\n1. **Page stays edge-to-edge** (default in .NET 10):\n\n```xaml\n<ContentPage SafeAreaEdges=\"None\">\n    <BlazorWebView HostPage=\"wwwroot/index.html\">\n        <BlazorWebView.RootComponents>\n            <RootComponent Selector=\"#app\" ComponentType=\"{x:Type local:Routes}\" />\n        </BlazorWebView.RootComponents>\n    </BlazorWebView>\n</ContentPage>\n```\n\n2. **Add `viewport-fit=cover`** in `index.html`:\n\n```html\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0,\n      maximum-scale=1.0, user-scalable=no, viewport-fit=cover\" />\n```\n\n3. **Use CSS `env()` functions**:\n\n```css\nbody {\n    padding-top: env(safe-area-inset-top);\n    padding-bottom: env(safe-area-inset-bottom);\n    padding-left: env(safe-area-inset-left);\n    padding-right: env(safe-area-inset-right);\n}\n```\n\nAvailable CSS environment variables: `env(safe-area-inset-top)`, `env(safe-area-inset-bottom)`, `env(safe-area-inset-left)`, `env(safe-area-inset-right)`.\n\n## Migration from Legacy APIs\n\n| Legacy (.NET 9 and earlier) | New (.NET 10+) |\n|-----------------------------|----------------|\n| `ios:Page.UseSafeArea=\"True\"` | `SafeAreaEdges=\"Container\"` |\n| `Layout.IgnoreSafeArea=\"True\"` | `SafeAreaEdges=\"None\"` |\n| `WindowSoftInputModeAdjust.Resize` | `SafeAreaEdges=\"All\"` on ContentPage |\n\nThe legacy properties still compile but are marked obsolete. `IgnoreSafeArea=\"True\"` maps internally to `SafeAreaRegions.None`.\n\n```xaml\n<!-- .NET 9 (legacy, iOS-only) -->\n<ContentPage xmlns:ios=\"clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls\"\n             ios:Page.UseSafeArea=\"True\">\n\n<!-- .NET 10+ (cross-platform) -->\n<ContentPage SafeAreaEdges=\"Container\">\n```\n\n## Platform-Specific Behavior\n\n### iOS & Mac Catalyst\n\n- Safe area insets cover: status bar, navigation bar, tab bar, notch/Dynamic Island, home indicator\n- `SoftInput` includes the keyboard when visible\n- Insets update automatically on rotation and UI visibility changes\n- `ScrollView` with `Default` maps to `UIScrollViewContentInsetAdjustmentBehavior.Automatic`\n\nTransparent navigation bar for content behind the nav bar:\n\n```xaml\n<Shell Shell.BackgroundColor=\"#80000000\" Shell.NavBarHasShadow=\"False\" />\n```\n\n### Android\n\n- Safe area insets cover: system bars (status/navigation) and display cutouts\n- `SoftInput` includes the soft keyboard\n- MAUI uses `WindowInsetsCompat` and `WindowInsetsAnimationCompat` internally\n- Behavior varies by Android version and OEM edge-to-edge settings\n\n## Common Pitfalls\n\n1. **Forgetting to set `None` on the layout too.** `ContentPage SafeAreaEdges=\"None\"` makes the page edge-to-edge, but child layouts default to `Container` and still pad inward. Set `None` on both page and layout for truly immersive content.\n\n2. **Using `SoftInput` directly on ScrollView.** ScrollView manages its own content insets and ignores `SoftInput`. Wrap the ScrollView in a Grid or StackLayout and apply `SoftInput` there.\n\n3. **Confusing `Default` with `None`.** `Default` means \"platform default for this control type\" — on ScrollView (iOS) this enables automatic content insets. `None` means \"no safe area padding at all.\"\n\n4. **Double-padding in Blazor Hybrid.** Setting `SafeAreaEdges=\"Container\"` on the page **and** using CSS `env(safe-area-inset-*)` results in doubled insets. Pick one approach — CSS is recommended for Blazor.\n\n5. **Missing `viewport-fit=cover` in Blazor.** Without this meta tag, CSS `env(safe-area-inset-*)` values are always zero on iOS.\n\n6. **Assuming .NET 9 behavior on Android.** After upgrading to .NET 10, Android `ContentPage` defaults to `None` (was effectively `Container`). Add `SafeAreaEdges=\"Container\"` to restore the previous behavior.\n\n7. **Using legacy `ios:Page.UseSafeArea` in new code.** The old API is iOS-only and obsolete. Always use `SafeAreaEdges` for cross-platform safe area management.\n\n## Checklist\n\n- [ ] Android upgrade: `SafeAreaEdges=\"Container\"` added if content goes under status bar\n- [ ] Edge-to-edge: `None` set on **both** page and layout\n- [ ] ScrollView keyboard avoidance uses wrapper Grid, not ScrollView's own `SafeAreaEdges`\n- [ ] Blazor Hybrid: using either XAML or CSS safe areas, not both\n- [ ] `viewport-fit=cover` in Blazor's `index.html` `<meta viewport>` tag\n- [ ] Legacy `UseSafeArea` / `IgnoreSafeArea` migrated to `SafeAreaEdges`",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/maui-safe-area"
  ],
  "tags": [
    "dotnet-maui",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-maui/skills/maui-safe-area/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-maui/skills/maui-safe-area/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py."
  }
}