Skip to content

flexbox

The flexbox component arranges children of a 3D primitive in a one-dimensional row or column, similar to CSS Flexbox. It works on any primitive with a defined width and height (a-plane, a-box, a-entity with a geometry) and positions its children inside the container's bounding box.

The Flexbox family consists of three components that work together:

  • flexbox – the container that lays out its children along a main axis.
  • flex-grow – marks a child as flexible so it expands into the remaining space.
  • flex-col – gives a child a width expressed as a fraction of a 12-column grid, with values per breakpoint based on the container's size.

Basic usage

A flexbox container distributes its children along the main axis (direction) and aligns them along the cross axis (items). The example below shows three boxes in a row, evenly spaced and vertically centred inside the container.

Direction

direction: row lays children horizontally (main axis = X). direction: column lays them vertically (main axis = Y). justify always controls the main axis and items always controls the cross axis, so swapping direction swaps which property reads as "horizontal".

Main-axis alignment (justify)

justify controls how items are positioned and spaced along the main axis. The five accepted values mirror their CSS counterparts (note the shorter between / around spelling). between places items flush to the container edges with even gaps in between; around surrounds each item with an equal amount of free space (edge gaps end up half the size of the gaps between items).

The five rows below share the same three child boxes — only justify changes.

Cross-axis alignment (items)

items controls where children sit along the cross axis. The composite below puts three sub-containers side by side, each holding the same three items of varying height so the difference between start, center, and end reads at a glance.

Spacing (gap)

gap is a vec2 value. The first number adds spacing between items along the main axis; the second adds spacing between wrapped lines along the cross axis. Both are expressed in metres. A single-row layout only uses the first component; the second only matters when wrap: true.

The example shows both at once — a single-row container on the left (only gap.x) and a wrapping container on the right (both gap.x and gap.y).

Wrapping

With wrap: true, items that don't fit on the current main-axis line continue on the next one. Combine with gap to control spacing both within and between lines.

The four mini containers below show the most common variants together:

  • top-left — equal items wrap onto multiple rows (direction: row);
  • top-right — items of varying widths still wrap greedily, row by row;
  • bottom-leftdirection: column; wrap: true; flows items top-to-bottom and into a second column;
  • bottom-rightjustify: between applies per line, so each wrapped row distributes its items to the container edges.

Growing

Add the flex-grow attribute to a child to let it expand and consume the remaining main-axis space. Use this to build, for example, a toolbar with a fixed icon on each side and a stretchable centre region. In the example below, green items have flex-grow applied. When multiple children carry flex-grow, the leftover space is divided equally among them (bottom row).

Combining flex-grow with flex-col

When flex-grow sits on a child that also has flex-col, the grow item claims all unused columns on its line of the 12-column grid. Below, the two outer items take 3 columns each (sm: 3) and the middle item — also declared as sm: 3 but with flex-grow — expands to fill the remaining 6.

Responsive grid

flex-col gives a child a width expressed as a fraction of a 12-column grid. You can declare a different column count per breakpoint, and the component picks the largest matching one based on the container's 3D width (see Breakpoints below).

Note: column widths are computed from the container's width without subtracting any gap, so a row where the column counts add up to exactly 12 will overflow and wrap as soon as you add a non-zero gap.x. Use gap: 0 (or smaller column counts) when you want the items to fit on one row.

Basic 12-column layouts

Three common splits, stacked top-to-bottom: thirds (sm: 4, 4 + 4 + 4 = 12), halves (sm: 6, 6 + 6 = 12), and an over-12 case (sm: 5, 5 + 5 + 5 = 15) where the third item wraps because the next column count would overflow.

Responsive across breakpoints

Each of the three containers below holds the same children with flex-col="sm: 12; md: 6; lg: 4". As container width crosses the breakpoints, the same markup reflows from a stack into a half-grid into a one-row layout.

The 3 m container falls below the md threshold (4 m), so each child uses its sm value of 12 columns and they stack. The 6 m container matches md, so each child takes 6 columns (two per row, third wraps). The 8 m container matches lg, so each child takes 4 columns and all three fit on one row.

Custom breakpoints

The named breakpoints (sm, md, lg, xl, 2xl, 3xl) use fixed default thresholds, but you can override those thresholds on the container with customBreakpoints. It accepts 1–6 numbers (metres) that are assigned to the named breakpoints in order: the first to sm, the second to md, and so on. Children keep using the named breakpoints in flex-col.

Below the container sets customBreakpoints: 0 1 1.7, so sm = 0 m, md = 1 m and lg = 1.7 m. At the 2 m container width the lg threshold applies, so each child takes its lg value of 4 columns and all three fit on one row.

If the same container were 1.2 m wide, the md threshold (1 m) would apply (6 columns each, two per row); below 1 m the sm threshold (0 m) would stack them at 12 columns each.

flexbox props

PropertyTypeDefaultDescription
directionenum(row, column)rowMain layout axis. row is horizontal, column is vertical.
justifyenum(start, end, center, between, around)startDistribution along the main axis.
itemsenum(start, end, center)startAlignment along the cross axis.
wrapbooleanfalseIf true, items that don't fit on the main axis continue on the next line.
gapvec20 0Spacing between items in metres. First value = main-axis gap, second = cross-axis (line) gap.
customBreakpointsarray (1–6 numbers)[]Overrides the metre thresholds of the named breakpoints (sm md lg xl 2xl 3xl) in order, e.g. customBreakpoints: 0 1 1.7.

flex-grow props

PropertyTypeDefaultDescription
booleantrueWhen present (or set to true), the item expands into the free main-axis space.

flex-col props

Each property declares the column count (out of 12) to use at the corresponding breakpoint.

PropertyTypeDefaultDescription
smnumberColumns when the container is at least the sm width.
mdnumberColumns when the container is at least the md width.
lgnumberColumns when the container is at least the lg width.
xlnumberColumns when the container is at least the xl width.
2xlnumberColumns when the container is at least the 2xl width.
3xlnumberColumns when the container is at least the 3xl width.

Only breakpoints you set are considered; the component picks the largest one whose threshold is less than or equal to the parent container's 3D width.

Breakpoints

Breakpoints are based on the parent container's width in metres, not on the browser viewport. The default thresholds are:

BreakpointContainer width
sm≥ 0 m
md≥ 4 m
lg≥ 7 m
xl≥ 10 m
2xl≥ 12 m
3xl≥ 15 m

You can override these thresholds per container with the customBreakpoints property on the flexbox component. It takes 1–6 numbers assigned to sm, md, lg, xl, 2xl, 3xl in order. For example, flexbox="… ; customBreakpoints: 3 6" makes sm apply above 3 m and md above 6 m, so flex-col="sm: 12; md: 6" yields 12 columns above 3 m and 6 columns above 6 m. See Custom breakpoints above.

Notes

  • The flexbox container must be a primitive with explicit width and height (e.g. a-plane, a-box, or an a-entity with a geometry). Children with no bounding box are placed using a 0.1 m fallback size.
  • gap adds spacing without changing item sizes. The second value only matters when wrap: true.
  • flex-col widths are calculated from the raw container width (gaps are not subtracted), so set gap: 0 when the column counts on a row already add up to exactly 12.
  • flex-grow works in both row and column directions and recomputes when the container resizes.
  • With wrap: true, justify applies per line — useful when laying out grid cells with flex-col.
  • For predictable responsive layouts, combine flex-col with wrap: true so wrapped lines flow naturally as the container width crosses breakpoints.