Skip to main content
skill CX Codex

maui-safe-area — Claude Code skill: what it does, how to install

Safe area and edge-to-edge layout guidance for .NET 10+. Solves system bar overlaps and keyboard padding across Android, iOS, and Mac Catalyst.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add dotnet/skills --skill maui-safe-area -a claude-code
Open on GitHub

What maui-safe-area does

This skill teaches the assistant how to use the cross-platform safe area API introduced in .NET 10. It focuses on the SafeAreaEdges struct and SafeAreaRegions flags enum (None, SoftInput, Container, All), which replace legacy iOS-only UseSafeArea and layout-level IgnoreSafeArea properties.

Upgrading to .NET 10 breaks previous Android layout assumptions: ContentPage now defaults to None across platforms, rendering views directly under the status bar. The skill provides XAML Thickness-style syntax examples to configure uniform or per-edge insets, handle immersive media views, and avoid soft input keyboard overlap.

The new APIs require target framework net10.0-* or later. If your project targets .NET 9 or earlier, these types do not exist, and the skill falls back to legacy platform properties. Windows does not use system bar insets.

What it helps with

  • prevent UI content from sliding under the status bar, notch, or Dynamic Island
  • configure edge-to-edge screens for media players and maps
  • adjust layouts automatically when the on-screen keyboard opens
  • migrate code from deprecated UseSafeArea and IgnoreSafeArea properties

Example prompts for the agent

  • Configure SafeAreaEdges for a chat screen so the entry box stays above the keyboard
  • Why is my Android ContentPage overlapping the status bar after updating to .NET 10?
  • Show how to set up an edge-to-edge header with safe body content in .NET 10 XAML

How to install maui-safe-area

1Skills CLI (Vercel)

npx skills add dotnet/skills --skill maui-safe-area -a claude-code

2Manually: put the files in place

~/.claude/skills/maui-safe-area/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/maui-safe-area/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about maui-safe-area

How do I install maui-safe-area in Claude Code?

Run npx skills add dotnet/skills --skill maui-safe-area -a claude-code.

What does maui-safe-area do?

It provides architecture rules and XAML patterns for managing insets, system bars, and keyboard avoidance using the .NET 10 safe area API.

Does maui-safe-area support .NET 9 projects?

No, SafeAreaEdges and SafeAreaRegions exist only in net10.0-* or later. For .NET 9, developers must keep using legacy ios:Page.UseSafeArea and Layout.IgnoreSafeArea.

Source text

SKILL.md · MIT
---
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.
license: MIT
---

# Safe Area & Edge-to-Edge Layout (.NET 10+)

.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.

> **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.

## When to Use

- Content overlaps status bar, notch, Dynamic Island, or home indicator after upgrading to .NET 10
- Implementing edge-to-edge / immersive layouts (photo viewers, video players, maps)
- Keyboard avoidance for chat or form UIs
- Migrating from `ios:Page.UseSafeArea`, `Layout.IgnoreSafeArea`, or `WindowSoftInputModeAdjust.Resize`
- Blazor Hybrid apps that need CSS `env(safe-area-inset-*)` coordination
- Mixed layouts with an edge-to-edge header but a safe-area-respecting body

## When Not to Use

- Projects targeting .NET 9 or earlier — use the legacy iOS-specific APIs
- General page layout questions unrelated to system bars or keyboard — use standard layout guidance
- App lifecycle or navigation structure — use maui-app-lifecycle or Shell guidance
- Theming or visual styling — use the **maui-theming** skill

## Inputs

- Target framework: must be `net10.0-*` or later for the new APIs
- Target platforms: Android, iOS, Mac Catalyst (Windows does not have system bar insets)
- UI approach: XAML/C#, Blazor Hybrid, or MauiReactor

## SafeAreaRegions Enum

```csharp
[Flags]
public enum SafeAreaRegions
{
    None      = 0,       // Edge-to-edge — no safe area padding
    SoftInput = 1 << 0,  // Pad to avoid the on-screen keyboard
    Container = 1 << 1,  // Stay inside status bar, notch, home indicator
    Default   = -1,      // Use the platform default for the control type
    All       = 1 << 15  // Respect all safe area insets (most restrictive)
}
```

`SoftInput` and `Container` are combinable flags:
`SafeAreaRegions.Container | SafeAreaRegions.SoftInput` = respect system bars **and** keyboard.

## SafeAreaEdges Struct

```csharp
public readonly struct SafeAreaEdges
{
    public SafeAreaRegions Left { get; }
    public SafeAreaRegions Top { get; }
    public SafeAreaRegions Right { get; }
    public SafeAreaRegions Bottom { get; }

    // Uniform — same value for all four edges
    public SafeAreaEdges(SafeAreaRegions uniformValue)

    // Horizontal / Vertical
    public SafeAreaEdges(SafeAreaRegions horizontal, SafeAreaRegions vertical)

    // Per-edge
    public SafeAreaEdges(SafeAreaRegions left, SafeAreaRegions top,
                         SafeAreaRegions right, SafeAreaRegions bottom)
}
```

