P11ModalNative Component
P11ModalNative component is an adaptive, high-end dialog system. Unlike standard modals, it automatically adjusts its visual structure—including title alignment, close buttons, and action positions—to match the look and feel of Windows, macOS, iOS, or Android. This ensures a seamless, 'native-like' experience for users across different platforms within a Blazor environment.Automated Native Back-Stack & Gesture Management
This component automatically manages its Z-Index and registers itself in the global modal stack.
- Native Gesture Support: Swipe-Back (iOS) and hardware Back-Button (Android) are handled via the global stack and event bridge.
- Global Control via Service:
Use the
IEventStateService
to access the current modal state and trigger close actions (e.g.
TryCloseTopModalAsync) from your MainLayout or App logic. - Cleanup on Navigation:
Call
ClearAllNativeModalsAsyncwhen navigating away to reset all states.
Note: For integration details and examples see: EventStateService documentation
Adaptive UI
Automatically repositions header elements (like the macOS 'Traffic Lights' or iOS 'Done' buttons) based on the target device setting.Desktop & Mobile Pro
Supports desktop features like 'Maximize' and 'Sticky Headers' alongside mobile-first interactions and full-screen modes.NativeDevice parameter to switch layouts. For accessibility, the component maintains strict ARIA standards, ensuring that even with custom native layouts, screen readers correctly identify titles and controls.Modal native Examples
This section demonstrates the usage of the P11ModalNative component with various configurations and highlights its key features.
Windows / Web
macOS
Mobile
Implementation Details
<div class="container mt-5">
<div class="p-4 border rounded bg-white shadow-sm">
<div class="row g-4">
@* --- WINDOWS / WEB --- *@
<div class="col-md-4">
<h6 class="fw-bold text-uppercase text-muted small mb-3 border-bottom pb-1">Windows / Web</h6>
<div class="d-grid gap-2">
<button class="btn btn-sm btn-outline-secondary text-start" @onclick='(() => Open(NativeDevice.WEB, "Info Dialog", false, false))'>Standard (Kein Scroll/Max)</button>
<button class="btn btn-sm btn-outline-secondary text-start" @onclick='(() => Open(NativeDevice.WEB, "Dokument", false, true))'>Standard + Scroll</button>
<button class="btn btn-sm btn-outline-secondary text-start" @onclick='(() => Open(NativeDevice.WINDOWS, "Editor", true, true))'>Maximize + Scroll</button>
</div>
</div>
@* --- MAC --- *@
<div class="col-md-4">
<h6 class="fw-bold text-uppercase text-muted small mb-3 border-bottom pb-1">macOS</h6>
<div class="d-grid gap-2">
<button class="btn btn-sm btn-outline-secondary text-start" @onclick='(() => Open(NativeDevice.MAC, "Finder Info", false, false))'>macOS Info (Nur Rot)</button>
<button class="btn btn-sm btn-outline-secondary text-start" @onclick='(() => Open(NativeDevice.MAC, "System Log", true, true))'>macOS Full (Rot/Grün + Scroll)</button>
</div>
</div>
@* --- MOBILE VARIANTEN --- *@
<div class="col-md-4">
<h6 class="fw-bold text-uppercase text-muted small mb-3 border-bottom pb-1">Mobile Varianten</h6>
<div class="d-grid gap-2">
@* iOS Triple *@
<button class="btn btn-sm btn-primary text-start opacity-75" @onclick='(() => OpenMobile(NativeDevice.IPHONE, "Settings", false, false))'>iOS: Nur Links (Clean)</button>
<button class="btn btn-sm btn-primary text-start" @onclick='(() => OpenMobile(NativeDevice.IPHONE, "New Contact", true, false))'>iOS: Links + Rechts (Clean)</button>
<button class="btn btn-sm btn-primary text-start fw-bold" @onclick='(() => OpenMobile(NativeDevice.IPHONE, "Mail Edit", true, true))'>iOS: Buttons + Linie</button>
@* Android Triple *@
<div class="mt-2"></div>
<button class="btn btn-sm btn-success text-start opacity-75" @onclick='(() => OpenMobile(NativeDevice.ANDROID, "Profile", false, false))'>Android: Nur Links (Clean)</button>
<button class="btn btn-sm btn-success text-start" @onclick='(() => OpenMobile(NativeDevice.ANDROID, "Settings", true, false))'>Android: Links + Rechts (Clean)</button>
<button class="btn btn-sm btn-success text-start fw-bold" @onclick='(() => OpenMobile(NativeDevice.ANDROID, "System Info", true, true, "bg-light"))'>Android: Buttons + Linie + Gray</button>
</div>
</div>
</div>
</div>
</div>
@* Die Komponente *@
<P11ModalNative @bind-Visible="_isVisible"
NativeDevice="_currentDevice"
AllowMaximize="_allowMaximize"
Position="@(_shouldScroll ? ModalPosition.Scrollable : ModalPosition.Default)"
Title="@_modalTitle"
ShowHeaderSeparator="_showSeparator"
CssClassHeader="@_headerClass"
CloseButtonText="@_btnText"
CloseButtonIconCss="@_btnIcon">
<HeaderAction>
@if (_showSaveButton)
{
@if (_currentDevice == NativeDevice.IPHONE)
{
<button class="btn btn-link p-0 text-decoration-none fw-bold" style="color: #007aff; font-size: 1rem;" @onclick="Save">Fertig</button>
}
else if (_currentDevice == NativeDevice.ANDROID)
{
<button class="btn btn-link text-dark p-1" @onclick="Save"><i class="bi bi-check2 fs-4"></i></button>
}
}
</HeaderAction>
<BodyContent>
<div class="p-3">
<p class="text-muted small">Modus: <strong>@_currentDevice</strong></p>
@if (_shouldScroll)
{
<div style="height: 1000px; background: linear-gradient(180deg, #f8f9fa 0%, #dee2e6 100%); padding: 20px;" class="rounded border shadow-sm">
<p class="fw-bold">Scrollbarer Inhalt aktiv.</p>
</div>
}
else
{
<div class="alert alert-secondary border-0 rounded-3">Kompakter Inhalt ohne Scrollen.</div>
}
</div>
</BodyContent>
<FooterContent>
<div class="d-flex justify-content-end gap-2 w-100">
@if (_currentDevice == NativeDevice.MAC)
{
@* macOS Buttons mit BS 5.3 Utilities (rounded-2 = 6px radius) *@
<button class="btn btn-sm btn-outline-secondary rounded-2 px-3 border-secondary-subtle text-dark" style="font-size: 0.85rem; min-width: 80px;" @onclick="Close">Abbrechen</button>
<button class="btn btn-sm btn-primary rounded-2 px-3 shadow-sm" style="font-size: 0.85rem; min-width: 80px;" @onclick="Save">Speichern</button>
}
else if (_currentDevice == NativeDevice.WEB || _currentDevice == NativeDevice.WINDOWS)
{
<button class="btn btn-secondary" @onclick="Close">Abbrechen</button>
<button class="btn btn-primary px-4" @onclick="Save">OK</button>
}
</div>
</FooterContent>
</P11ModalNative>@code {
private bool _isVisible;
private NativeDevice _currentDevice;
private bool _allowMaximize;
private bool _shouldScroll;
private bool _showSeparator;
private bool _showSaveButton;
private string _modalTitle = "";
private string? _btnText;
private string? _btnIcon;
private string? _headerClass;
private void Open(NativeDevice dev, string title, bool max, bool scroll)
{
Reset();
_currentDevice = dev;
_modalTitle = title;
_allowMaximize = max;
_shouldScroll = scroll;
_isVisible = true;
}
private void OpenMobile(NativeDevice dev, string title, bool showSave, bool separator, string? bgColorClass = null)
{
Reset();
_currentDevice = dev;
_modalTitle = title;
_showSaveButton = showSave;
_showSeparator = separator;
_shouldScroll = true; // Mobile fast immer mit Scroll
_headerClass = bgColorClass;
_isVisible = true;
if (dev == NativeDevice.IPHONE)
{
_btnText = "Zurück";
_btnIcon = "bi bi-chevron-left";
}
}
private void Reset()
{
_allowMaximize = false;
_shouldScroll = false;
_showSeparator = true;
_showSaveButton = false;
_btnText = null;
_btnIcon = null;
_headerClass = null;
}
private void Save() => _isVisible = false;
private void Close() => _isVisible = false;
}1. Best Practice Setup Guide
To enable native features like iOS Edge-Swipe or Android Hardware Back-Button management, follow these steps:
Register the IEventStateService as a Singleton in Blazor WASM to maintain a consistent modal stack across the application lifecycle.
// Registration in Blazor WASM / Capacitor
builder.Services.AddSingleton<IEventStateService, EventStateService>();The MainLayout acts as the central coordinator. You must subscribe to the navigation events and initialize the bridge.
protected override void OnInitialized()
{
// Subscribe to stack changes to trigger StateHasChanged
EventState.OnStackChanged += HandleStackChanged;
// Critical: Listen for iOS swipe events when no modals are open
EventState.OnGlobalBackNavigationRequested += HandleIOSGlobalBack;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// 1. Determine platform and map to NativeDevice enum
var device = GetCurrentDevice();
// 2. Optional: Configure Debug Mode and Swipe behavior
#if DEBUG
await EventState.SetDebugModeAsync(true);
#endif
EventState.SwipeConfig.SwipeThreshold = 40;
// 3. Initialize the JS-Bridge and internal DotNetObjectReference
await EventState.InitializeAsync(device);
// 4. Capacitor specific: Register the MainLayout for Android Hardware Back
await Platform.RegisterNativeNavigationAsync(_dotNetRef);
}
}1. For Capacitor Projects (Recommended & Tested)
Ensure Capacitor is initialized (e.g., in index.html). Your bridge script (cap.js) must forward the hardware event to Blazor:
// Inside your cap.js bridge
registerNavigationHelper: function (helper) {
this._dotNetHelper = helper;
if (window.pE_Capacitor?.App) {
window.pE_Capacitor.App.addListener('backButton', async (data) => {
if (this._dotNetHelper) {
// Bridge to MainLayout.razor
await this._dotNetHelper.invokeMethodAsync('HandleNativeBack', data.canGoBack);
}
});
}
}
2. For .NET MAUI Projects (Untested / Community Info)
In MAUI, you can bypass JavaScript and call the Service directly from C#:
// Inside AppShell.xaml.cs (MAUI Android logic)
protected override async void OnBackButtonPressed()
{
// Ask P11 if it can handle the back-action (close a modal)
var wasModalClosed = await EventStateService.TryCloseTopModalAsync();
if (!wasModalClosed) {
base.OnBackButtonPressed(); // Normal navigation if no modal was open
}
}2. Implementation Rules & Best Practices
To ensure maximum stability and prevent "ghost" overlays or incorrect stacking behavior, please strictly follow these two essential rules:
Persistent Modal IDs (Guids)
The P11ModalNative uses a global stack to manage back-navigation and gestures. To keep this stack accurate, every modal must have a unique, persistent ID.
Guid as a private field in your @code block, not directly in the component parameter.
Why? If you use Id="@Guid.NewGuid()", the ID changes every time Blazor triggers SetParametersAsync. This confuses the internal stack management and may prevent the modal from closing correctly.
// CORRECT: The ID remains constant during the page lifecycle
private Guid _myModalId = Guid.NewGuid();
// Usage in Razor
<P11ModalNative Id="_myModalId" @bind-Visible="_showModal" ... />Avoiding Nested Modals (iOS Stacking Context)
Testing revealed a critical behavior in how iOS engines handle CSS Stacking Contexts compared to Android or Windows.
P11ModalNative components as siblings on the same hierarchy level. Never nest one modal inside the code block of another.
The Issue: If Modal B is nested inside Modal A's code, iOS sometimes collapses the entire stack when the topmost modal is swiped closed, causing all parent modals to disappear instantly.
<P11ModalNative Id="ID1">
<!-- Content only -->
</P11ModalNative>
<P11ModalNative Id="ID2">
<!-- Content only -->
</P11ModalNative> <P11ModalNative Id="ID1">
<P11ModalNative Id="ID2">
<!-- This fails on iOS! -->
</P11ModalNative>
</P11ModalNative>3. Enabling Native Mobile Look & Feel
By default, the P11 Library remains platform-agnostic to support Blazor Server and WPF. To activate the high-end native behaviors (like Viewport fixation, iOS Safe Areas, and Swipe-container optimization), you must explicitly enable the mobile mode in your host project.
Activation via index.html
In your Blazor WASM (Capacitor) or MAUI project,
locate the index.html and add the class p11-mobile to the root <html> tag:
<!-- index.html: Mandatory for Capacitor & MAUI -->
<html lang="en" class="p11-mobile">
...
</html>
What happens under the hood?
Once the p11-mobile class is present, the p11.css activates several critical native rules:
The body is set to position: fixed. This prevents the entire browser layout
from shifting or expanding during translateX animations (Swipe-to-close).
Automatically respects env(safe-area-inset-top) for modern iPhones, ensuring
content doesn't disappear under the "Notch" or the home indicator.
Optimizes touch-action for buttons and disables text-selection on UI elements
to mimic native app behavior, while keeping inputs selectable.
Couples the footer and #app container correctly, so that during a
swipe-back gesture, the UI moves as one stable unit without "white edges".
p11-mobile class for
Blazor Server or Blazor WPF unless you specifically want the fixed,
app-like viewport behavior on those platforms as well.
4. Advanced Tuning via ThemeService (Global Overrides)
To avoid rebuilding NuGet packages for minor CSS layout adjustments, P11 uses a Global Variable approach.
All layout-critical values are stored in the :root element and can be overridden at runtime via C#.
Runtime Customization
Use the ThemeService to inject new values into the :root selector.
This is typically done in your MainLayout.razor during initialization.
// Adjusting the native behavior live from C# code
ThemeService.SetScopedVariables(":root", new Dictionary<string, string> {
{ "--p11-footer-fix", "0px" }, // Manually move footer to the very bottom
{ "--p11-main-padding-bottom", "20px" }, // Add more scroll-breathing room
{ "--p11-swipe-transition", "0.2s ease-out" }, // Make animations snappier
{ "--p11-overlay-bg", "rgba(0,0,0,0.8)" } // Change backdrop darkness
});
Available Configuration Variables
| Variable | Default (Fallback) | Description |
|---|---|---|
--p11-footer-fix |
0px (or 28px via JS) |
Vertical offset for the footer (overrides iOS auto-detection). |
--p11-main-padding-bottom |
15px |
Extra space at the end of the scrollable <main> area. |
--p11-safe-area-top |
env(...) |
Top margin for status bars and notches. |
--p11-swipe-transition |
0.3s ... |
Duration and feel of the Modal slide-in/out effect. |
--p11-overlay-bg |
#000000 |
The background color of the swipe-to-close overlay. |
:root, you ensure that your settings propagate
through the entire application instantly, bypassing any hardcoded library defaults.
Implementation Details
<div class="component-description mt-4">
<P11Button Text="Open Modal 1" OnClick="() => isOpenModalEx2_1 = true" />
<br />
@if (isOpenModalEx2_1)
{
<P11ModalNative @bind-Visible="@isOpenModalEx2_1" OnClose="async () => { }"
Id="@guidModalEx2_1"
NativeDevice="NativeDevice.IPHONE"
ShowHeaderSeparator="true"
CloseButtonText="Back"
CloseButtonIconCss="bi bi-chevron-left"
AriaLabelledby="modalTitle1Help"
CssClassContent="p11-form-bg-01"
Size="Size.Large">
<HeaderContent>
<h5 id="specific-position-panel-settings" class="modal-title">Modal 1</h5>
</HeaderContent>
<BodyContent>
<h3>Modal 1</h3>
<P11Button Text="Open Modal 2" OnClick="() => isOpenModalEx2_2 = true" />
<div class="alert alert-info mt-3">
<strong>P11 Stack Info:</strong>
<ul class="list-unstyled mb-0">
<li>Open Modals: <strong>@EventState.ModalCount</strong></li>
<li>Top Modal ID: <small>@EventState.TopModalId</small></li>
<li>Stack Active: <span class="badge @(EventState.IsAnyModalOpen ? "bg-success" : "bg-danger")">
@(EventState.IsAnyModalOpen ? "YES" : "NO")
</span></li>
</ul>
</div>
<div class="d-flex flex-column gap-2 mt-3">
<P11Button Text="Simulate external closing"
OnClick="async() => {
if(EventState.IsAnyModalOpen)
await EventState.TryCloseTopModalAsync();
}" />
<P11Button Text="Toggle Debug Logs"
OnClick="() => EventState.DebugMode = !EventState.DebugMode" />
</div>
<div class="mt-3">
<h6>Full Stack Trace:</h6>
<div style="font-size: 10px; font-family: monospace; background: #eee; padding: 5px;">
@foreach (var id in EventState.ModalStack)
{
<div>ID: @id @(id == EventState.TopModalId ? "<-- TOP" : "")</div>
}
</div>
</div>
</BodyContent>
</P11ModalNative>
}
@if (isOpenModalEx2_2)
{
<P11ModalNative @bind-Visible="@isOpenModalEx2_2" OnClose="async () => { }"
Id="@guidModalEx2_2"
NativeDevice="NativeDevice.IPHONE"
ShowHeaderSeparator="true"
CloseButtonText="Back"
CloseButtonIconCss="bi bi-chevron-left"
AriaLabelledby="modalTitle1Help"
CssClassContent="p11-form-bg-01"
Size="Size.Large">
<HeaderContent>
<h5 id="specific-position-panel-settings" class="modal-title">Modal 2</h5>
</HeaderContent>
<BodyContent>
<h3>Modal 2</h3>
<P11Button Text="Open Modal 3" OnClick="() => isOpenModalEx2_3 = true" />
<div class="alert alert-info mt-3">
<strong>P11 Stack Info:</strong>
<ul class="list-unstyled mb-0">
<li>Open Modals: <strong>@EventState.ModalCount</strong></li>
<li>Top Modal ID: <small>@EventState.TopModalId</small></li>
<li>Stack Active: <span class="badge @(EventState.IsAnyModalOpen ? "bg-success" : "bg-danger")">
@(EventState.IsAnyModalOpen ? "YES" : "NO")
</span></li>
</ul>
</div>
<div class="d-flex flex-column gap-2 mt-3">
<P11Button Text="Simulate external closing"
OnClick="async() => {
if(EventState.IsAnyModalOpen)
await EventState.TryCloseTopModalAsync();
}" />
<P11Button Text="Toggle Debug Logs"
OnClick="() => EventState.DebugMode = !EventState.DebugMode" />
</div>
<div class="mt-3">
<h6>Full Stack Trace:</h6>
<div style="font-size: 10px; font-family: monospace; background: #eee; padding: 5px;">
@foreach (var id in EventState.ModalStack)
{
<div>ID: @id @(id == EventState.TopModalId ? "<-- TOP" : "")</div>
}
</div>
</div>
</BodyContent>
</P11ModalNative>
}
@if (isOpenModalEx2_3)
{
<P11ModalNative @bind-Visible="@isOpenModalEx2_3" OnClose="async () => { }"
Id="@guidModalEx2_3"
NativeDevice="NativeDevice.IPHONE"
ShowHeaderSeparator="true"
CloseButtonText="Back"
CloseButtonIconCss="bi bi-chevron-left"
AriaLabelledby="modalTitle1Help"
CssClassContent="p11-form-bg-01"
Size="Size.Large">
<HeaderContent>
<h5 id="specific-position-panel-settings" class="modal-title">Modal 3</h5>
</HeaderContent>
<BodyContent>
<h3>Modal 3</h3>
<div class="alert alert-info mt-3">
<strong>P11 Stack Info:</strong>
<ul class="list-unstyled mb-0">
<li>Open Modals: <strong>@EventState.ModalCount</strong></li>
<li>Top Modal ID: <small>@EventState.TopModalId</small></li>
<li>Stack Active: <span class="badge @(EventState.IsAnyModalOpen ? "bg-success" : "bg-danger")">
@(EventState.IsAnyModalOpen ? "YES" : "NO")
</span></li>
</ul>
</div>
<div class="d-flex flex-column gap-2 mt-3">
<P11Button Text="Simulate external closing"
OnClick="async() => {
if(EventState.IsAnyModalOpen)
await EventState.TryCloseTopModalAsync();
}" />
<P11Button Text="Toggle Debug Logs"
OnClick="() => EventState.DebugMode = !EventState.DebugMode" />
</div>
<div class="mt-3">
<h6>Full Stack Trace:</h6>
<div style="font-size: 10px; font-family: monospace; background: #eee; padding: 5px;">
@foreach (var id in EventState.ModalStack)
{
<div>ID: @id @(id == EventState.TopModalId ? "<-- TOP" : "")</div>
}
</div>
</div>
</BodyContent>
</P11ModalNative>
}
</div>@code {
// Example 2
private bool isOpenModalEx2_1 = false;
private bool isOpenModalEx2_2 = false;
private bool isOpenModalEx2_3 = false;
private bool isOpenModalEx2_4 = false;
private Guid guidModalEx2_1 = Guid.NewGuid();
private Guid guidModalEx2_2 = Guid.NewGuid();
private Guid guidModalEx2_3 = Guid.NewGuid();
private Guid guidModalEx2_4 = Guid.NewGuid();
protected override void OnInitialized()
{
// 1. Stack-Changes für UI Updates
EventState.OnStackChanged += HandleStackChanged;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Initialisierung des Services (wie im MainLayout)
await EventState.InitializeAsync(NativeDevice.IPHONE);
}
}
private void HandleStackChanged() => InvokeAsync(StateHasChanged);
public override void Dispose()
{
EventState.OnStackChanged -= HandleStackChanged;
}
/* -------------------------------------------------------------------------
REFERENCE: Required MainLayout Implementation
The following logic must exist in your MainLayout.razor to bridge
native OS events to the P11 Modal Native system:
-------------------------------------------------------------------------
// 1. Native Bridge Entry Point (Android/Capacitor)
[JSInvokable("HandleNativeBack")]
public async Task HandleNativeBack(bool canGoBack)
{
// Check if any P11 Modal is open and close the topmost one
bool wasModalClosed = await _eventState.TryCloseTopModalAsync();
if (wasModalClosed) return; // Event consumed, do nothing more
// Otherwise: Standard navigation logic (e.g., NavigateBack or Exit App)
await ProcessGlobalBackLogic(canGoBack);
}
// 2. iOS Swipe Listener (Global back when no modals are open)
private async Task HandleIOSGlobalBack()
{
// P11 calls this when a swipe occurs but the Modal Stack is empty
await ProcessGlobalBackLogic(true);
}
// 3. Lifecycle Registration
protected override void OnInitialized()
{
_eventState.OnStackChanged += HandleModalStackChanged;
_eventState.OnGlobalBackNavigationRequested += HandleIOSGlobalBack;
}
*/
}Component API
| Parameter | Type | Default | Description |
|---|---|---|---|
Visible |
bool |
false |
Controls the visibility of the modal. Bind with @bind-Visible. |
Id |
Guid |
- | Unique identifier for the modal stack. Mandatory for reliable back-navigation tracking. |
NativeDevice |
NativeDevice |
WEB |
Sets the visual style and header behavior (WEB, WINDOWS, MAC, IPHONE, ANDROID). |
IsNativeControlFullscreen |
bool |
true |
Forces fullscreen mode on mobile devices (iPhone/Android) regardless of other settings. |
AllowMaximize |
bool |
false |
Enables the maximize/zoom button for Desktop modes (Windows/macOS). |
CloseOnOutsideClick |
bool |
false |
If true, clicking the backdrop closes the modal. |
| Content & Layout | |||
Title |
string? |
null |
The title displayed in the header. Position adapts to the NativeDevice. |
HeaderContent |
RenderFragment? |
- | Additional custom content for the header area. |
HeaderAction |
RenderFragment? |
- | Slot for primary actions (e.g., 'Save') positioned according to OS standards. |
BodyContent |
RenderFragment? |
- | The main content of the modal. |
FooterContent |
RenderFragment? |
- | Content for the modal's footer (usually Action-Buttons). |
| Visuals & UI | |||
Size |
Size |
Medium |
The Bootstrap size of the modal dialog. |
Position |
ModalPosition |
Default |
Positioning logic (Default or Scrollable). |
IsFullScreen |
bool |
false |
Forces the modal to take up the entire viewport. |
ShowHeaderSeparator |
bool |
true |
Shows a border-bottom under the header. |
CloseButtonText |
string? |
null |
Custom text for the close button (e.g., 'Zurück' for iOS). |
CloseButtonIconCss |
string? |
null |
Icon class (e.g., 'bi bi-chevron-left') for the close button. |
| Custom CSS Classes | |||
CssClassContent |
string? |
null |
CSS class for the .modal-content container. |
CssClassHeader |
string? |
null |
CSS class for the .modal-header. |
CssClassBody |
string? |
null |
CSS class for the .modal-body. |
CssClassFooter |
string? |
null |
CSS class for the .modal-footer. |
| Behavior & A11y | |||
ShowCloseButton |
bool |
true |
Determines if the close control is rendered. |
PreventScroll |
bool |
true |
Locks background page scrolling. |
RestoreFocusId |
string? |
null |
Element ID to refocus after closing. |
AriaLabel |
string? |
null |
Accessibility label. |
AriaLabelledby |
string? |
null |
ID of the labeling element. |
| Events | |||
VisibleChanged |
EventCallback<bool> |
- | Supports @bind-Visible. |
OnClose |
EventCallback |
- | Invoked when the modal is closed. |