Fill the viewport with one internal scroll region
By default an App grows with its content and the whole page scrolls. For
an app-like layout — a fixed header and/or footer with a single scrolling body
that fills the remaining height — you want the body to scroll, not the page.
Two pieces do it:
scrollWholePage="false"on theApp— the page no longer scrolls as a whole.height="*"on the child that should fill the leftover space — it takes all the height not used by the header/footer and scrolls within itself.
<App layout="vertical-sticky" scrollWholePage="false">
<AppHeader>
<Heading level="h4" value="Full-height layout" />
</AppHeader>
<List
height="*"
data="{Array.from({ length: 60 }).map((_, i) => ({ id: i, text: 'Row ' + i }))}">
<property name="itemTemplate">
<Card paddingVertical="$space-2">
<Text value="{$item.text}" />
</Card>
</property>
</List>
<Footer>
<Text value="The header and footer stay put; only the list scrolls." />
</Footer>
</App>Fixed header + footer, scrolling body
<App layout="vertical-sticky" scrollWholePage="false">
<AppHeader>
<Heading level="h4" value="Full-height layout" />
</AppHeader>
<List
height="*"
data="{Array.from({ length: 60 }).map((_, i) => ({ id: i, text: 'Row ' + i }))}">
<property name="itemTemplate">
<Card paddingVertical="$space-2">
<Text value="{$item.text}" />
</Card>
</property>
</List>
<Footer>
<Text value="The header and footer stay put; only the list scrolls." />
</Footer>
</App>The height="*" child becomes the sole scroller, so the page never grows
and the header/footer never scroll away.
If the scrolling body isn't a List (arbitrary content, a form, a diff),
wrap it in a bounded region so it — not the page — scrolls:
<VStack height="*" overflowY="auto">
<!-- long content -->
</VStack>Remove the default page padding (so content sits flush under the header)
by overriding the paddingVertical-Pages theme variable for the content:
<Theme paddingVertical-Pages="0">
<Pages> ... </Pages>
</Theme>Notes:
- The
height="*""fill the remaining space" behavior flows through the layout, so aList(or aheight="*"wrapper) inside aPagefills the viewport under a stickyAppHeader/Footer. - Keep to one scroller in the region. Nesting a scrolling child inside a
scrolling parent produces the classic double-scrollbar: pick the single
element that should scroll and give it
height="*"(or a boundedmaxHeight), and let everything above/below it be fixed. - Pair this with Keep a list scrolled to the newest item for a chat/log that fills the viewport and follows the bottom.