App shell with a dialog drawer on phones
- hidden md:flex
- <dialog>
- starting:open:
- backdrop:
- min-w-0
On a phone, tap the menu button to open the drawer.
From 768px the aside is a flex column with hidden md:flex and a fixed w-64, and the content column takes the rest with flex-1 min-w-0. On phones the same links live in a native dialog opened with showModal(), so the browser keeps focus inside it, makes the page inert, closes on Escape and returns focus to the menu button. The slide comes from -translate-x-full, open:translate-x-0 and starting:open: inside motion-safe:, and the script only opens, closes on a backdrop or link click, and keeps aria-expanded in sync.
View code
<div class="flex min-h-[560px] w-full bg-stone-50 text-stone-900">
<aside class="hidden w-64 shrink-0 flex-col border-r border-stone-200 bg-white md:flex" aria-label="Sidebar">
<div class="flex h-16 items-center px-5">
<a href="#" class="flex items-center gap-2.5 rounded-lg text-base font-semibold text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-emerald-700">
<span class="grid size-8 place-items-center rounded-lg bg-emerald-700 text-white"><svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 19c0-8 5-14 15-14 0 10-6 15-14 15M5 19l7-7"/></svg></span>
Fieldbook
</a>
</div>
<nav aria-label="Main" class="flex-1 overflow-y-auto px-3 py-2">
<ul class="space-y-1">
<li><a href="#" aria-current="page" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 11l9-7 9 7M5 9.5V20h5v-6h4v6h5V9.5"/></svg>Overview</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 20V11M12 20V5M19 20v-6"/></svg>Harvests</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z"/></svg>Fields</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 19c0-2.8-2.2-5-5-5s-5 2.2-5 5M10 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7zM16 11a3 3 0 0 0 0-6M20 19c0-2.4-1.4-4.3-3.4-4.8"/></svg>Crew</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16v14H4zM4 10h16M8 3v4M16 3v4"/></svg>Schedule</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3h7l5 5v13H7zM14 3v5h5"/></svg>Reports</a></li>
</ul>
</nav>
<div class="m-3 rounded-xl border border-emerald-100 bg-emerald-50 p-4">
<p class="text-sm font-semibold text-emerald-900">Frost risk on Thursday</p>
<p class="mt-1 text-sm text-emerald-800">Cover the north beds before 6 PM.</p>
</div>
</aside>
<dialog id="tsb-shell-drawer" aria-label="Main menu" class="m-0 h-full max-h-none w-72 max-w-[calc(100%-3rem)] bg-white p-0 text-stone-900 shadow-xl -translate-x-full open:translate-x-0 motion-safe:transition-[translate,display,overlay] motion-safe:transition-discrete motion-safe:duration-300 motion-safe:ease-out motion-safe:starting:open:-translate-x-full backdrop:bg-stone-950/0 open:backdrop:bg-stone-950/50 motion-safe:backdrop:transition-[background-color,display,overlay] motion-safe:backdrop:transition-discrete motion-safe:backdrop:duration-300 motion-safe:starting:open:backdrop:bg-stone-950/0">
<div class="flex h-full flex-col">
<div class="flex h-16 items-center justify-between px-5">
<a href="#" class="flex items-center gap-2.5 rounded-lg text-base font-semibold text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-emerald-700">
<span class="grid size-8 place-items-center rounded-lg bg-emerald-700 text-white"><svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 19c0-8 5-14 15-14 0 10-6 15-14 15M5 19l7-7"/></svg></span>
Fieldbook
</a>
<button type="button" class="tsb-shell-close -mr-2 grid size-11 place-items-center rounded-lg text-stone-500 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-emerald-700">
<span class="sr-only">Close menu</span>
<svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
</button>
</div>
<nav aria-label="Main" class="flex-1 overflow-y-auto px-3 py-2">
<ul class="space-y-1">
<li><a href="#" aria-current="page" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 11l9-7 9 7M5 9.5V20h5v-6h4v6h5V9.5"/></svg>Overview</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 20V11M12 20V5M19 20v-6"/></svg>Harvests</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z"/></svg>Fields</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 19c0-2.8-2.2-5-5-5s-5 2.2-5 5M10 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7zM16 11a3 3 0 0 0 0-6M20 19c0-2.4-1.4-4.3-3.4-4.8"/></svg>Crew</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16v14H4zM4 10h16M8 3v4M16 3v4"/></svg>Schedule</a></li>
<li><a href="#" class="group flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-700 aria-[current=page]:bg-emerald-50 aria-[current=page]:text-emerald-800"><svg class="size-5 shrink-0 text-stone-400 group-hover:text-stone-600 group-aria-[current=page]:text-emerald-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3h7l5 5v13H7zM14 3v5h5"/></svg>Reports</a></li>
</ul>
</nav>
</div>
</dialog>
<div class="flex min-w-0 flex-1 flex-col">
<header class="flex h-16 items-center gap-3 border-b border-stone-200 bg-white px-4 md:px-8">
<button type="button" id="tsb-shell-open" aria-haspopup="dialog" aria-controls="tsb-shell-drawer" aria-expanded="false" class="-ml-2 grid size-11 place-items-center rounded-lg text-stone-600 hover:bg-stone-100 hover:text-stone-900 focus-visible:outline-2 focus-visible:outline-emerald-700 md:hidden">
<span class="sr-only">Open menu</span>
<svg class="size-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
</button>
<h2 class="text-lg font-semibold">Overview</h2>
<span class="ml-auto rounded-full bg-stone-100 px-3 py-1 text-xs font-medium text-stone-600">Week 43</span>
</header>
<div class="space-y-6 p-4 md:p-8">
<div class="grid grid-cols-2 gap-3 md:grid-cols-3 md:gap-4">
<div class="rounded-xl border border-stone-200 bg-white p-4"><p class="text-sm text-stone-500">Acres harvested</p><p class="mt-1 text-2xl font-semibold">412</p></div>
<div class="rounded-xl border border-stone-200 bg-white p-4"><p class="text-sm text-stone-500">Crew on shift</p><p class="mt-1 text-2xl font-semibold">18</p></div>
<div class="col-span-2 rounded-xl border border-stone-200 bg-white p-4 md:col-span-1"><p class="text-sm text-stone-500">Bins in cold storage</p><p class="mt-1 text-2xl font-semibold">96</p></div>
</div>
<div class="rounded-xl border border-stone-200 bg-white">
<h3 class="border-b border-stone-200 px-4 py-3 text-sm font-semibold">Today</h3>
<ul class="divide-y divide-stone-100 text-sm">
<li class="flex items-center justify-between gap-3 px-4 py-3"><span>Pick block 7, Honeycrisp</span><span class="shrink-0 text-stone-500">7:30 AM</span></li>
<li class="flex items-center justify-between gap-3 px-4 py-3"><span>Irrigation check, south field</span><span class="shrink-0 text-stone-500">11:00 AM</span></li>
<li class="flex items-center justify-between gap-3 px-4 py-3"><span>Load truck for Hillside Market</span><span class="shrink-0 text-stone-500">3:15 PM</span></li>
</ul>
</div>
</div>
</div>
</div>(function () {
var dlg = document.getElementById('tsb-shell-drawer');
var openBtn = document.getElementById('tsb-shell-open');
if (!dlg || !openBtn) return;
openBtn.addEventListener('click', function () {
dlg.showModal();
dlg.querySelector('.tsb-shell-close').focus();
openBtn.setAttribute('aria-expanded', 'true');
});
dlg.addEventListener('close', function () {
openBtn.setAttribute('aria-expanded', 'false');
});
dlg.addEventListener('click', function (e) {
if (e.target === dlg) dlg.close();
});
dlg.querySelectorAll('.tsb-shell-close, a').forEach(function (el) {
el.addEventListener('click', function () { dlg.close(); });
});
window.matchMedia('(min-width: 768px)').addEventListener('change', function (e) {
if (e.matches && dlg.open) dlg.close();
});
})();