Skip to main content

AppShell

Info:Shell
22

The three-row application scaffold: toolbar over a left/main/right body over a status bar.

layout scaffold chrome shell application frame workspace

Live demo

live · @xtyle/astro

AppShell

A full three-row scaffold — Toolbar · Dock + main · Statusbar

Both rails are resizable: drag either inner edge (or focus the handle and use the arrow keys), and double-click to reset. Tab into the frame to reveal the skip link.

Skip to main content
xtyle
Success:Live

Theme preview

The main region scrolls independently between the fixed chrome.

Background

#0b0d12

Foreground

#e6e9ef

Accent

#6ea8fe

xtyle-default OKLCH 299 tokens 100% coverage

AppShell is the outermost layout frame for a full-screen application. It establishes a three-row grid (a top toolbar, a flexible body, and a bottom status bar) where the body is itself a three-column grid of a left rail, a scrollable main column, and a right rail.

Every region is an optional named slot, so the same scaffold collapses cleanly from a full IDE-style layout down to a bare main column. The main region is a real <main> landmark that owns the only scroll, keeping the chrome pinned. An optional skip link, hidden until focused, lets keyboard users jump straight past the chrome to the content. It carries no chrome of its own: the Astro and HTML bindings emit the same light-DOM structure, and the custom element is a transparent display: contents host that contributes nothing to the layout.

When to use

How this component composes with the rest of the set.

Fill the toolbar slot with Toolbar and the statusbar slot with Statusbar for the matching chrome.
Use Panel in the left / right slots for collapsible rails.
Mount one AppShell per application; nest layout components inside the main slot rather than nesting shells.

Props

7 props, straight from the manifest.

PropTypeDefaultBindingsDescription
skipLink string | boolean
html svelte astro
Renders a skip link targeting the main region. `true` uses the default label; a string overrides it. The Astro binding also accepts a `skip-link` slot for richer content.
leftSize number | string
html svelte astro
Width of the left rail column. A bare number is treated as px; a string passes through (`18rem`, `20%`). Omit to size the rail to its content.
rightSize number | string
html svelte astro
Width of the right rail column, same rules as `leftSize`. When `rightResizable` is set this is the rail's starting width and its double-click reset target.
leftResizable boolean false
html svelte astro
Makes the left rail user-resizable: a drag handle on the rail's inner edge, arrow-key nudges (`Shift` for a larger step, `Home`/`End` to jump to the bounds), and a double-click reset to `leftSize`. The main column reflows live as the rail changes. The element emits `resize` / `resize-end` events with the side and size.
rightResizable boolean false
html svelte astro
Makes the right rail user-resizable, mirroring `leftResizable` with a double-click reset to `rightSize`.
leftMin / leftMax number 160 / 720
html svelte astro
Clamp, in px, for the resizable left rail (`left-min` / `left-max`).
rightMin / rightMax number 160 / 720
html svelte astro
Clamp, in px, for the resizable right rail (`right-min` / `right-max`).

Appearance

States

main-focus

.xtyle-main:focus-visible

The main region after the skip link moves focus to it; an inset ring marks the landing.

skip-link-focus

.xtyle-app__skip-link:focus-visible

The skip link revealed on keyboard focus, sliding into view with the standard ring.

Anatomy

The named parts that make up the component, with their selectors.

app

.xtyle-app

The outer three-row grid filling the viewport, carrying the body background and base typography.

--body-bg --fg-0 --font-sans --text-body --leading-normal

body

.xtyle-app__body

The middle row split into the left rail, the main column, and the right rail.

main

.xtyle-main

The scrollable <main> landmark and skip-link target; takes the remaining space.

--space-5

rail-handle

.xtyle-app__resizer

The drag handle on a resizable rail's inner edge (when leftResizable / rightResizable is set); a hairline that thickens to the accent on hover, keyboard focus, and drag.

--line --accent --border-thin --border-normal --ring --duration-fast --ease-standard

skip-link

.xtyle-app__skip-link

The keyboard-only jump link, off-screen until focused, sliding into the top-left when it is.

--font-sans --text-sm --weight-medium --leading-tight --accent --accent-fg --border-thin --radius-md --space-2 --space-3 --space-4 --duration-fast --ease-standard

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 22/22 consumed tokens produced default register: 299 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --accent-fg --body-bg --border-normal --border-thick --border-thin --duration-fast --ease-standard --fg-0 --font-sans --leading-normal --leading-tight --line --radius-md --ring --space-2 --space-3 --space-4 --space-5 --text-body --text-sm --weight-medium

