Shell

A two-column frame, a page padding of p-6, and one modal envelope shared by every dialog.

Frame โ€” AppShell.svelte

Four classes and one component. The rail is a fixed 208px; the main column takes the rest and owns its own scroll. min-w-0 on the main is what stops a wide table from pushing the rail off screen.

w-52 ยท bg-gray-50
min-w-0 flex-1 overflow-auto
<div class="flex min-h-screen bg-gray-100">
  <Sidebar />
  <main class="min-w-0 flex-1 overflow-auto">{@render children?.()}</main>
</div>

Page header

Title left, actions right, bottom margin 6. Group and Lobby both use it; the NDO view uses a white bar with a bottom border instead, because its tabs hang off the bottom edge.

Sensorica Lab

sensorica-lab-7f3a

<div class="mb-6 flex items-start justify-between">
  <div>
    <h1 class="text-2xl font-bold text-gray-900">{name}</h1>
    <p class="mt-1 font-mono text-sm text-gray-400">{id}</p>
  </div>
  <div class="flex items-center gap-2"><!-- actions --></div>
</div>

Profile bar โ€” LobbyProfileBar.svelte

A white strip above the lobby content, outside its p-6, so it spans the full width of the main column.

Signed in as riverstone
<div class="flex items-center justify-between border-b border-gray-200 bg-white px-6 py-2">
  <span class="text-sm text-gray-700">Signed in as <span class="font-semibold text-gray-900">{nickname}</span></span>
  <button class="rounded px-2 py-1 text-xs font-medium text-blue-600 hover:bg-blue-50">Edit</button>
</div>

Modal envelope

Every dialog in the app shares this: a blurred 40% black backdrop, a 12px-radius white card, a hairline-divided header and footer. Only max-w varies โ€” sm for a choice, md for a form, lg for the NDO create form.

Create NDO

Register a new NondominiumIdentity Layer 0 within this group.

Body scrolls at max-h-[70vh].

<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
  <div class="relative w-full max-w-lg rounded-xl border border-gray-200 bg-white shadow-xl" role="dialog" aria-modal="true">
    <div class="border-b border-gray-100 px-6 py-4"><!-- title + subtitle --></div>
    <div class="max-h-[70vh] overflow-y-auto px-6 py-4 space-y-4"><!-- body --></div>
    <div class="flex justify-end gap-2 border-t border-gray-100 px-6 py-4"><!-- cancel + primary --></div>
  </div>
</div>

Drift worth fixing

The lobby profile modal is a native <dialog> driven by a Melt builder, with its backdrop in a ::backdrop rule. Every other modal is a fixed div with a bg-black/40 overlay. They look nearly identical and behave differently: only the dialog traps focus and closes on Escape. Compare them in the prototype.