Static presets: `SafeAreaEdges.None`, `SafeAreaEdges.All`, `SafeAreaEdges.Default`.

### XAML Type Converter

Follows Thickness-like comma-separated syntax:

```xaml
<!-- Uniform -->
SafeAreaEdges="Container"

<!-- Horizontal, Vertical -->
SafeAreaEdges="Container, SoftInput"

<!-- Left, Top, Right, Bottom -->
SafeAreaEdges="Container, Container, Container, SoftInput"
```

## Control Defaults

| Control | Default | Notes |
|---------|---------|-------|
| `ContentPage` | `None` | Edge-to-edge. **Breaking change from .NET 9 on Android.** |
| `Layout` (Grid, StackLayout, etc.) | `Container` | Respects bars/notch, flows under keyboard |
| `ScrollView` | `Default` | iOS maps to automatic content insets. Only `Container` and `None` take effect. |
| `ContentView` | `None` | Inherits parent behavior |
| `Border` | `None` | Inherits parent behavior |

## Breaking Changes from .NET 9

### ContentPage default changed to `None`

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:

```xaml
<!-- .NET 10 default — content extends under status bar -->
<ContentPage>

<!-- Restore .NET 9 Android behavior -->
<ContentPage SafeAreaEdges="Container">
```

### WindowSoftInputModeAdjust.Resize superseded

`WindowSoftInputModeAdjust.Resize` still exists and still compiles (it is not removed and not obsolete), but it is Android-only. For cross-platform keyboard avoidance prefer `SafeAreaEdges="All"` (or the `SoftInput` region) on the ContentPage.

## Usage Patterns

### Edge-to-edge immersive content

Set `None` on **both** page and layout — layouts default to `Container`:

```xaml
<ContentPage SafeAreaEdges="None">
    <Grid SafeAreaEdges="None">
        <Image Source="background.jpg" Aspect="AspectFill" />
        <VerticalStackLayout Padding="20" VerticalOptions="End">
            <Label Text="Overlay text" TextColor="White" FontSize="24" />
        </VerticalStackLayout>
    </Grid>
</ContentPage>
```

### Forms and critical content

```xaml
<ContentPage SafeAreaEdges="All">
    <VerticalStackLayout Padding="20">
        <Label Text="Safe content" FontSize="18" />
        <Entry Placeholder="Enter text" />
        <Button Text="Submit" />
    </VerticalStackLayout>
</ContentPage>
```

### Keyboard-aware chat layout

```xaml
<ContentPage>
    <Grid RowDefinitions="*,Auto"
          SafeAreaEdges="Container, Container, Container, SoftInput">
        <ScrollView Grid.Row="0">
            <VerticalStackLayout Padding="20" Spacing="10">
                <Label Text="Messages" FontSize="24" />
            </VerticalStackLayout>
        </ScrollView>
        <Border Grid.Row="1" BackgroundColor="LightGray" Padding="20">
            <Grid ColumnDefinitions="*,Auto" Spacing="10">
                <Entry Placeholder="Type a message..." />
                <Button Grid.Column="1" Text="Send" />
            </Grid>
        </Border>
    </Grid>
</ContentPage>
```

### Mixed: edge-to-edge header + safe body + keyboard footer

```xaml
<ContentPage SafeAreaEdges="None">
    <Grid RowDefinitions="Auto,*,Auto">
        <Grid BackgroundColor="{StaticResource Primary}">
            <Label Text="App Header" TextColor="White" Margin="20,40,20,20" />
        </Grid>
        <ScrollView Grid.Row="1" SafeAreaEdges="Container">
            <!-- Use Container, not All — ScrollView only honors Container and None -->
            <VerticalStackLayout Padding="20">
                <Label Text="Main content" />
            </VerticalStackLayout>
        </ScrollView>
        <Grid Grid.Row="2" SafeAreaEdges="SoftInput"
              BackgroundColor="LightGray" Padding="20">
            <Entry Placeholder="Type a message..." />
        </Grid>
    </Grid>
</ContentPage>
```

### Programmatic (C#)

```csharp
var page = new ContentPage
{
    SafeAreaEdges = SafeAreaEdges.All
};

var grid = new Grid
{
    SafeAreaEdges = new SafeAreaEdges(
        left: SafeAreaRegions.Container,
        top: SafeAreaRegions.Container,
        right: SafeAreaRegions.Container,
        bottom: SafeAreaRegions.SoftInput)
};
```

## Decision Framework

| Scenario | SafeAreaEdges value |
|----------|---------------------|
| Forms, critical inputs | `All` |
| Photo viewer, video player, game | `None` (on page **and** layout) |
| Scrollable content with fixed header/footer | `Container` |
| Chat/messaging with bottom input bar | Per-edge: `Container, Container, Container, SoftInput` |
| Blazor Hybrid app | `None` on page; CSS `env()` for insets |

## Blazor Hybrid Integration

For Blazor Hybrid apps, let CSS handle safe areas to avoid double-padding.

1. **Page stays edge-to-edge** (default in .NET 10):

