Skip to main content

Layout

Function Layout 

Source
pub fn Layout(props: LayoutProps) -> impl IntoView
Expand 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

  1. Set overlay_header=true when the header uses Sticky or Fixed [AppBar]. 2. Use Sticky [AppBar] with frost material for the default pinned window-scroll shell. 3. Set main_inset_scroll=true for opaque fixed bars with inner main scroll below the bar. 4. Match header_inset density to the [AppBar] density tier. 5. Place navigation in LayoutSidebar and page content in LayoutMain. 6. LayoutSidebar stays pinned below the bar; only the page scrolls in pinned mode.

§Scroll modes

ModePropsWhen
Inline headerdefaultHeader scrolls with page content
Overlay pinnedoverlay_header=true, sticky frost [AppBar]Content scrolls beneath the bar
Overlay insetoverlay_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] with Solid + Flat in LayoutSidebar for co-planar side nav * Use main_inset_scroll=true with Solid opaque fixed bars in bounded containers * Keep header_inset in sync with [AppBar] density * Put long scrolling content in LayoutMain, not the layout root * Reserve Resting / Raised elevation 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>
}

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

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-testid for E2E hooks on the layout root.
  • overlay_header: bool
    • When true, the header overlays main content and provides AppBarInset context.
  • main_inset_scroll: bool
    • When true with overlay_header, main content scrolls inside an inset below the header.
  • page_scrollport: bool
    • When true (default), pinned overlay shells scroll via a full-viewport ScrollArea so Chrome applies themed scrollbar chrome. Set false for bounded preview hosts.
  • header_inset: AppBarDensity
    • Header height token used for overlay inset padding — match the [AppBar] density.
  • 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.
  • layout_sidebar: LayoutSidebar
    • Sidebar slot — typically a [Navigation] rail beside main content.
  • layout_main: LayoutMain
    • Main content slot — page body rendered in the scrollable region.