Flex Layouts
Flex containers arrange visible children automatically. Use them to build rows, columns, toolbars, sidebars, forms, and application shells that adapt to the available terminal size.
Flex, Row, and Column
Flex is the configurable base element:
local flex = frame:addFlex({
direction = "row",
})Row and Column provide convenient defaults:
local row = frame:addRow()
local column = frame:addColumn()- A Row lays out children from left to right.
- A Column lays out children from top to bottom.
- Rows default to
width = basalt.fill()andheight = basalt.auto(). - Columns default to
width = basalt.auto()andheight = basalt.fill().
Set both dimensions explicitly when the container should fill a complete area.
Flow Children
Children use position = "flow" by default. Their x and y properties are replaced by positions calculated by the flex container:
local toolbar = frame:addRow({
x = 2,
y = 2,
width = basalt.fill(),
height = basalt.auto(),
gap = 1,
})
toolbar:addButton({
width = basalt.auto(),
text = "New",
})
toolbar:addButton({
width = basalt.auto(),
text = "Save",
})The insertion order determines the flow order.
Gap and Padding
gap adds cells between flow children. padding adds equal inner spacing on all four sides:
local column = frame:addColumn({
x = 1,
y = 1,
width = basalt.fill(),
height = basalt.fill(),
gap = 1,
padding = 1,
})Padding is part of the container's own size.
Filling Remaining Space
On the main axis, fixed and intrinsic children are measured first. basalt.fill() children then share what remains:
local row = frame:addRow({
width = basalt.fill(),
height = 5,
})
row:addFrame({
width = 12,
height = basalt.fill(),
})
row:addFrame({
width = basalt.fill(),
height = basalt.fill(),
})Fill weights divide the remainder proportionally:
row:addFrame({ width = basalt.fill(1) })
row:addFrame({ width = basalt.fill(2) })The second frame receives approximately twice as much remaining width.
Cross-Axis Alignment
The container's align property controls children on the cross axis:
row.align = "center"Supported values are:
"start""center""end""stretch"
Override one child with alignSelf:
local button = row:addButton({
text = "Special",
alignSelf = "end",
})stretch expands an auto-sized child across the available cross axis.
Main-Axis Justification
justify controls how unused main-axis space is distributed:
toolbar.justify = "spaceBetween"Supported values are:
"start""center""end""spaceBetween"
spaceBetween places the extra space between children and has an effect when at least two flow children exist.
Shrinking
When content is wider or taller than the flex container, eligible children shrink without crossing their minimum size.
Fill-sized children shrink automatically. Configure a fixed child explicitly:
local sidebar = row:addFrame({
width = 18,
minWidth = 10,
shrink = 1,
})Set shrink = 0 to prevent shrinking. Multiple children distribute the deficit according to their shrink weights.
Flex overflow is currently clipped. Use a scrollable Frame when content must remain accessible beyond the viewport.
Absolute Children
An absolute child is excluded from flow:
local closeButton = column:addButton({
position = "absolute",
x = "{parent.width - 3}",
y = 1,
width = 3,
text = "X",
})This is useful for overlays and decorations. Ordinary content should remain in flow.
Complete Application Shell
local basalt = require("basalt")
local frame = basalt.getMainFrame()
frame.background = colors.black
local page = frame:addColumn({
x = 1,
y = 1,
width = basalt.fill(),
height = basalt.fill(),
gap = 1,
padding = 1,
})
local toolbar = page:addRow({
width = basalt.fill(),
height = basalt.auto(),
gap = 1,
justify = "spaceBetween",
})
toolbar:addLabel({
width = basalt.auto(),
text = "BASALT APP",
foreground = colors.orange,
})
toolbar:addButton({
width = basalt.auto(),
text = "Exit",
}):onClick(function()
basalt.stop()
end)
local content = page:addRow({
width = basalt.fill(),
height = basalt.fill(),
gap = 1,
})
local sidebar = content:addFrame({
width = 14,
minWidth = 10,
height = basalt.fill(),
shrink = 1,
background = colors.gray,
})
sidebar:addLabel({
x = 2,
y = 2,
text = "Navigation",
})
local main = content:addColumn({
width = basalt.fill(),
height = basalt.fill(),
gap = 1,
padding = 1,
background = colors.lightGray,
})
main:addLabel({
width = basalt.auto(),
text = "Dashboard",
foreground = colors.black,
})
main:addProgressBar({
width = basalt.fill(),
progress = 72,
showPercentage = true,
barColor = colors.green,
})
main:addLabel({
width = basalt.auto(),
text = "Resize the terminal to test the layout.",
foreground = colors.gray,
})
basalt.run()The outer Column creates the vertical page structure. The content Row divides the remaining area between a shrinkable sidebar and a fill-sized main area.
Common Mistakes
Setting x and y on flow children
Flex calculates their positions. Use gap, padding, align, and justify, or set position = "absolute" intentionally.
Leaving the container at its intrinsic default
Rows and Columns have convenient but different default dimensions. Set width and height when building a full application region.
Using fill without bounded parent space
A fill-sized child needs a parent that offers a meaningful numeric size.
Expecting automatic wrapping
Flex currently lays out one row or one column. It does not wrap children onto another line.
Related API
Next Steps
Connect the application shell to data with Reactive State.