pub fn Layout(props: LayoutProps) -> impl IntoViewExpand description
Application shell with optional overlay header and side navigation.
Compose with LayoutHeader, LayoutSidebar, and LayoutMain slots. Pair overlay_header=true with a Fixed or Sticky AppBar in the header slot.
§When to use
- Full-page application shells with header, optional side nav, and scrollable main content - Pinned sticky frost headers where page content scrolls beneath the bar - Opaque fixed headers where content and scrollbar start below the bar (inner scroll) - Inline header layouts without overlay chrome
§Usage
- Set
overlay_header=truewhen the header uses Sticky or Fixed [AppBar]. 2. Use Sticky [AppBar] with frost material for the default pinned window-scroll shell. 3. Setmain_inset_scroll=truefor opaque fixed bars with inner main scroll below the bar. 4. Matchheader_insetdensity to the [AppBar] density tier. 5. Place navigation inLayoutSidebarand page content inLayoutMain. 6.LayoutSidebarstays pinned below the bar; only the page scrolls in pinned mode.
§Scroll modes
| Mode | Props | When |
|---|---|---|
| Inline header | default | Header scrolls with page content |
| Overlay pinned | overlay_header=true, sticky frost [AppBar] | Content scrolls beneath the bar |
| Overlay inset | overlay_header=true, main_inset_scroll=true, opaque fixed [AppBar] | Main scrolls below the bar inside the shell |
§Best Practices
§Do’s
- Use [
MaterialElevation::Flat] on shell chrome — borders separate regions, not shadows * Flat shell chrome in overlay layouts shares the layout canvas surface (Background3), not card fills * Use Sticky [AppBar] with Frost or Shell [AppBarMaterial] for pinned shells * Pair [Navigation] withSolid+FlatinLayoutSidebarfor co-planar side nav * Usemain_inset_scroll=truewith Solid opaque fixed bars in bounded containers * Keepheader_insetin sync with [AppBar] density * Put long scrolling content inLayoutMain, not the layout root * ReserveResting/Raisedelevation for in-content cards and callouts, not shell regions
§Don’ts
- Do not use Fixed AppBar with pinned overlay — use Sticky so the header row stays in document flow * Do not add manual top padding in main when the header sits above the body row * Do not use elevated Material tiers on AppBar or Navigation — use Flat with auto border separators
§Examples
§Inline header
Header and main in normal document flow—the default stacked shell.
use crate::{DemoBox, Layout, LayoutHeader, LayoutMain, Title3};
view! {
<div data-testid="layout-preview" style="height: 200px; border: 1px solid var(--orb-color-border-subtle);">
<Layout>
<LayoutHeader slot>
<DemoBox data_testid="layout-header-demo"><Title3>"Workspace"</Title3></DemoBox>
</LayoutHeader>
<LayoutMain slot>
<DemoBox fill=true data_testid="layout-main-demo">"Main content"</DemoBox>
</LayoutMain>
</Layout>
</div>
}§With sidebar
Side navigation beside the primary content column.
use crate::{DemoBox, Layout, LayoutMain, LayoutSidebar};
view! {
<div data-testid="layout-with-sidebar" style="height: 200px; border: 1px solid var(--orb-color-border-subtle);">
<Layout>
<LayoutSidebar slot>
<DemoBox fill=true data_testid="layout-sidebar-demo">"Nav"</DemoBox>
</LayoutSidebar>
<LayoutMain slot>
<DemoBox fill=true data_testid="layout-main-demo">"Main column"</DemoBox>
</LayoutMain>
</Layout>
</div>
}§Overlay pinned header
Sticky frost header—content starts below the bar and scrolls beneath it via window scroll.
use crate::{
AppBar, AppBarLeading, AppBarMaterial, AppBarPosition, DemoBox, Layout, LayoutHeader, LayoutMain,
MaterialCorners, MaterialElevation, MaterialVariant, Title3,
};
view! {
<div data-testid="layout-overlay-header" style="height: 400px; border: 1px solid var(--orb-color-border-subtle); overflow: auto;">
<Layout overlay_header=true page_scrollport=false>
<LayoutHeader slot>
<AppBar position=AppBarPosition::Sticky>
<AppBarMaterial variant=MaterialVariant::Frost elevation=MaterialElevation::Flat corners=MaterialCorners::Square slot />
<AppBarLeading slot><Title3>"Overlay shell"</Title3></AppBarLeading>
</AppBar>
</LayoutHeader>
<LayoutMain slot>
<DemoBox height="1200px" data_testid="layout-scroll-content">
<p>"First line starts below the bar."</p>
<p>"Scroll to see content pass under the frosted header."</p>
</DemoBox>
</LayoutMain>
</Layout>
</div>
}§Overlay inset header
Opaque fixed header—content and scrollbar start below the bar with no scroll-under.
use crate::{
AppBar, AppBarLeading, AppBarMaterial, AppBarPosition, DemoBox, Layout, LayoutHeader, LayoutMain,
MaterialCorners, MaterialElevation, MaterialVariant, Title3,
};
view! {
<div data-testid="layout-inset-header" style="height: 240px; border: 1px solid var(--orb-color-border-subtle); overflow: hidden;">
<Layout overlay_header=true main_inset_scroll=true>
<LayoutHeader slot>
<AppBar position=AppBarPosition::Fixed>
<AppBarMaterial variant=MaterialVariant::Solid elevation=MaterialElevation::Raised corners=MaterialCorners::Square slot />
<AppBarLeading slot><Title3>"Inset shell"</Title3></AppBarLeading>
</AppBar>
</LayoutHeader>
<LayoutMain slot>
<DemoBox height="400px" data_testid="layout-inset-content">
<p>"First line starts below the bar."</p>
<p>"Content stays below the opaque header when scrolling."</p>
</DemoBox>
</LayoutMain>
</Layout>
</div>
}§App shell
Header, sidebar, and main—the catalog-style compound shell.
use crate::{
AppBar, AppBarLeading, AppBarMaterial, AppBarPosition, AppBarTrailing, Button,
ButtonAppearance, DemoBox, Layout, LayoutHeader, LayoutMain, LayoutSidebar, MaterialCorners,
MaterialElevation, MaterialVariant, Navigation, NavigationBody, NavigationConfig, NavigationItem,
Title3,
};
let selected = RwSignal::new(None::<String>);
let open = RwSignal::new(vec![] as Vec<String>);
view! {
<div data-testid="layout-app-shell" style="height: 260px; border: 1px solid var(--orb-color-border-subtle); overflow: auto;">
<Layout overlay_header=true page_scrollport=false>
<LayoutHeader slot>
<AppBar position=AppBarPosition::Sticky>
<AppBarMaterial variant=MaterialVariant::Frost elevation=MaterialElevation::Flat corners=MaterialCorners::Square slot />
<AppBarLeading slot><Title3>"Orbital Components"</Title3></AppBarLeading>
<AppBarTrailing slot>
<Button appearance=ButtonAppearance::Transparent icon=icondata::AiBulbOutlined />
</AppBarTrailing>
</AppBar>
</LayoutHeader>
<LayoutSidebar slot>
<Navigation config=NavigationConfig::new().with_selected_value(selected).with_open_categories(open)>
<NavigationBody slot>
<NavigationItem config="card" icon=icondata::AiAppstoreOutlined>"Card"</NavigationItem>
</NavigationBody>
</Navigation>
</LayoutSidebar>
<LayoutMain slot>
<DemoBox fill=true data_testid="layout-main-demo">"Preview outlet"</DemoBox>
</LayoutMain>
</Layout>
</div>
}§Sidebar toggle
Coordinated sidebar open state via [LayoutSidebarToggle].
use crate::{
AppBar, AppBarLeading, AppBarMaterial, AppBarPosition, DemoBox, Layout, LayoutHeader, LayoutMain,
LayoutSidebar, LayoutSidebarToggle, MaterialCorners, MaterialElevation, MaterialVariant,
Navigation, NavigationBody, NavigationConfig, NavigationItem, Title3,
};
let sidebar_open = RwSignal::new(true);
let selected = RwSignal::new(None::<String>);
let open = RwSignal::new(vec![] as Vec<String>);
view! {
<div data-testid="layout-sidebar-toggle" style="height: 240px; border: 1px solid var(--orb-color-border-subtle); overflow: hidden;">
<Layout overlay_header=true sidebar_open=sidebar_open>
<LayoutHeader slot>
<AppBar position=AppBarPosition::Sticky>
<AppBarMaterial variant=MaterialVariant::Solid elevation=MaterialElevation::Flat corners=MaterialCorners::Square slot />
<AppBarLeading slot>
<LayoutSidebarToggle />
<Title3>"Shell"</Title3>
</AppBarLeading>
</AppBar>
</LayoutHeader>
<LayoutSidebar slot>
<Navigation config=NavigationConfig::new().with_open(Signal::derive(move || sidebar_open.get())).with_selected_value(selected).with_open_categories(open)>
<NavigationBody slot>
<NavigationItem config="home" icon=icondata::AiHomeOutlined>"Home"</NavigationItem>
</NavigationBody>
</Navigation>
</LayoutSidebar>
<LayoutMain slot>
<DemoBox fill=true data_testid="layout-main-demo">"Main content"</DemoBox>
</LayoutMain>
</Layout>
</div>
}§Sidebar closed
Initial closed rail with expand affordance in the header.
use crate::{
AppBar, AppBarLeading, AppBarMaterial, AppBarPosition, DemoBox, Layout, LayoutHeader, LayoutMain,
LayoutSidebar, LayoutSidebarToggle, MaterialCorners, MaterialElevation, MaterialVariant,
Navigation, NavigationBody, NavigationConfig, NavigationItem, Title3,
};
let sidebar_open = RwSignal::new(false);
let selected = RwSignal::new(None::<String>);
let open = RwSignal::new(vec![] as Vec<String>);
view! {
<div data-testid="layout-sidebar-closed" style="height: 240px; border: 1px solid var(--orb-color-border-subtle); overflow: hidden;">
<Layout overlay_header=true sidebar_open=sidebar_open>
<LayoutHeader slot>
<AppBar position=AppBarPosition::Sticky>
<AppBarMaterial variant=MaterialVariant::Solid elevation=MaterialElevation::Flat corners=MaterialCorners::Square slot />
<AppBarLeading slot>
<LayoutSidebarToggle />
<Title3>"Collapsed rail"</Title3>
</AppBarLeading>
</AppBar>
</LayoutHeader>
<LayoutSidebar slot>
<Navigation config=NavigationConfig::new().with_open(Signal::derive(move || sidebar_open.get())).with_selected_value(selected).with_open_categories(open)>
<NavigationBody slot>
<NavigationItem config="home" icon=icondata::AiHomeOutlined>"Home"</NavigationItem>
</NavigationBody>
</Navigation>
</LayoutSidebar>
<LayoutMain slot>
<DemoBox fill=true data_testid="layout-main-demo">"Main content"</DemoBox>
</LayoutMain>
</Layout>
</div>
}§Optional Props
- class:
impl Into<MaybeProp<String>>- Extra CSS class names merged onto the shell root grid.
- data_testid:
impl Into<MaybeProp<String>>- Optional
data-testidfor E2E hooks on the layout root.
- Optional
- overlay_header:
bool- When true, the header overlays main content and provides
AppBarInsetcontext.
- When true, the header overlays main content and provides
- main_inset_scroll:
bool- When true with
overlay_header, main content scrolls inside an inset below the header.
- When true with
- page_scrollport:
bool- When true (default), pinned overlay shells scroll via a full-viewport
ScrollAreaso Chrome applies themed scrollbar chrome. Set false for bounded preview hosts.
- When true (default), pinned overlay shells scroll via a full-viewport
- header_inset:
AppBarDensity- Header height token used for overlay inset padding — match the [
AppBar] density.
- Header height token used for overlay inset padding — match the [
- sidebar_open:
impl Into<RwSignal<bool>>- Parent-owned sidebar open state; defaults to an internal open signal when omitted.
- layout_header:
LayoutHeader- Header slot — typically an [
AppBar] with leading and trailing regions.
- Header slot — typically an [
- layout_sidebar:
LayoutSidebar- Sidebar slot — typically a [
Navigation] rail beside main content.
- Sidebar slot — typically a [
- layout_main:
LayoutMain- Main content slot — page body rendered in the scrollable region.