```xaml
<ContentPage SafeAreaEdges="None">
    <BlazorWebView HostPage="wwwroot/index.html">
        <BlazorWebView.RootComponents>
            <RootComponent Selector="#app" ComponentType="{x:Type local:Routes}" />
        </BlazorWebView.RootComponents>
    </BlazorWebView>
</ContentPage>
```

2. **Add `viewport-fit=cover`** in `index.html`:

```html
<meta name="viewport" content="width=device-width, initial-scale=1.0,
      maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
```

3. **Use CSS `env()` functions**:

```css
body {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
}
```

Available CSS environment variables: `env(safe-area-inset-top)`, `env(safe-area-inset-bottom)`, `env(safe-area-inset-left)`, `env(safe-area-inset-right)`.

## Migration from Legacy APIs

| Legacy (.NET 9 and earlier) | New (.NET 10+) |
|-----------------------------|----------------|
| `ios:Page.UseSafeArea="True"` | `SafeAreaEdges="Container"` |
| `Layout.IgnoreSafeArea="True"` | `SafeAreaEdges="None"` |
| `WindowSoftInputModeAdjust.Resize` | `SafeAreaEdges="All"` on ContentPage |

The legacy `ios:Page.UseSafeArea` and `Layout.IgnoreSafeArea` properties still compile but are marked obsolete. `IgnoreSafeArea="True"` maps internally to `SafeAreaRegions.None`. `WindowSoftInputModeAdjust.Resize` is **not** obsolete — it remains supported, but is Android-only.

```xaml
<!-- .NET 9 (legacy, iOS-only) -->
<ContentPage xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
             ios:Page.UseSafeArea="True">

<!-- .NET 10+ (cross-platform) -->
<ContentPage SafeAreaEdges="Container">
```

## Platform-Specific Behavior

### iOS & Mac Catalyst

- Safe area insets cover: status bar, navigation bar, tab bar, notch/Dynamic Island, home indicator
- `SoftInput` includes the keyboard when visible
- Insets update automatically on rotation and UI visibility changes
- `ScrollView` with `Default` maps to `UIScrollViewContentInsetAdjustmentBehavior.Automatic`

Transparent navigation bar for content behind the nav bar:

```xaml
<Shell Shell.BackgroundColor="#80000000" Shell.NavBarHasShadow="False" />
```

### Android

- Safe area insets cover: system bars (status/navigation) and display cutouts
- `SoftInput` includes the soft keyboard
- MAUI uses `WindowInsetsCompat` and `WindowInsetsAnimationCompat` internally
- Behavior varies by Android version and OEM edge-to-edge settings

## Common Pitfalls

1. **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.

2. **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.

3. **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."

4. **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.

5. **Missing `viewport-fit=cover` in Blazor.** Without this meta tag, CSS `env(safe-area-inset-*)` values are always zero on iOS.

6. **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.

7. **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.

## Checklist

- [ ] Android upgrade: `SafeAreaEdges="Container"` added if content goes under status bar
- [ ] Edge-to-edge: `None` set on **both** page and layout
- [ ] ScrollView keyboard avoidance uses wrapper Grid, not ScrollView's own `SafeAreaEdges`
- [ ] Blazor Hybrid: using either XAML or CSS safe areas, not both
- [ ] `viewport-fit=cover` in Blazor's `index.html` `<meta viewport>` tag
- [ ] Legacy `UseSafeArea` / `IgnoreSafeArea` migrated to `SafeAreaEdges`

© dotnet · MIT · dotnet/skills

Similar skills

Everything for Mobile →

From the same set101

template-discovery Finds suitable dotnet new templates from a project description, then checks parameters and expected output before files are created. CX Codex +1 3,300installs configure-auth Adds authentication and authorization to a Blazor Web App based on its render mode. It prevents runtime crashes with Identity and interactive routing. CX Codex +1 3,100installs apple-crash-symbolication Turns raw addresses in an .ips crash log into function names, source files and line numbers for .NET. Useful when a MAUI or Mono app dies on iOS, tvOS, macOS or Mac Catalyst. CX Codex +1 3,100installs system-text-json-net11 A skill that tells the agent how to use three new System.Text.Json APIs in .NET 11 instead of writing the old workarounds. CX Codex +1 3,100installs maui-shell-navigation Set up Shell navigation in a .NET MAUI app, including tabs, flyout menus, and page transitions. Use it when routes or navigation parameters need attention. CX Codex +1 3,100installs maui-collectionview A reference skill for CollectionView in .NET MAUI: item templates, grids, grouping, incremental loading and swipe actions. Useful when the agent writes list XAML. CX Codex +1 3,200installs use-js-interop Sets up and audits JavaScript interop in Blazor components. It guides .NET-to-JS calls, lifecycle handling, and call batching across the circuit. CX Codex +1 3,000installs convert-blazor-server-to-webapp Guides a pre-.NET 8 Blazor Server app to the Blazor Web App model. Use it when replacing the old host setup while retaining interactive server rendering. CX Codex +1 3,000installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor