Sizing

Goals

Container precedence

The parent container's layout rules determine each child's final position and size. Child settings such as width, height and weight express sizing requirements; the container interprets them as fixed sizes, preferred sizes, minimum sizes or allocation weights according to its layout policy. Container behavior takes precedence when resolving these settings.

For example, a Grid child fills its assigned cell, including an expanded span. A declared height of 60 remains a minimum rather than preventing the child from filling a taller cell. HBox and VBox instead use the linear expansion priorities, weights and cross-axis rules below; this principle does not make every child stretch or discard its size requirements.

Desired and minimum sizes

.layout(width=..., height=...) declares a desired size, not a hard maximum. A desired dimension sets that axis's automatic expansion priority to 0; the other axis keeps its own policy. A positive weight still explicitly requests expansion on the parent's allocation axis (both axes in Grid). Parent allocation, native content constraints and explicit toolkit overrides determine the final size.

.layout(minWidth=..., minHeight=...) supplies minimum-size hints without setting a desired size or lowering expansion priority. A desired size may also supply a minimum hint; the larger of the two hints wins. A minimum must never be copied back into width/height. Textual uses .tLayout(minWidth=..., minHeight=...) in terminal cells.

For example, an unweighted Canvas has priority 4 on both axes. Canvas(paint).layout(width=160) requests a compact width while its height can expand; .layout(minWidth=160) retains automatic expansion on both axes. Native padding, natural sizes and ancestor constraints can still produce different pixel sizes; Flet's parent constraints can clamp size hints.

Backend implementation approach

wx is the reference for an explicit implementation of this sizing policy: per-axis expansion priorities, competition among siblings, weighted space allocation, and cross-axis alignment. Use its layout implementation as a guide when a backend needs to implement these rules itself. The policy in this document remains the common contract.

PySide6 linear containers apply the same per-axis priorities and sibling competition, then use native QBoxLayout stretch, alignment and size policies to allocate space. Static controls keep their preferred size; only siblings with the highest main-axis priority expand. When none expand, children pack at the start rather than stretching or spreading apart. HBox centers compact children vertically; VBox left-aligns them. Native size hints, minimum sizes, fonts and themes still determine exact dimensions. Explicit .qt size-policy overrides remain available, and wrapped native widgets retain their original policy as the priority baseline. See the alignment matrix.

Responsive sizing

Responsive declarations choose effective settings before the backend applies this sizing policy. wideLayout/wideStyle and their Textual variants override ordinary settings when the parent content width reaches a threshold; they do not introduce another space-allocation algorithm. Declare each method’s breakpoints from small to large; descending thresholds log a warning without changing declaration order. Sibling priorities, weights and cross-axis alignment continue to follow the rules below.

.visible() sets shared visibility; .wideVisible() overrides it at a graphical parent-width threshold in logical pixels. .tVisible() and .tWideVisible() provide Textual boolean overrides and cell-width rules. Textual inherits the shared visibility boolean unless explicitly overridden, but never inherits pixel bounds. Hidden nodes collapse their allocated space and freeze child synchronization.

Conditions use the parent's full measured content area, not the remaining space after siblings. A native layout measurement updates State, which causes subscribed declarations to be rebuilt; identical sizes do not notify. Content-dependent parent sizing can still feed back into a breakpoint, so an externally sized parent is preferable. No arbitrary size cap is added, and adaptive layouts do not guarantee that all content fits extremely small areas.

See the Responsive / Adaptive Layout guide for API choices, declaration ordering, terminal units and examples.

Grid sizing

Grid computes expansion independently for columns (X) and rows (Y). Each track uses the highest effective priority among its visible cells, including inherited descendant demand. A spanning cell contributes to every track it covers. After accounting for content sizes, only tracks at the highest nonzero priority on that axis receive extra space. If all priorities are zero, tracks remain compact and pack at the left or top. For a Label / Label / TextField form, the first two columns fit their content and the editor column expands.

Equal-priority tracks with equal weights do not imply equal content sizes. On Web/Pyodide, these tracks keep their individual intrinsic sizes and share only the extra space equally. A content-sized nested Grid competing with an outer Spacer therefore keeps short button columns compact, including when another cell spans several columns.

A wide toolbar or nested section spanning both compact and expanding columns must satisfy its additional width through the expanding columns. It must not widen the form's label columns merely because it shares their Grid.

A Grid child's positive weight gives priority 5 on both axes; weight=0 keeps its demand at zero. A track shared by multiple cells takes their maximum priority and, among cells at that priority, their maximum weight (1 for unweighted demand). Adding more rows does not multiply a column's weight. Hidden cells do not contribute; visibility, placement, span and descendant changes recompute the track allocation. Children still fill their assigned cells, subject to native limits, and declared dimensions remain size requirements.

Web/Pyodide Grid labels keep filling their cells, with their text vertically centred and horizontally left-aligned. This aligns ordinary label/input rows without changing track allocation; multiline labels and row spans centre their text within the full assigned cell.

This policy applies to both explicit coordinates and automatic columns on PySide6, wx, Tkinter, Web, Pyodide and Textual. .columns(n) controls placement; it does not force equal-width or expanding columns. Font metrics, padding, minimum sizes and native stretch rounding can still differ by backend. Flet does not implement Grid. Run python -m PUI.examples.grid_sizing BACKEND for a three-column form with a spanning toolbar and a nested Grid beside a Spacer.

Grid children use .wideGrid() / .tWideGrid() to override base positions and spans at inclusive parent-width thresholds. Automatic Grid columns default to expandChildren=True: after placement, children expand right then down into empty cells within the existing grid bounds. wideColumns/tWideColumns can override this setting at a breakpoint; omitting it preserves the current value. Rules merge in call order without changing vnode identity. Automatic columns use effective spans but ignore explicit row/column; see Grid reference.

Cross-axis alignment

Tkinter converts explicit pixel dimensions to native character/line counts for text controls, measuring the current font/theme and native insets. Requests round up to whole native units, so the requested size can exceed the pixel value by less than one character or line (subject to the native minimum). Font and theme updates recalculate the conversion; removing an override restores the original native request. Canvas and Frame dimensions remain pixels. Axes without a native sizing option, such as Entry height, remain theme-controlled.

HBox should approximately center compact children vertically (middle), and VBox should left-align them horizontally. Children that expand on that axis can still fill the available space. This positions the widget, not the text inside it, and does not require pixel-identical output across toolkits.

Backend support and native limits are listed in the reference alignment matrix. Textual uses native group alignment, so mixed-size children do not necessarily have individual matching center lines. Explicit dimensions also retain backend-specific size-hint/minimum behavior.

Concepts

In following demos, elements come with 5px padding/gap and 3px colored border.

Case 1: Fit the content

The most compact sizing.
Static
Static
Static
Static
Static
Static
Static
Static

Case 2: Container with specified size

The outmost container is specified with a size of 150x150 px. (Positioning is not discussed here.)
Static
A Static
B Static
C Static
D Static
E Static
F Static
G Static
H Static
I Static
J Static
A Static
B Static
C Static
D Static
E Static
F Static
G Static
H Static
I Static
J Static

Case 3: Weighting

The outmost container is specified with a width of 150%.
weighted
weighted
weighted
 
weighted
weighted
weighted
weighted

Case 4: More complex cases

A Static
B Static
C Static
D Static
E Static
F Static
G Static
H Static
I Static
J Static
canvas