{"version":"v1.0","client_state":{"DocsNavigation.default":{"fields":{"query":""},"persist":"memory","send_to_server":true},"ColorScheme.default":{"fields":{"mode":"system"},"persist":"local","send_to_server":true}},"endpoints":{"action":"/_bretzel/action","sse":"/_bretzel/sse","refetch":"/_bretzel/refetch"},"csrf":"6999825d6e736356709ca1f9ba2cf48b","page_id":"339d3a4122ca49b5a1c2d94a90bb6d3c","address":""}

Scrolling

A site page scrolls whole. A tool does not: its sidebar stays, its header stays, and it is the central area that moves. Bretzel does both, and the choice is made once, at the shell.

The default: the document scrolls

Nothing to write. A page grows, the browser scrolls. It is the model of most sites, and of the majority of the repository's examples.
@layout
def coque() -> None:
    with ui.vstack(gap="md", classes="p-8"):
        ui.heading("Mon app")
        ui.outlet()

The other: the document is frozen

`ui.viewport` is a full-screen frame, out of the flow: IT does not scroll. `ui.pane` is a region that takes the remaining space and scrolls on its own. That is exactly what this documentation's shell does.
@layout
def shell() -> None:
    with ui.viewport():          # does NOT scroll
        with ui.sidebar(collapsible="rail"):
            ...                  # stays in place
        with ui.pane(padding="lg"):
            ui.outlet()          # scrolls, on its own
`ui.viewport` is a `Flex`: it accepts `direction`, `gap`, `align`, `justify`. By default its children are in a row — sidebar on the left, panel on the right.

Which one to pick

IfThen
a content page one reads top to bottomthe default — the document scrolls
a sidebar or a header that must stay visible`ui.viewport` + `ui.pane`
two lists side by side that scroll independently`ui.viewport` + two `ui.pane`
one hesitatesthe default. Freezing the document is a commitment: everything that overflows must live in a region

What freezing the document implies

The main trap
Everything that overflows must be INSIDE a scrolling region. Content placed directly in the `ui.viewport` is cut off, with no scrollbar to catch it — the frame does not scroll, that is its definition.
What surprises you when scripting
`document.body.scrollHeight` is 0 in this model, and `window.scrollTo` does nothing: it is not the document that carries the scrolling. A script — or a test — that wants to scroll must target the region. Measured while writing this documentation's probes, where the instrument first returned 0 px of total height.

This page is an example of it

The left bar does not move when this text scrolls — it is a `ui.viewport` with a `ui.sidebar` and a `ui.pane`. The playground too. The simpler demonstration apps, for their part, let the document scroll.