Learn Vortex's data-driven layout vocabulary and responsive behavior.
Layout in Vortex is data, not code: arrangement is authored as style attributes, resolved by the core, and interpreted by each platform's reconciler (Qt box layouts on desktop, CSS flexbox on web, native LinearLayout-style weights on Android). One vocabulary, three native implementations.
A Box arranges its children with these attributes:
| Attribute | Values | Default |
|---|---|---|
Layout.Direction |
Row | Column |
Column |
Layout.Spacing |
gap between children, px | 0 |
Layout.Align |
cross-axis placement: Start | Center | End | Stretch |
Center |
Layout.Justify |
main-axis placement of the child block when nothing grows: Start | Center | End |
Center |
Layout.Scroll |
Vertical | Horizontal | Both: overflow scrolls instead of clipping |
no scroll |
Direction can vary by state: the shell's window flips to Column-Reverse in @Narrow so the titlebar sits at the bottom on phones.
How a node sits inside its parent:
| Attribute | Meaning |
|---|---|
Layout.Grow |
Main-axis flex factor; 0 (default) means don't grow. |
Size.Width / Size.Height |
Fixed pixel size; unset means intrinsic content size. |
Size.Max Width |
A fill-then-cap constraint: the node expands to fill the parent's cross axis up to the cap, then shrinks with the parent below it. With the parent's default Layout.Align: Center, this yields the classic centered content column that stays comfortable on wide windows and reflows on small ones. |
Aspect Ratio |
Image/Video only: width over height (e.g. 1.7778 for 16:9). Height derives from the laid-out width, so the media keeps a proportional shape at any parent width; Size.Height is ignored while set, and Fit resolves any mismatch with the artwork's own ratio (Cover crops, Contain letterboxes). Overridable per list row via the Aspect:<field> bind. |
Layout.Shrink |
Main-axis shrink factor for the static-web emitter (0 pins a section at content size inside a scrollable page). Runtime reconcilers size scroll regions natively and ignore it. |
A titlebar is the vocabulary in one line: a Row bar whose title has Layout.Grow: 1 (pushing the controls to the far edge) over a Layout.Grow: 1 content area.
Margins.*: external space around a node, contributed by the parent's layout.Padding.*: internal space between a node's border and its content.Border/Thickness also insets content: content inset = border + padding. Border is a child layer of style data (see the styling guide), so its color is Border/Fill.Corner Radii.*: per-corner rounding.Layers name expansions keep these compact: Padding.(Bottom,Left,Right,Top): 10 sets all four sides.
A node can leave the normal flow via the Overlay attribute:
Modal: lifted out and centered over a darkening backdrop (a movable frameless dialog window on desktop; a fixed centered element on web/Android). Shown and hidden through normal visibility, so open/close rides the render diff.PinnedBottom: floated over the content, anchored to the bottom edge, and reserving space there. Used by the titlebar in @Narrow, where it slides away on scroll.BottomSheet: like PinnedBottom but reserving no space: the node overlays content and can grow upward when expanded (collapsible drawers that peek as a header).SideSheet: a detail panel floated at the right edge, full height, capped at Size.Max Width, behind a click-to-dismiss scrim; slides in and out horizontally via an @Open variant.Transform.Offset Y / Transform.Offset X: render-time offsets as a fraction of the node's own size (1.0 = fully off by its own height/width), animated by the reconcilers. These drive the hide-away titlebar and sheet slides.Transform.Rotation: degrees clockwise, purely visual (layout keeps the un-rotated slot); handy for reusing one icon in two orientations.Opacity: fades a node without reflowing; it keeps its layout slot.Visible: false: removes the node (and subtree) from the render tree entirely.Collapsed: true: keeps the node in the tree but animates its height to zero and back, so collapse/expand can tween.The core tracks the viewport and folds window states into style resolution:
@Narrow; below 900 px, "medium" (app code can also branch with app.is_narrow() / app.is_medium()).@ControlsHidden state pushes pinned bars off-screen (via Transform.Offset Y), and reverse scrolling reveals them.@Maximized (the shell drops its border and corner rounding there).On phone-class platforms (the @Mobile state), desktop-tuned pixel values are generally too small, so the core auto-scales resolved values instead of requiring @Mobile overrides on every attribute:
Size.Width/Size.Height): ×1.5Font.Size): ×1.25An explicit @Mobile value on an attribute always wins verbatim, so per-attribute exceptions still work. Unitless values (Layout.Grow, transform fractions, Font.Weight) are never scaled. The three factors are adjustable per app.