Skip to content

Layout

golit.layout arranges the reactive view fragments into a page. References (View, Control, Controls) point at nodes by id; containers (Row, Stack, Grid, Tabs, Section, Sidebar) nest. Assign a tree to app.layout. See the Page layout tutorial.

Page-level layout — golit.layout.

By default Golit stacks every view vertically under one controls panel. A layout lets you arrange the reactive view fragments into columns, tabs, a sidebar, etc. Crucially, each view stays its own <section id=…>: the layout is just static scaffold placed around the sections, so the POST out-of-band and SSE swaps still target each view by id — moving a control re-renders only its fragment, wherever it sits in the page.

from golit import layout as L

app.layout = L.Sidebar(
    L.Controls(),                       # all inputs, in the sidebar
    L.Stack(
        L.Row(L.View("kpi"), L.View("status")),
        L.Tabs({"Chart": L.View("chart"), "Data": L.View("table")}),
    ),
)

References (View/Control) point at nodes by id; containers (Row, Stack, Grid, Tabs, Section, Sidebar, Controls) nest. A plain string child is passed through as trusted HTML, so golit.ui components drop in as static decoration.

View

View(node_id: str)

Place a view node's fragment (its live <section id=…>).

Source code in python/golit/layout.py
def __init__(self, node_id: str) -> None:
    self.node_id = node_id

node_id instance-attribute

node_id = node_id

Control

Control(input_id: str)

Place a single input widget's control.

Source code in python/golit/layout.py
def __init__(self, input_id: str) -> None:
    self.input_id = input_id

input_id instance-attribute

input_id = input_id

Controls

Controls(*input_ids: str, columns: int | None = None)

A panel of input controls — the given ids, or every input if none given. columns fixes the grid column count; by default it flows with the viewport, or collapses to a single column inside a :class:Sidebar.

Source code in python/golit/layout.py
def __init__(self, *input_ids: str, columns: int | None = None) -> None:
    self.input_ids = input_ids
    self.columns = columns

input_ids instance-attribute

input_ids = input_ids

columns instance-attribute

columns = columns

Row

Row(
    *children: Any,
    widths: list[int] | None = None,
    gap: int = 6,
)

A responsive row. widths (summing to 12) gives a custom split.

Source code in python/golit/layout.py
def __init__(self, *children: Any, widths: list[int] | None = None, gap: int = 6) -> None:
    self.children = children
    self.widths = widths
    self.gap = gap

children instance-attribute

children = children

widths instance-attribute

widths = widths

gap instance-attribute

gap = gap

Stack

Stack(*children: Any, gap: int = 8)

A vertical stack with spacing.

Source code in python/golit/layout.py
def __init__(self, *children: Any, gap: int = 8) -> None:
    self.children = children
    self.gap = gap

children instance-attribute

children = children

gap instance-attribute

gap = gap

Grid

Grid(*children: Any, cols: int = 3, gap: int = 6)

A fixed-column responsive grid.

Source code in python/golit/layout.py
def __init__(self, *children: Any, cols: int = 3, gap: int = 6) -> None:
    self.children = children
    self.cols = cols
    self.gap = gap

children instance-attribute

children = children

cols instance-attribute

cols = cols

gap instance-attribute

gap = gap

Tabs

Tabs(panels: dict[str, Any], *, default: str | None = None)

A tab group; panels maps label → child.

Source code in python/golit/layout.py
def __init__(self, panels: dict[str, Any], *, default: str | None = None) -> None:
    self.panels = panels
    self.default = default

panels instance-attribute

panels = panels

default instance-attribute

default = default

Section

Section(
    *children: Any,
    title: str | None = None,
    subtitle: str | None = None,
)

A titled card grouping its children.

Source code in python/golit/layout.py
def __init__(
    self, *children: Any, title: str | None = None, subtitle: str | None = None
) -> None:
    self.children = children
    self.title = title
    self.subtitle = subtitle

children instance-attribute

children = children

title instance-attribute

title = title

subtitle instance-attribute

subtitle = subtitle

Sidebar

Sidebar(side: Any, main: Any, *, width: int = 4)

A sidebar + main two-column split. width is the sidebar span (of 12).

Source code in python/golit/layout.py
def __init__(self, side: Any, main: Any, *, width: int = 4) -> None:
    self.side = side
    self.main = main
    self.width = width

side instance-attribute

side = side

main instance-attribute

main = main

width instance-attribute

width = width

validate_layout

validate_layout(node: Any, app: Any) -> None

Check every View/Control reference points at a real view/input and appears at most once — each is a unique swap target, so a duplicate would put two elements with the same id in the DOM and break the by-id swap.

Source code in python/golit/layout.py
def validate_layout(node: Any, app: Any) -> None:
    """Check every ``View``/``Control`` reference points at a real view/input and
    appears at most once — each is a unique swap target, so a duplicate would put
    two elements with the same id in the DOM and break the by-id swap."""
    seen: set[tuple[str, str]] = set()
    for kind, ref in _iter_refs(node):
        if (kind, ref) in seen:
            label = "View" if kind == "view" else "Control"
            raise ValueError(f"layout: {label}({ref!r}) is placed more than once")
        seen.add((kind, ref))
        if kind == "view":
            ndef = app.node_defs.get(ref)
            if ndef is None:
                raise ValueError(f"layout: View({ref!r}) is not a defined node")
            if ndef.kind is not NodeKind.VIEW:
                raise ValueError(f"layout: View({ref!r}) refers to a {ndef.kind.value}, not a view")
        elif ref not in app.widgets:
            raise ValueError(f"layout: Control({ref!r}) is not a defined input")