Slots

default
html svelte astro

The main content, rendered inside the scrollable <main> landmark.

toolbar
html svelte astro

The top row; typically a Toolbar with the app title and global actions.

left
html svelte astro

The left rail; typically navigation.

right
html svelte astro

The right rail; typically contextual detail.

statusbar
html svelte astro

The bottom row; typically a Statusbar.

skip-link
astro

Custom content for the skip link (Astro only); falls back to a default label otherwise.

Accessibility

The main content is a native <main> landmark, so assistive tech can jump to it directly.
The optional skip link is the first focusable element and targets #main (which carries tabindex="-1"), letting keyboard users bypass the chrome.
The skip link is positioned off-screen and only slides into view on :focus-visible, so it stays out of the visual layout until needed.
<main> receives a focus ring when the skip link moves focus to it, confirming the landing point.
The custom element host is display: contents, so it adds no box and never disturbs the landmark or grid structure.
A resizable rail's handle is a role="separator" with aria-orientation="vertical" and aria-valuenow/min/max, is keyboard-focusable, and drives the width with the arrow keys, so the rail resizes without a pointer.

Code

Full application scaffold

Toolbar, left and right rails, scrollable main, a status bar, and a skip link.

<xtyle-app-shell skip-link>
	<header slot="toolbar" class="xtyle-toolbar">App title and global actions</header>
	<nav slot="left" class="xtyle-panel">Primary navigation</nav>

	<h1>Page content</h1>
	<p>Everything in the default slot lands in the scrollable main column.</p>

	<aside slot="right" class="xtyle-panel">Contextual details</aside>
	<footer slot="statusbar" class="xtyle-statusbar">Ready</footer>
</xtyle-app-shell>
<script lang="ts">
	import { AppShell } from "@xtyle/svelte";
</script>

<AppShell skipLink>
	{#snippet toolbar()}
		<header class="xtyle-toolbar">App title and global actions</header>
	{/snippet}
	{#snippet left()}
		<nav class="xtyle-panel">Primary navigation</nav>
	{/snippet}

	<h1>Page content</h1>
	<p>Everything in the default slot lands in the scrollable main column.</p>

	{#snippet right()}
		<aside class="xtyle-panel">Contextual details</aside>
	{/snippet}
	{#snippet statusbar()}
		<footer class="xtyle-statusbar">Ready</footer>
	{/snippet}
</AppShell>
---
import { AppShell } from "@xtyle/astro";
---

<AppShell skipLink>
	<header slot="toolbar" class="xtyle-toolbar">App title and global actions</header>
	<nav slot="left" class="xtyle-panel">Primary navigation</nav>

	<h1>Page content</h1>
	<p>Everything in the default slot lands in the scrollable main column.</p>

	<aside slot="right" class="xtyle-panel">Contextual details</aside>
	<footer slot="statusbar" class="xtyle-statusbar">Ready</footer>
</AppShell>

Resizable right rail

An editor with a user-resizable inspector: drag the rail's inner edge (or arrow-key the handle) and the main column reflows; double-click to reset. leftResizable mirrors it.

<xtyle-app-shell right-size="344" right-resizable right-min="288" right-max="560">
	<header slot="toolbar" class="xtyle-toolbar">Editor</header>

	<h1>Canvas</h1>

	<!-- Drag the rail's inner edge to resize; double-click it to reset to 344. -->
	<aside slot="right" class="xtyle-panel">Inspector</aside>
</xtyle-app-shell>
<script lang="ts">
	import { AppShell } from "@xtyle/svelte";
</script>

<AppShell rightSize={344} rightResizable rightMin={288} rightMax={560}>
	{#snippet toolbar()}
		<header class="xtyle-toolbar">Editor</header>
	{/snippet}

	<h1>Canvas</h1>

	{#snippet right()}
		<aside class="xtyle-panel">Inspector</aside>
	{/snippet}
</AppShell>
---
import { AppShell } from "@xtyle/astro";
---

<AppShell rightSize={344} rightResizable rightMin={288} rightMax={560}>
	<header slot="toolbar" class="xtyle-toolbar">Editor</header>

	<h1>Canvas</h1>

	<aside slot="right" class="xtyle-panel">Inspector</aside>
</AppShell>