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.

CategoryBlocks
TypeWeb Component (<mv-app-shell>)
Statusbeta
Also installsbadge, card, chart, command, dialog, popover, sidebar, tooltip, table
Keywordsdashboard, app-shell, layout, admin, kpi, header, command-palette

When to use

Avoid when

Install

node scripts/add.mjs app-shell --out ./src/marvelous

AI 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

NameTypeDefaultDescription
hotkeystringkKey combined with Ctrl/⌘ to open search (“/” also opens it outside text fields); empty to disable.

Methods

NameDescription
openSearch()Opens the search palette.

Events

NameDescription
mv-navigateCommand picked in the palette (which then closes). detail: { value, label }.

Content structure

NameDescription
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

NameDescription
mv-app-shell-header / -actions / -searchSticky blurred header, right-hand action area, fake search field (icon only in narrow containers).
mv-app-shell-main / -heading / -title / -subtitle / -toolbarContent (mv-app-shell query container), page title and actions.
mv-app-shell-stats / -stat / -stat-head / -stat-value / -stat-foot1 → 2 → 4 column grid (34 / 56rem) of KPI tiles.
mv-app-shell-row2/3 + 1/3 row above 56rem.
mv-app-shell-notifs / -notifNotification list inside a popover.
mv-app-shell-breakdown / -person / -card-headBar 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.