Navbar
A navigation system that provides toggle state, responsive show/hide, keyboard dismiss, and ARIA attributes — while giving you full visual control via Tailwind CSS or any CSS.
Marai.UI has two separate navigation components for different use cases:
- MNav (this page) — a stateful, composable system for responsive layouts. Use it when you need a hamburger-driven sidebar or collapsible menu. It owns open/close state and wires ARIA across multiple sub-components.
- MNavbar — a lightweight, opinionated top navigation bar for simple site headers. Use it when you need a persistent horizontal top bar with brand, content slots, and action buttons, but no toggle behavior or sidebar.
Overview
The MNav system gives you full visual control while managing all navigation state and ARIA wiring.
It owns state (is the sidebar open? is the menu open?) and exposes it to descendants via a cascading context.
Developers control all visual aspects through CSS classes — the components set
data-state="open" / data-state="closed" on elements so you can target state
with standard CSS attribute selectors or Tailwind arbitrary variants like data-[state=open]:translate-x-0.
The system is composed of six components:
MNav— root; owns state and provides context to all children viaCascadingValueMNavBar— semantic<header>wrapper; zero behavior, pure structureMNavToggle—<button>wired to context; setsaria-expanded/aria-controlsMNavMenu—<nav>withdata-state; always in DOM;aria-hiddenwhen closedMNavSidebar—<aside>withdata-state; Escape key dismiss; optional focus-on-openMNavOverlay— backdrop<div>; only rendered in DOM when any nav is open; click closes all
Usage
Add the namespace to your _Imports.razor:
@using Marai.UI.Components.MNavSidebar Toggle (Mobile Drawer)
Click the hamburger to open the sidebar. Click the overlay or press Escape to close.
<MNav class="relative h-50 overflow-hidden rounded-lg border border-slate-200">
<MNavBar class="flex items-center h-14 px-4 gap-3 bg-primary text-white">
<span class="font-bold text-[0.95rem]">My App</span>
<MNavToggle Target="MNavTarget.Sidebar"
class="ml-auto bg-transparent border-0 text-white cursor-pointer p-1.5 rounded hover:bg-white/10 flex items-center justify-center">
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" viewBox="0 0 24 24" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
</MNavToggle>
</MNavBar>
<MNavSidebar class="absolute top-14 left-0 w-50 h-[calc(100%-3.5rem)] bg-white border-r border-slate-200 p-4
-translate-x-full data-[state=open]:translate-x-0 transition-transform duration-200">
<nav class="flex flex-col gap-2 text-sm">
<a href="#" class="text-slate-700 no-underline hover:text-slate-900">Home</a>
<a href="#" class="text-slate-700 no-underline hover:text-slate-900">Docs</a>
<a href="#" class="text-slate-700 no-underline hover:text-slate-900">Components</a>
</nav>
</MNavSidebar>
<MNavOverlay class="absolute inset-0 bg-black/40" />
</MNav>
Menu Toggle (Dropdown Nav)
Target MNavTarget.Menu to toggle a horizontal menu panel instead of a sidebar.
<MNav class="relative h-50 overflow-hidden rounded-lg border border-border">
<MNavBar class="flex items-center h-14 px-4 gap-3 bg-primary text-white">
<span class="font-bold text-[0.95rem]">My App</span>
<MNavToggle Target="MNavTarget.Menu"
class="ml-auto bg-transparent border-0 text-white cursor-pointer p-1.5 rounded hover:bg-white/10 flex items-center justify-center">
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" viewBox="0 0 24 24" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
</MNavToggle>
</MNavBar>
<MNavMenu class="absolute top-14 left-0 right-0 bg-primary px-4 pb-4
scale-y-0 data-[state=open]:scale-y-100 origin-top transition-transform duration-150">
<nav class="flex flex-col gap-1">
<a href="#" class="text-white/80 no-underline text-sm py-1.5 border-t border-white/10 hover:text-white">Home</a>
<a href="#" class="text-white/80 no-underline text-sm py-1.5 border-t border-white/10 hover:text-white">Docs</a>
<a href="#" class="text-white/80 no-underline text-sm py-1.5 border-t border-white/10 hover:text-white">Components</a>
</nav>
</MNavMenu>
</MNav>
The data-state Pattern
MNavSidebar and MNavMenu always render in the DOM.
They receive data-state="open" or data-state="closed" based on context state.
Target these with CSS attribute selectors:
/* sidebar slides in from left on mobile */
.my-sidebar {
transform: translateX(-100%);
transition: transform 0.2s;
}
.my-sidebar[data-state=open] {
transform: translateX(0);
}Or with Tailwind CSS arbitrary variants in your class string:
<MNavSidebar Class="fixed top-0 left-0 h-full w-64 z-50
-translate-x-full data-[state=open]:translate-x-0
transition-transform duration-200
lg:translate-x-0 lg:static lg:h-auto lg:z-auto">
<!-- sidebar links -->
</MNavSidebar>Full Layout Integration
Wrap your entire layout in MNav so the cascading context reaches both
the navbar and the sidebar even when they live in separate child components:
@* DocsLayout.razor — MNav wraps both TopNavbar and Sidebar *@
@inherits LayoutComponentBase
<MNav>
<TopNavbar />
<div class="docs-layout">
<Sidebar />
<main>@Body</main>
</div>
</MNav>
@* TopNavbar.razor — reads MNavContext via CascadingParameter *@
<MNavBar class="...your-classes...">
<a href="/">Logo</a>
<MNavToggle Target="MNavTarget.Sidebar" class="... lg:hidden">
<!-- hamburger icon -->
</MNavToggle>
</MNavBar>
@* Sidebar.razor — MNavSidebar picks up state from parent MNav *@
<MNavSidebar class="fixed ... -translate-x-full data-[state=open]:translate-x-0 ...">
<!-- nav links -->
</MNavSidebar>
<MNavOverlay class="fixed inset-0 bg-black/40 z-40 lg:hidden" />Accessibility
MNavTogglesetsaria-expanded="true"/"false"based on the target's open stateMNavTogglesetsaria-controls="m-nav-menu"oraria-controls="m-nav-sidebar"MNavMenusetsaria-hidden="true"when closedMNavSidebarreceives focus via Blazor'sElementReference.FocusAsync()when opened (setFocusOnOpen="false"to opt out)- Pressing Escape while focus is inside
MNavSidebarcloses all nav MNavOverlayincludesaria-hidden="true"and closes all nav on click
API Reference
MNav
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
All nav sub-components. State is cascaded to everything inside. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes passed to the root <div>. |
MNavBar
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
Header content — brand, links, toggle button. |
| Class | string? | null |
CSS classes applied to the <header> element. |
| Style | string? | null |
Inline style applied to the <header> element. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes passed through to <header>. |
MNavToggle
| Parameter | Type | Default | Description |
|---|---|---|---|
| Target | MNavTarget | Sidebar |
Menu, Sidebar, or Both. Controls which nav panel is toggled. |
| ChildContent | RenderFragment? | null |
Button content — typically a hamburger SVG icon. |
| Class | string? | null |
CSS classes applied to the <button>. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes (e.g. aria-label). |
MNavMenu
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
Menu links or content. Always in DOM; visibility controlled via data-state. |
| Class | string? | null |
CSS classes applied to the <nav id="m-nav-menu"> element. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes. |
MNavSidebar
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
Drawer content — nav links, sections, footer. Always in DOM. |
| FocusOnOpen | bool | true |
When true, the sidebar receives focus when it opens. |
| Class | string? | null |
CSS classes applied to the <aside id="m-nav-sidebar"> element. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes. |
MNavOverlay
| Parameter | Type | Default | Description |
|---|---|---|---|
| Class | string? | null |
CSS classes applied to the overlay <div>. Only rendered when a nav panel is open. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes. |
MNavTarget Enum
| Value | Description |
|---|---|
| Menu | Toggles the MNavMenu panel. |
| Sidebar | Toggles the MNavSidebar drawer. |
| Both | If either is open, closes all. Otherwise opens the sidebar. |