App Shell
A responsive application frame with optional header, footer, and independently collapsible sidebars.App Shell provides the stable frame around an application: optional header and footer regions, plus independently controlled left and right sidebars around a scrollable main area.
Variants and behaviour
Pass only the regions the product needs. Each sidebar is optional and can be controlled through its bindable open state, which makes responsive menus and user-controlled panels straightforward.
Customization
Place your own navigation, utilities, and content in the named snippets. The block uses the library’s layout and colour tokens, so it can be re-themed through the same CSS custom properties as the rest of Fractalsvelte.
Examples
Installation
Install Fractalsvelte, then import the block from the dedicated blocks entry point.
Install
pnpm add fractalsvelteImport
<script lang="ts">
import { AppShell } from 'fractalsvelte/blocks';
</script>Usage
Usage
<script lang="ts">
import { AppShell } from 'fractalsvelte/blocks';
</script>
<AppShell>
<p>Your application content.</p>
</AppShell>Props
| Prop | Type | Default | Description |
|---|---|---|---|
header | Snippet | - | Optional header region. |
footer | Snippet | - | Optional footer region. |
leftSidebar | Snippet | - | Optional left navigation region. |
rightSidebar | Snippet | - | Optional right utility region. |
leftOpen | boolean | true | Whether the left sidebar is visible. |
rightOpen | boolean | true | Whether the right sidebar is visible. |
Source
Block source is published with the package and can be inspected here.
AppShell.svelte
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props { header?: Snippet; footer?: Snippet; leftSidebar?: Snippet; rightSidebar?: Snippet; leftOpen?: boolean; rightOpen?: boolean; children: Snippet; }
let { header, footer, leftSidebar, rightSidebar, leftOpen = $bindable(true), rightOpen = $bindable(true), children }: Props = $props();
</script>
<div class="app-shell" data-left={Boolean(leftSidebar) && leftOpen} data-right={Boolean(rightSidebar) && rightOpen}>
{#if header}<header class="app-shell-header">{@render header()}</header>{/if}
{#if leftSidebar}<aside class="app-shell-left">{@render leftSidebar()}</aside>{/if}
<main class="app-shell-main">{@render children()}</main>
{#if rightSidebar}<aside class="app-shell-right">{@render rightSidebar()}</aside>{/if}
{#if footer}<footer class="app-shell-footer">{@render footer()}</footer>{/if}
</div>
<style lang="sass">
.app-shell
display: grid
grid-template-areas: 'header header header' 'left main right' 'footer footer footer'
grid-template-columns: 0 minmax(0, 1fr) 0
min-height: 100%
transition: grid-template-columns 200ms ease
&[data-left='true']
grid-template-columns: minmax(200px, 280px) minmax(0, 1fr) 0
&[data-right='true']
grid-template-columns: 0 minmax(0, 1fr) minmax(200px, 280px)
&[data-left='true'][data-right='true']
grid-template-columns: minmax(200px, 280px) minmax(0, 1fr) minmax(200px, 280px)
.app-shell-header, .app-shell-footer
background: var(--color00)
border-color: var(--border-primary)
border-style: solid
padding: 16px
.app-shell-header
border-width: 0 0 1px
grid-area: header
.app-shell-footer
border-width: 1px 0 0
grid-area: footer
.app-shell-left, .app-shell-right
background: var(--color00)
overflow: auto
padding: 16px
.app-shell-left
border-right: 1px solid var(--border-primary)
grid-area: left
.app-shell-right
border-left: 1px solid var(--border-primary)
grid-area: right
.app-shell-main
grid-area: main
min-width: 0
padding: 16px
@media (max-width: 720px)
.app-shell, .app-shell[data-left='true'], .app-shell[data-right='true'], .app-shell[data-left='true'][data-right='true']
grid-template-areas: 'header' 'main' 'footer'
grid-template-columns: minmax(0, 1fr)
.app-shell-left, .app-shell-right
display: none
@media (prefers-reduced-motion: reduce)
.app-shell
transition: none
</style>
