App Shell
The outer page skeleton (one CSS Grid, named areas).
Real use · sidebar + header + footer (same AppShell as below, different slots)
Content for “dashboard” — this pane scrolls independently; try collapsing the sidebar.
Filler paragraph 1 to make the main area scroll.
Filler paragraph 2 to make the main area scroll.
Filler paragraph 3 to make the main area scroll.
Filler paragraph 4 to make the main area scroll.
Filler paragraph 5 to make the main area scroll.
Filler paragraph 6 to make the main area scroll.
Filler paragraph 7 to make the main area scroll.
Filler paragraph 8 to make the main area scroll.
Filler paragraph 9 to make the main area scroll.
Filler paragraph 10 to make the main area scroll.
Filler paragraph 11 to make the main area scroll.
Filler paragraph 12 to make the main area scroll.
Same AppShell, no sidebar · header + footer only
No sidebar slot rendered — its grid column collapses to zero automatically.
Using App Shell
Silica ships one design system through three paths. They share a class vocabulary but are not interchangeable — pick the one that matches what you are building.
Plain HTML — CSS classes
The Tailwind v4 plugin. Works in any framework or none — Rails, Django, PHP, Go, a static file. This path is complete for App Shell — it needs no JavaScript, so the classes below are the whole story.
/* in your CSS */
@import "tailwindcss";
@plugin "@wizeworks/silicaui";Root class app-shell, with 4 modifiers:
.app-shell.app-shell-footer.app-shell-header.app-shell-main.app-shell-sidebar
React
Typed components over those same classes, with behavior and accessibility from Base UI.
npm i @wizeworks/silicaui-reactimport { AppShell } from "@wizeworks/silicaui-react";Source of the demo above
The exact file that renders the examples above, so it can never drift from them. It imports a few of this site’s own layout helpers (../lib/…), so read it for the component calls rather than copying it whole.
Show source
import { useState } from "react";
import {
AppShell,
AppShellSidebar,
AppShellHeader,
AppShellMain,
AppShellFooter,
SidebarProvider,
Sidebar,
SidebarHeader,
SidebarHeaderBrand,
SidebarContent,
SidebarGroup,
SidebarGroupLabel,
SidebarItem,
SidebarTrigger,
Navbar,
NavbarStart,
NavbarEnd,
Footer,
Wordmark,
Button,
} from "@wizeworks/silicaui-react";
import { Section } from "../lib/Section";
const HomeIcon = (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 12 12 3l9 9" /> <path d="M5 10v10h14V10" />
</svg>
);
const SettingsIcon = (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 17H5" /> <path d="M19 7h-9" /> <circle cx="17" cy="17" r="3" /> <circle cx="7" cy="7" r="3" />
</svg>
);
export function AppShellDemo() {
const [active, setActive] = useState("dashboard");
return (
<>
<Section title="Real use · sidebar + header + footer (same AppShell as below, different slots)">
<div className="h-[26rem] w-full max-w-2xl overflow-hidden rounded-box border border-base-300">
<SidebarProvider>
<AppShell>
<AppShellSidebar>
<Sidebar>
<SidebarHeader>
<SidebarHeaderBrand>
<Wordmark size="sm" color="primary">Acme</Wordmark>
</SidebarHeaderBrand>
<SidebarTrigger />
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
<SidebarItem
icon={HomeIcon}
active={active === "dashboard"}
onClick={() => setActive("dashboard")}
>
Dashboard
</SidebarItem>
<SidebarItem
icon={SettingsIcon}
active={active === "settings"}
onClick={() => setActive("settings")}
>
Settings
</SidebarItem>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</AppShellSidebar>
<AppShellHeader>
<Navbar className="border-b border-base-300">
<NavbarStart>
<span className="text-sm font-semibold capitalize">{active}</span>
</NavbarStart>
<NavbarEnd>
<Button size="sm" color="primary">
New
</Button>
</NavbarEnd>
</Navbar>
</AppShellHeader>
<AppShellMain className="p-6">
<p className="text-sm opacity-70">
Content for “{active}” — this pane scrolls independently; try
collapsing the sidebar.
</p>
{Array.from({ length: 12 }, (_, i) => (
<p key={i} className="mt-3 text-sm opacity-60">
Filler paragraph {i + 1} to make the main area scroll.
</p>
))}
</AppShellMain>
<AppShellFooter>
<Footer center className="border-t border-base-300 py-3 text-xs">
© 2026 Acme, Inc.
</Footer>
</AppShellFooter>
</AppShell>
</SidebarProvider>
</div>
</Section>
<Section title="Same AppShell, no sidebar · header + footer only">
<div className="h-64 w-full max-w-2xl overflow-hidden rounded-box border border-base-300">
<AppShell>
<AppShellHeader>
<Navbar className="border-b border-base-300">
<NavbarStart>
<span className="text-sm font-semibold">Acme</span>
</NavbarStart>
</Navbar>
</AppShellHeader>
<AppShellMain className="p-6">
<p className="text-sm opacity-70">
No sidebar slot rendered — its grid column collapses to zero
automatically.
</p>
</AppShellMain>
<AppShellFooter>
<Footer center className="border-t border-base-300 py-3 text-xs">
© 2026 Acme, Inc.
</Footer>
</AppShellFooter>
</AppShell>
</div>
</Section>
</>
);
}Vanilla — node tree to HTML
AppShell — lowers to <div>, holding its children. Build a node tree, project it with toHtml(), and let the zero-dependency runtime hydrate it. This is the path for generated or user-authored documents — a site builder, CMS output, a static export.
npm i @wizeworks/silicaui-html @wizeworks/silicaui-behaviorsimport { atom, toHtml } from "@wizeworks/silicaui-html";
const node = atom("AppShell", "app-shell", { /* props */ }, [/* children */]);
const markup = toHtml(node);Styling on this path is the class string in the second argument — the same classes as the Plain HTML section above. There is no color prop here; write "app-shell app-shell-primary" rather than passing a colour as a prop.
- Takes children
- yes
- Hydrated by
- nothing — this one is static markup
The full API for App Shell, on every path, is available through the Silica MCP server.