beta
App Shell — <mv-app-shell>
Complete dashboard block: collapsible sidebar, sticky header (breadcrumb, Ctrl/⌘ + K search, unread notifications, account), KPI tiles with sparklines, a sales chart by period and an activity table — layout driven by container queries.
| Category | Blocks |
|---|---|
| Type | Web Component (<mv-app-shell>) |
| Status | beta |
| Also installs | badge, card, chart, command, dialog, popover, sidebar, tooltip, table |
| Keywords | dashboard, app-shell, layout, admin, kpi, header, command-palette |
When to use
- An admin or SaaS dashboard needs a ready layout with sidebar, sticky header, KPI tiles, a chart and an activity table
- Users expect a Ctrl/Cmd + K search palette, breadcrumbs and unread notifications in the app header
- A back-office prototype must adapt to its container width without separate mobile templates
Avoid when
- Only the collapsible side navigation is needed inside an existing layout → use Sidebar instead
- The page is a public marketing site rather than a logged-in app → use Navbar instead
Install
node scripts/add.mjs app-shell --out ./src/marvelousAI agent with the Marvelous UI MCP server: install_components({ slugs: ["app-shell"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, core/observe.js, core/position.js, components/app-shell/app-shell.js, components/app-shell/app-shell.css, components/badge/badge.css, components/card/card.css, components/chart/chart.js, components/chart/chart.css, components/command/command.js, components/command/command.css, components/dialog/dialog.js, components/dialog/dialog.css, core/focus.js, components/popover/popover.js, components/popover/popover.css, components/sidebar/sidebar.js, components/sidebar/sidebar.css, components/tooltip/tooltip.js, components/tooltip/tooltip.css, components/table/table.css, components/table/table.js.
Usage
Canonical markup — start from it and customize with attributes, data-* and CSS variables:
<mv-app-shell id="as-demo">
<div class="mv-sidebar-layout" data-variant="inset" style="height:780px;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);overflow:hidden">
<mv-sidebar label="Main navigation" storage-key="mv-demo-app-shell">
<div class="mv-sidebar-header">
<mv-menu placement="bottom-start">
<button class="mv-sidebar-item" data-size="lg" type="button">
<span class="mv-sidebar-logo" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19V5l8 7 8-7v14"/></svg></span>
<span class="mv-sidebar-label"><strong>Nova Supply</strong><small>Online store</small></span>
<svg class="mv-sidebar-item-end" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 15 5 5 5-5M7 9l5-5 5 5"/></svg>
</button>
<div data-content style="--mv-menu-width:15rem">
<div class="mv-menu-label">Workspaces</div>
<div role="group" data-name="workspace">
<button role="menuitemradio" aria-checked="true" value="nova">Nova Supply</button>
<button role="menuitemradio" value="lumen">Studio Lumen</button>
</div>
<hr>
<button role="menuitem" value="new-workspace">New workspace…</button>
</div>
</mv-menu>
</div>
<nav class="mv-sidebar-content" aria-label="Sections">
<div class="mv-sidebar-group">
<h2 class="mv-sidebar-group-label">Store</h2>
<ul class="mv-sidebar-menu">
<li><a class="mv-sidebar-item" href="#as-demo" aria-current="page"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="7" height="9" rx="1"/><rect x="14" y="3" width="7" height="5" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/><rect x="3" y="16" width="7" height="5" rx="1"/></svg><span class="mv-sidebar-label">Dashboard</span></a></li>
<li><a class="mv-sidebar-item" href="#as-demo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><path d="M3 6h18M16 10a4 4 0 0 1-8 0"/></svg><span class="mv-sidebar-label">Orders</span><span class="mv-sidebar-badge" data-variant="accent" aria-label="8 to process">8</span></a></li>
<li><a class="mv-sidebar-item" href="#as-demo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7.5 4.27 9 5.15M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="m3.3 7 8.7 5 8.7-5M12 22V12"/></svg><span class="mv-sidebar-label">Products</span></a></li>
<li><a class="mv-sidebar-item" href="#as-demo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></svg><span class="mv-sidebar-label">Customers</span></a></li>
<li class="mv-sidebar-sub">
<button class="mv-sidebar-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v18h18"/><path d="M18 17V9M13 17V5M8 17v-3"/></svg><span class="mv-sidebar-label">Reports</span></button>
<ul class="mv-sidebar-submenu">
<li><a class="mv-sidebar-subitem" href="#as-demo">Sales</a></li>
<li><a class="mv-sidebar-subitem" href="#as-demo">Traffic</a></li>
<li><a class="mv-sidebar-subitem" href="#as-demo">Retention</a></li>
</ul>
</li>
<li><a class="mv-sidebar-item" href="#as-demo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/></svg><span class="mv-sidebar-label">Marketing</span></a></li>
</ul>
</div>
<div class="mv-sidebar-group" data-push>
<ul class="mv-sidebar-menu">
<li><a class="mv-sidebar-item" href="#as-demo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 7h-9M14 17H5"/><circle cx="17" cy="17" r="3"/><circle cx="7" cy="7" r="3"/></svg><span class="mv-sidebar-label">Settings</span></a></li>
<li><a class="mv-sidebar-item" href="#as-demo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3M12 17h.01"/></svg><span class="mv-sidebar-label">Help</span></a></li>
</ul>
</div>
</nav>
<div class="mv-sidebar-footer">
<mv-menu placement="right-start">
<button class="mv-sidebar-item" data-size="lg" type="button">
<span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:200" aria-hidden="true"><span class="mv-avatar-fallback">CR</span></span>
<span class="mv-sidebar-label"><strong>Chloe Reed</strong><small>Admin</small></span>
<svg class="mv-sidebar-item-end" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 15 5 5 5-5M7 9l5-5 5 5"/></svg>
</button>
<div data-content style="--mv-menu-width:14rem">
<button role="menuitem" value="profile">Profile</button>
<button role="menuitem" value="billing">Billing</button>
<hr>
<button role="menuitem" value="logout">Log out</button>
</div>
</mv-menu>
</div>
</mv-sidebar>
<div class="mv-sidebar-inset">
<header class="mv-app-shell-header">
<button class="mv-button" data-variant="ghost" data-size="icon" data-mv-sidebar-toggle aria-label="Toggle sidebar" title="Sidebar (Ctrl B)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v18"/></svg>
</button>
<div class="mv-separator" role="separator" aria-orientation="vertical" data-orientation="vertical"></div>
<nav class="mv-breadcrumb" aria-label="Breadcrumb" data-size="sm">
<ol>
<li><a href="#as-demo">Nova Supply</a></li>
<li><span aria-current="page">Dashboard</span></li>
</ol>
</nav>
<div class="mv-app-shell-actions">
<mv-dialog data-app-shell-search>
<button class="mv-app-shell-search" type="button" data-mv-open aria-label="Search (Ctrl K)" aria-keyshortcuts="Control+K Meta+K">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<span>Search…</span>
<kbd class="mv-kbd-group"><kbd class="mv-kbd" data-size="sm">Ctrl</kbd><kbd class="mv-kbd" data-size="sm">K</kbd></kbd>
</button>
<dialog class="mv-command-dialog" aria-label="Search">
<mv-command label="Search Nova Supply">
<input placeholder="Search orders, customers, pages…">
<div class="mv-command-list">
<div class="mv-command-group" data-label="Pages">
<div class="mv-command-item" data-value="orders" data-keywords="sales carts">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><path d="M3 6h18"/></svg>
Orders <span class="mv-command-item-meta">8 to process</span>
</div>
<div class="mv-command-item" data-value="customers" data-keywords="contacts buyers">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="7" r="4"/><path d="M2 21v-2a4 4 0 0 1 4-4h6a4 4 0 0 1 4 4v2"/></svg>
Customers
</div>
<div class="mv-command-item" data-value="reports" data-keywords="analytics sales traffic">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v18h18"/><path d="M18 17V9M13 17V5M8 17v-3"/></svg>
Sales reports
</div>
</div>
<div class="mv-command-group" data-label="Actions">
<div class="mv-command-item" data-value="new-product" data-keywords="add create item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
New product <span class="mv-command-shortcut"><kbd>N</kbd></span>
</div>
<div class="mv-command-item" data-value="export" data-keywords="csv download">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
Export this month’s sales
</div>
</div>
</div>
<div class="mv-command-empty">No results.</div>
</mv-command>
</dialog>
</mv-dialog>
<mv-popover placement="bottom-end">
<button class="mv-button mv-badge-anchor" data-variant="ghost" data-size="icon" data-app-shell-unread-label="Notifications" aria-label="Notifications">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>
<span class="mv-badge" data-variant="danger" data-appearance="solid" data-shape="pill" data-size="sm" data-position="top-right" style="--mv-badge-offset:4px" data-app-shell-unread aria-hidden="true">3</span>
</button>
<div data-content style="--mv-popover-width:22rem">
<div class="mv-app-shell-notifs">
<div class="mv-app-shell-notifs-head">
<h3>Notifications</h3>
<button class="mv-button" data-variant="ghost" data-size="sm" data-app-shell-read-all>Mark all as read</button>
</div>
<ul>
<li><a class="mv-app-shell-notif" href="#as-demo" data-unread><span class="mv-avatar" data-size="sm" data-variant="tinted" style="--mv-avatar-hue:40" aria-hidden="true"><span class="mv-avatar-fallback">JD</span></span><span><p><strong>James Davis</strong> placed an order for $214.90</p><time datetime="2026-09-23T10:42">4 min ago</time></span></a></li>
<li><a class="mv-app-shell-notif" href="#as-demo" data-unread><span class="mv-avatar" data-size="sm" data-variant="tinted" style="--mv-avatar-hue:160" aria-hidden="true"><span class="mv-avatar-fallback">MC</span></span><span><p><strong>Mateo Cruz</strong> left a 5-star review on “Eclipse Lamp”</p><time datetime="2026-09-23T09:15">1 hr ago</time></span></a></li>
<li><a class="mv-app-shell-notif" href="#as-demo" data-unread><span class="mv-avatar" data-size="sm" data-variant="tinted" style="--mv-avatar-hue:20" aria-hidden="true"><span class="mv-avatar-fallback">!</span></span><span><p><strong>Low stock:</strong> only 3 “Opal Vase” left</p><time datetime="2026-09-23T08:02">2 hr ago</time></span></a></li>
<li><a class="mv-app-shell-notif" href="#as-demo"><span class="mv-avatar" data-size="sm" data-variant="tinted" style="--mv-avatar-hue:280" aria-hidden="true"><span class="mv-avatar-fallback">IM</span></span><span><p><strong>Imani Moore</strong> joined the team</p><time datetime="2026-09-22T17:30">Yesterday</time></span></a></li>
</ul>
<div class="mv-app-shell-notifs-foot"><a href="#as-demo">View all notifications</a></div>
</div>
</div>
</mv-popover>
<mv-menu placement="bottom-end">
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="My account" style="border-radius:var(--mv-radius-full)">
<span class="mv-avatar" data-size="sm" data-variant="tinted" style="--mv-avatar-hue:200" aria-hidden="true"><span class="mv-avatar-fallback">CR</span></span>
</button>
<div data-content style="--mv-menu-width:14rem">
<div class="mv-menu-label">[email protected]</div>
<button role="menuitem" value="profile">Profile</button>
<button role="menuitem" value="settings">Settings <span class="mv-menu-shortcut">⌘,</span></button>
<hr>
<button role="menuitem" value="logout">Log out</button>
</div>
</mv-menu>
</div>
</header>
<main class="mv-app-shell-main" aria-labelledby="as-demo-title">
<div class="mv-app-shell-heading">
<div>
<h1 class="mv-app-shell-title" id="as-demo-title">Dashboard</h1>
<p class="mv-app-shell-subtitle">Hi Chloe, here’s how the store did in September.</p>
</div>
<div class="mv-app-shell-toolbar">
<button class="mv-button" data-variant="outline" data-size="sm">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
Export
</button>
<button class="mv-button" data-size="sm">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
New product
</button>
</div>
</div>
<section class="mv-app-shell-stats" aria-label="Key metrics">
<article class="mv-card mv-app-shell-stat">
<div class="mv-app-shell-stat-head"><span>Revenue</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 10h12M4 14h9M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2"/></svg></div>
<div class="mv-app-shell-stat-value">$48,290</div>
<div class="mv-app-shell-stat-foot"><span class="mv-badge" data-variant="success" data-size="sm">▲ 12.4%</span> vs Aug</div>
<mv-chart sparkline type="area" label="Revenue, last 12 weeks" format="currency" data='{"labels":["W27","W28","W29","W30","W31","W32","W33","W34","W35","W36","W37","W38"],"series":[{"name":"Revenue","values":[8.1,8.9,8.4,9.6,9.1,10.2,9.8,10.9,11.4,10.8,11.9,12.6]}]}'></mv-chart>
</article>
<article class="mv-card mv-app-shell-stat">
<div class="mv-app-shell-stat-head"><span>New customers</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="7" r="4"/><path d="M2 21v-2a4 4 0 0 1 4-4h6a4 4 0 0 1 4 4v2M19 8v6M16 11h6"/></svg></div>
<div class="mv-app-shell-stat-value">1,284</div>
<div class="mv-app-shell-stat-foot"><span class="mv-badge" data-variant="success" data-size="sm">▲ 8.1%</span> vs Aug</div>
<mv-chart sparkline type="area" label="New customers, last 12 weeks" data='{"labels":["W27","W28","W29","W30","W31","W32","W33","W34","W35","W36","W37","W38"],"series":[{"name":"Customers","values":[240,262,251,270,288,276,301,297,312,305,322,331]}]}'></mv-chart>
</article>
<article class="mv-card mv-app-shell-stat">
<div class="mv-app-shell-stat-head"><span>Orders</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><path d="M3 6h18M16 10a4 4 0 0 1-8 0"/></svg></div>
<div class="mv-app-shell-stat-value">3,912</div>
<div class="mv-app-shell-stat-foot"><span class="mv-badge" data-variant="danger" data-size="sm">▼ 2.3%</span> vs Aug</div>
<mv-chart sparkline type="area" label="Orders, last 12 weeks" data='{"labels":["W27","W28","W29","W30","W31","W32","W33","W34","W35","W36","W37","W38"],"series":[{"name":"Orders","values":[1010,1042,998,1065,1031,1002,987,1019,972,961,948,936]}]}'></mv-chart>
</article>
<article class="mv-card mv-app-shell-stat">
<div class="mv-app-shell-stat-head"><span>Conversion rate</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 7-8.5 8.5-5-5L2 17"/><path d="M16 7h6v6"/></svg></div>
<div class="mv-app-shell-stat-value">3.42%</div>
<div class="mv-app-shell-stat-foot"><span class="mv-badge" data-variant="success" data-size="sm">▲ 0.4 pt</span> vs Aug</div>
<mv-chart sparkline type="area" label="Conversion rate, last 12 weeks" format="percent" data='{"labels":["W27","W28","W29","W30","W31","W32","W33","W34","W35","W36","W37","W38"],"series":[{"name":"Conversion","values":[2.9,3.0,2.95,3.1,3.05,3.2,3.15,3.28,3.3,3.25,3.38,3.42]}]}'></mv-chart>
</article>
</section>
<div class="mv-app-shell-row">
<article class="mv-card">
<header class="mv-card-header mv-app-shell-card-head">
<div>
<h2 class="mv-card-title">Sales</h2>
<p class="mv-card-description" id="as-demo-range-desc">Last 30 days, in USD</p>
</div>
<mv-tabs data-variant="segmented" data-size="sm" value="30" id="as-demo-range">
<div role="tablist" aria-label="Period">
<button role="tab" data-value="7">7d</button>
<button role="tab" data-value="30">30d</button>
<button role="tab" data-value="90">3 mo</button>
</div>
</mv-tabs>
</header>
<div class="mv-card-content">
<mv-chart id="as-demo-chart" type="area" format="currency" height="250" label="Daily sales"></mv-chart>
</div>
</article>
<article class="mv-card">
<header class="mv-card-header">
<h2 class="mv-card-title">Traffic sources</h2>
<p class="mv-card-description">24,812 visits this month</p>
</header>
<div class="mv-card-content">
<ul class="mv-app-shell-breakdown">
<li><span>Organic search</span><strong>42%<small>10,421</small></strong><progress class="mv-progress" data-size="sm" max="100" value="42" aria-label="Organic search, 42%"></progress></li>
<li><span>Direct</span><strong>27%<small>6,699</small></strong><progress class="mv-progress" data-size="sm" max="100" value="27" aria-label="Direct, 27%"></progress></li>
<li><span>Social media</span><strong>18%<small>4,466</small></strong><progress class="mv-progress" data-size="sm" max="100" value="18" aria-label="Social media, 18%"></progress></li>
<li><span>Newsletter</span><strong>13%<small>3,226</small></strong><progress class="mv-progress" data-size="sm" max="100" value="13" aria-label="Newsletter, 13%"></progress></li>
</ul>
</div>
</article>
</div>
<article class="mv-card">
<header class="mv-card-header">
<h2 class="mv-card-title">Recent orders</h2>
<p class="mv-card-description">5 of 3,912 orders this month</p>
<div class="mv-card-action"><a class="mv-button" data-variant="outline" data-size="sm" href="#as-demo">View all</a></div>
</header>
<div class="mv-card-content">
<div class="mv-table-wrap">
<table class="mv-table" data-hover>
<thead>
<tr><th scope="col">Customer</th><th scope="col" data-app-shell-hide="sm">Order</th><th scope="col">Status</th><th scope="col" data-app-shell-hide="sm">Date</th><th scope="col" data-align="end">Amount</th></tr>
</thead>
<tbody>
<tr><td><span class="mv-app-shell-person"><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:40" aria-hidden="true"><span class="mv-avatar-fallback">JD</span></span><span><strong>James Davis</strong><small>[email protected]</small></span></span></td><td data-app-shell-hide="sm">#10492</td><td><span class="mv-table-status" data-status="success">Paid</span></td><td data-app-shell-hide="sm">Sep 23</td><td data-align="end">$214.90</td></tr>
<tr><td><span class="mv-app-shell-person"><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:160" aria-hidden="true"><span class="mv-avatar-fallback">MC</span></span><span><strong>Mateo Cruz</strong><small>[email protected]</small></span></span></td><td data-app-shell-hide="sm">#10491</td><td><span class="mv-table-status" data-status="info">Shipped</span></td><td data-app-shell-hide="sm">Sep 23</td><td data-align="end">$89.00</td></tr>
<tr><td><span class="mv-app-shell-person"><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:320" aria-hidden="true"><span class="mv-avatar-fallback">SN</span></span><span><strong>Sofia Nguyen</strong><small>[email protected]</small></span></span></td><td data-app-shell-hide="sm">#10490</td><td><span class="mv-table-status" data-status="warning">Pending</span></td><td data-app-shell-hide="sm">Sep 22</td><td data-align="end">$1,249.00</td></tr>
<tr><td><span class="mv-app-shell-person"><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:240" aria-hidden="true"><span class="mv-avatar-fallback">TG</span></span><span><strong>Tomás García</strong><small>[email protected]</small></span></span></td><td data-app-shell-hide="sm">#10489</td><td><span class="mv-table-status" data-status="danger">Refunded</span></td><td data-app-shell-hide="sm">Sep 22</td><td data-align="end">$64.50</td></tr>
<tr><td><span class="mv-app-shell-person"><span class="mv-avatar" data-variant="tinted" style="--mv-avatar-hue:100" aria-hidden="true"><span class="mv-avatar-fallback">LM</span></span><span><strong>Leila Mansour</strong><small>[email protected]</small></span></span></td><td data-app-shell-hide="sm">#10488</td><td><span class="mv-table-status" data-status="success">Paid</span></td><td data-app-shell-hide="sm">Sep 21</td><td data-align="end">$372.00</td></tr>
</tbody>
</table>
</div>
</div>
</article>
</main>
</div>
</div>
</mv-app-shell>
<script type="module">
const shell = document.getElementById("as-demo");
const chart = shell?.querySelector("#as-demo-chart");
const desc = shell?.querySelector("#as-demo-range-desc");
// Deterministic pseudo-random series so the demo is stable between reloads.
const series = (n, seed, base) => {
let s = seed;
const rnd = () => ((s = (s * 16807) % 2147483647) / 2147483647);
return Array.from({ length: n }, (_, i) => Math.round(base + i * base * 0.012 + (rnd() - 0.4) * base * 0.45 + Math.sin(i / 2.2) * base * 0.12));
};
const day = (offset) => new Date(Date.UTC(2026, 8, 23 - offset)).toLocaleDateString("en-US", { day: "numeric", month: "short", timeZone: "UTC" });
const ranges = {
7: { labels: Array.from({ length: 7 }, (_, i) => day(6 - i)), cur: series(7, 11, 1650), prev: series(7, 5, 1420), text: "Last 7 days, in USD" },
30: { labels: Array.from({ length: 30 }, (_, i) => day(29 - i)), cur: series(30, 7, 1450), prev: series(30, 3, 1300), text: "Last 30 days, in USD" },
90: { labels: Array.from({ length: 13 }, (_, i) => `W${26 + i}`), cur: series(13, 9, 10200), prev: series(13, 2, 9100), text: "Last 13 weeks, in USD" },
};
const show = (key) => {
const r = ranges[key];
if (!chart || !r) return;
chart.data = { labels: r.labels, series: [{ name: "This period", values: r.cur }, { name: "Previous period", values: r.prev }] };
if (desc) desc.textContent = r.text;
};
show("30");
shell?.querySelector("#as-demo-range")?.addEventListener("mv-change", (e) => show(e.detail.value));
</script>API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
hotkey | string | k | Key combined with Ctrl/⌘ to open search (“/” also opens it outside text fields); empty to disable. |
Methods
| Name | Description |
|---|---|
openSearch() | Opens the search palette. |
Events
| Name | Description |
|---|---|
mv-navigate | Command picked in the palette (which then closes). detail: { value, label }. |
Content structure
| Name | Description |
|---|---|
mv-dialog[data-app-shell-search] | Search palette (a .mv-command-dialog dialog containing an <mv-command>). |
[data-unread] | Unread notification (dot); clicking marks it as read. |
[data-app-shell-read-all] | “Mark all as read” button. |
[data-app-shell-unread] | Unread counter, hidden at 0. |
[data-app-shell-unread-label] | On the bell button: its aria-label becomes “<value>, N unread”. |
CSS classes
| Name | Description |
|---|---|
mv-app-shell-header / -actions / -search | Sticky blurred header, right-hand action area, fake search field (icon only in narrow containers). |
mv-app-shell-main / -heading / -title / -subtitle / -toolbar | Content (mv-app-shell query container), page title and actions. |
mv-app-shell-stats / -stat / -stat-head / -stat-value / -stat-foot | 1 → 2 → 4 column grid (34 / 56rem) of KPI tiles. |
mv-app-shell-row | 2/3 + 1/3 row above 56rem. |
mv-app-shell-notifs / -notif | Notification list inside a popover. |
mv-app-shell-breakdown / -person / -card-head | Bar breakdown, customer cell (avatar + name), card header with actions. |
[data-app-shell-hide="sm"] | Column hidden below a 40rem container. |
Accessibility
Landmarks: navigation region, <main> named by the page title. Search: button with aria-keyshortcuts, modal dialog (focus trap, Esc). Bell: aria-label updated with the unread count. KPIs: named section; accessible charts (data table). Tables with column headers.