Patterns
How the pieces go together.
The compositions worth copying: the editor shell, the responsive navigation swap, and the states a data view has to cover.
The editor shell
Top bar, context bar, and a three-column workspace. The columns follow the panes that are actually rendered, so the same component serves a one-, two-, or three-column tool.
12 layers · SVG
Guides on<!-- A pane collapses by not being rendered, so nothing inside it
stays in the tab order while it is hidden. -->
<Workspace {sidebarOpen} {inspectorOpen}>…</Workspace>Narrow viewports
Below the compact breakpoint a navigation column has nowhere to sit. Swap it for a Drawer: same content, modal presentation, focus trapped while it is open. This site
does exactly that — narrow the window and the component list moves behind Browse.
<script lang="ts">
import { breakpointQuery, createMediaQuery, Drawer, Sidebar, Workspace } from '@loidolt/theme-svelte';
const compact = createMediaQuery(breakpointQuery('compact'));
let navOpen = $state(false);
</script>
{#if compact.matches}
<Drawer title="Navigation" bind:open={navOpen}>
{#snippet trigger()}Menu{/snippet}
{@render nav()}
</Drawer>
{/if}
<Workspace sidebarOpen={!compact.matches}>
{#snippet sidebar()}<Sidebar label="Navigation">{@render nav()}</Sidebar>{/snippet}
<main>…</main>
</Workspace>Data states
A list has four states, not one: loading, empty, populated, and error. Skeleton covers the first, EmptyState the second, and Alert the last. The table cannot
count your rows, so decide the empty case where the data is.
{#snippet nothingYet()}
<EmptyState title="No cut files yet" description="Import a drawing to get started." />
{/snippet}
<Table caption="Cut files" columns={3} empty={rows.length === 0 ? nothingYet : undefined}>
…
</Table>