← HaTchi-MaXchi

SaaS app shell

The modern SaaS/admin frame: persistent left navigation, a sticky top bar with the collapse toggle, and a ROUTED main workspace — nav links swap only the main slot, so the shell, sidebar state, and scroll survive every navigation. Collapse persists via a cookie the server reads, so first paint is always correct.

Composed of: app-shell · stack · auto-grid · card · button

Open full page ↗
Page source — the whole page is the snippet
html
<div class="app-shell" data-sidebar="open">
  <aside class="app-sidebar" id="app-sidebar">
    <div class="sidebar">
      <div class="sidebar-brand"><span class="sidebar-brand-text">Acme Ops</span></div>
      <nav class="sidebar-nav" aria-label="Primary">
        <ul class="sidebar-nav-list">
          <li><a class="sidebar-nav-link" aria-current="page" href="#" hx-get="/mock/shell/dashboard" hx-target="#main-content" hx-swap="innerHTML"><span class="sidebar-nav-icon"><svg class="icon" aria-hidden="true"><use href="#i-layout-dashboard"/></svg></span><span class="sidebar-nav-label">Dashboard</span></a></li>
          <li><a class="sidebar-nav-link" href="#" hx-get="/mock/shell/invoices" hx-target="#main-content" hx-swap="innerHTML"><span class="sidebar-nav-icon"><svg class="icon" aria-hidden="true"><use href="#i-receipt"/></svg></span><span class="sidebar-nav-label">Invoices</span></a></li>
          <li><a class="sidebar-nav-link" href="#"><span class="sidebar-nav-icon"><svg class="icon" aria-hidden="true"><use href="#i-users"/></svg></span><span class="sidebar-nav-label">Customers</span></a></li>
        </ul>
      </nav>
      <div class="sidebar-footer">
        <div class="sidebar-user-block"><span class="sidebar-user-name">Ada Lovelace</span></div>
      </div>
    </div>
  </aside>
  <div class="app-content">
    <header class="app-header">
      <div class="topbar">
        <div class="topbar-leading"><button type="button" class="sidebar-toggle" data-sidebar-toggle aria-expanded="true" aria-controls="app-sidebar" aria-label="Toggle navigation"><span class="sidebar-toggle__icon" aria-hidden="true"></span></button></div>
        <div class="topbar-title"><span class="topbar-title-text">Acme Ops</span></div>
        <div class="topbar-trailing"><button type="button" class="button" data-variant="primary">New invoice</button></div>
      </div>
    </header>
    <main class="app-main" id="main-content">
      <div class="stack" data-gap="md">
        <h1>Dashboard</h1>
        <div class="auto-grid" style="--grid-min: 10rem">
          <div class="card card-body">
            <div class="card-label">Outstanding</div>
            <div class="card-value">£12,450</div>
          </div>
          <div class="card card-body">
            <div class="card-label">Paid</div>
            <div class="card-value">£48,900</div>
          </div>
        </div>
      </div>
    </main>
  </div>
</div>
Agent Implementation Guidance
Routing is one attribute per link: hx-get the page fragment, hx-target="#main-content" — the server returns only the workspace body, and everything else (shell, sidebar collapse state, focus, scroll) persists. In Dazzle this is the workspace pattern: every example app's views render inside this shell. The collapse toggle writes the dz_sidebar cookie so the SERVER renders data-sidebar correctly on the next full page load — state-in-DOM at SSR, no client hydration flash.