Skip to main content
xriptxr

App Shell

Info:Shell
24

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.

resize-end: waiting for a rail to settle

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 310 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

10 props, straight from the manifest.

PropTypeDefaultBindingsDescription
skipLink html: skip-link 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.
mainId html: main-id string "main"
html svelte astro
The id given to the <main> region, and the anchor the skip link jumps to (main-id). Rename it when a page holds more than one shell, or when something else on the page already owns main.
leftSize html: left-size 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 html: right-size 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 html: left-resizable 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 html: right-resizable boolean false
html svelte astro
Makes the right rail user-resizable, mirroring leftResizable with a double-click reset to rightSize.
leftMin html: left-min number 160
html svelte astro
Lower clamp, in px, for the resizable left rail (left-min).
leftMax html: left-max number 720
html svelte astro
Upper clamp, in px, for the resizable left rail (left-max).
rightMin html: right-min number 160
html svelte astro
Lower clamp, in px, for the resizable right rail (right-min).
rightMax html: right-max number 720
html svelte astro
Upper clamp, in px, for the resizable right rail (right-max).

Events

What the component emits, and what rides along on event.detail. The name in the first column is the one addEventListener takes.

EventDetailBindingsDescription
resize { side, size }
html svelte astro
A resizable rail moved, continuous, one per pointer move.
resize-end svelte: onresizeend { side, size }
html svelte astro
A rail settled: a drag released, or a keyboard nudge, Home/End, or a double-click reset committed. The keyboard routes emit only this one, never resize.

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. A ::part() handle is reachable from your own stylesheet; the class beside it is the component's internal selector, which a shadow boundary keeps to itself.

app

Success:::part(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

internal .xtyle-app__body

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

main

Success:::part(main) .xtyle-main

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

--space-5

rail-handle

internal .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

Success:::part(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 24/24 consumed tokens produced default register: 310 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 --layer-chrome --layer-skip --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>

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>