$npx -y skills add dotnet/skills --skill maui-safe-area.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
| 1 | # Safe Area & Edge-to-Edge Layout (.NET 10+) |
| 2 | |
| 3 | .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. |
| 4 | |
| 5 | > **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. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Content overlaps status bar, notch, Dynamic Island, or home indicator after upgrading to .NET 10 |
| 10 | - Implementing edge-to-edge / immersive layouts (photo viewers, video players, maps) |
| 11 | - Keyboard avoidance for chat or form UIs |
| 12 | - Migrating from `ios:Page.UseSafeArea`, `Layout.IgnoreSafeArea`, or `WindowSoftInputModeAdjust.Resize` |
| 13 | - Blazor Hybrid apps that need CSS `env(safe-area-inset-*)` coordination |
| 14 | - Mixed layouts with an edge-to-edge header but a safe-area-respecting body |
| 15 | |
| 16 | ## When Not to Use |
| 17 | |
| 18 | - Projects targeting .NET 9 or earlier — use the legacy iOS-specific APIs |
| 19 | - General page layout questions unrelated to system bars or keyboard — use standard layout guidance |
| 20 | - App lifecycle or navigation structure — use maui-app-lifecycle or Shell guidance |
| 21 | - Theming or visual styling — use the **maui-theming** skill |
| 22 | |
| 23 | ## Inputs |
| 24 | |
| 25 | - Target framework: must be `net10.0-*` or later for the new APIs |
| 26 | - Target platforms: Android, iOS, Mac Catalyst (Windows does not have system bar insets) |
| 27 | - UI approach: XAML/C#, Blazor Hybrid, or MauiReactor |
| 28 | |
| 29 | ## SafeAreaRegions Enum |
| 30 | |
| 31 | ```csharp |
| 32 | [Flags] |
| 33 | public enum SafeAreaRegions |
| 34 | { |
| 35 | None = 0, // Edge-to-edge — no safe area padding |
| 36 | SoftInput = 1 << 0, // Pad to avoid the on-screen keyboard |
| 37 | Container = 1 << 1, // Stay inside status bar, notch, home indicator |
| 38 | Default = -1, // Use the platform default for the control type |
| 39 | All = 1 << 15 // Respect all safe area insets (most restrictive) |
| 40 | } |
| 41 | ``` |
| 42 | |
| 43 | `SoftInput` and `Container` are combinable flags: |
| 44 | `SafeAreaRegions.Container | SafeAreaRegions.SoftInput` = respect system bars **and** keyboard. |
| 45 | |
| 46 | ## SafeAreaEdges Struct |
| 47 | |
| 48 | ```csharp |
| 49 | public readonly struct SafeAreaEdges |
| 50 | { |
| 51 | public SafeAreaRegions Left { get; } |
| 52 | public SafeAreaRegions Top { get; } |
| 53 | public SafeAreaRegions Right { get; } |
| 54 | public SafeAreaRegions Bottom { get; } |
| 55 | |
| 56 | // Uniform — same value for all four edges |
| 57 | public SafeAreaEdges(SafeAreaRegions uniformValue) |
| 58 | |
| 59 | // Horizontal / Vertical |
| 60 | public SafeAreaEdges(SafeAreaRegions horizontal, SafeAreaRegions vertical) |
| 61 | |
| 62 | // Per-edge |
| 63 | public SafeAreaEdges(SafeAreaRegions left, SafeAreaRegions top, |
| 64 | SafeAreaRegions right, SafeAreaRegions bottom) |
| 65 | } |
| 66 | ``` |
| 67 | |
| 68 | Static presets: `SafeAreaEdges.None`, `SafeAreaEdges.All`, `SafeAreaEdges.Default`. |
| 69 | |
| 70 | ### XAML Type Converter |
| 71 | |
| 72 | Follows Thickness-like comma-separated syntax: |
| 73 | |
| 74 | ```xaml |
| 75 | <!-- Uniform --> |
| 76 | SafeAreaEdges="Container" |
| 77 | |
| 78 | <!-- Horizontal, Vertical --> |
| 79 | SafeAreaEdges="Container, SoftInput" |
| 80 | |
| 81 | <!-- Left, Top, Right, Bottom --> |
| 82 | SafeAreaEdges="Container, Container, Container, SoftInput" |
| 83 | ``` |
| 84 | |
| 85 | ## Control Defaults |
| 86 | |
| 87 | | Control | Default | Notes | |
| 88 | |---------|---------|-------| |
| 89 | | `ContentPage` | `None` | Edge-to-edge. **Breaking change from .NET 9 on Android.** | |
| 90 | | `Layout` (Grid, StackLayout, etc.) | `Container` | Respects bars/notch, flows under keyboard | |
| 91 | | `ScrollView` | `Default` | iOS maps to automatic content insets. Only `Container` and `None` take effect. | |
| 92 | | `ContentView` | `None` | Inherits parent behavior | |
| 93 | | `Border` | `None` | Inherits parent behavior | |
| 94 | |
| 95 | ## Breaking Changes from .NET 9 |
| 96 | |
| 97 | ### ContentPage default changed to `None` |
| 98 | |
| 99 | In .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: |
| 100 | |
| 101 | ```xaml |
| 102 | <!-- .NET 10 default — content extends under status bar --> |
| 103 | <ContentPage> |
| 104 | |
| 105 | <!-- Restore .NET 9 Android behavior --> |
| 106 | <ContentPage SafeAreaEdges="Container"> |
| 107 | ``` |
| 108 | |
| 109 | ### W |