Layout
Rect is both a shape and a flex layout container. Set group to arrange its children automatically.
Layout modes
group | Behavior |
|---|---|
'row' | Children placed side by side (left to right) |
'column' | Children stacked top to bottom |
'stack' | Children overlapping, centered by default |
Row
import { createScene, Rect } from '@motion-script/core';
export default createScene(function* (stage) {
stage.add(
<Rect group="row" gap={24} padding={32} fill="#1e293b" cornerRadius={16}>
<Rect width={80} height={80} fill="tomato" cornerRadius={8} />
<Rect width={80} height={80} fill="royalblue" cornerRadius={8} />
<Rect width={80} height={80} fill="seagreen" cornerRadius={8} />
</Rect>
);
});
Column
stage.add(
<Rect group="column" gap={16} padding={24} fill="#1e293b" cornerRadius={12}>
<Rect width="fill" height={60} fill="royalblue" cornerRadius={8} />
<Rect width="fill" height={60} fill="tomato" cornerRadius={8} />
<Rect width="fill" height={60} fill="seagreen" cornerRadius={8} />
</Rect>
);
Stack
Children are layered in z-order, centered within the container:
stage.add(
<Rect group="stack" width={300} height={300}>
<Rect width={300} height={300} fill="royalblue" cornerRadius={16} />
<Text text="Stacked" fontSize={32} fontWeight={700} fill="white" />
</Rect>
);
Gap
gap sets the space between children along the layout axis:
<Rect group="row" gap={32}>...</Rect>
<Rect group="column" gap={8}>...</Rect>
Pass gap="auto" to distribute children evenly (space-between):
<Rect group="row" gap="auto" width={600} height={80} padding={24}>
<Rect width={60} height={60} fill="tomato" />
<Rect width={60} height={60} fill="royalblue" />
<Rect width={60} height={60} fill="seagreen" />
</Rect>
Alignment
align controls child placement on the cross axis. Pass a named position
('center', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight',
'topCenter', 'bottomCenter', 'centerLeft', 'centerRight') or an explicit
{ x, y } pivot. On each axis, x is -1 (left) … 1 (right) and y is -1
(bottom) … 1 (top); 0 centers. It defaults to 'center'.
// Row: cross axis is vertical — align children to the top
<Rect group="row" gap={20} align="topCenter">
<Rect width={60} height={120} fill="tomato" />
<Rect width={60} height={60} fill="royalblue" />
</Rect>
// Column: cross axis is horizontal — x: 1 aligns children to the right
<Rect group="column" gap={12} align={{ x: 1, y: 0 }}>
<Rect width={200} height={50} fill="tomato" />
<Rect width={100} height={50} fill="royalblue" />
</Rect>
Padding
Inset the layout's content area from the container's edges:
// Uniform
<Rect group="column" gap={12} padding={24}>...</Rect>
// Per-side
<Rect group="column" gap={12} padding={{ top: 32, bottom: 32, left: 16, right: 16 }}>...</Rect>
// Symmetric shorthand
<Rect group="row" gap={16} padding={{ horizontal: 24, vertical: 16 }}>...</Rect>
Nesting layouts
Layouts can be nested arbitrarily deep:
stage.add(
<Rect group="column" gap={16} padding={32} fill="#0f1117" width="fill" height="fill">
<Rect group="row" gap={16} width="fill">
<Rect width="fill" height={200} fill="#1e293b" cornerRadius={12} />
<Rect width="fill" height={200} fill="#1e293b" cornerRadius={12} />
</Rect>
<Rect width="fill" height={120} fill="#1e293b" cornerRadius={12} />
</Rect>
);
Animating layout props
All layout props are animatable with .to():
import { createScene, Rect, createRef, easeOut } from '@motion-script/core';
export default createScene(function* (stage) {
const container = createRef<Rect>();
stage.add(
<Rect ref={container} group="row" gap={8} padding={24} fill="#1e293b" cornerRadius={16}>
<Rect width={80} height={80} fill="tomato" cornerRadius={8} />
<Rect width={80} height={80} fill="royalblue" cornerRadius={8} />
<Rect width={80} height={80} fill="seagreen" cornerRadius={8} />
</Rect>
);
// Expand gap
yield* container().to({ gap: 48 }, 0.6, easeOut);
// Switch to column layout
yield* container().to({ group: 'column' }, 0.5, easeOut);
});
Animated child insertion and removal
Children can be added or removed with animated transitions:
const card = new Rect({ width: 200, height: 80, fill: '#4f80ff', cornerRadius: 8 });
// Insert at index 1 with a 0.4-second fade/grow
yield* container().addChildAt(card, 1, 0.4, easeOut);
// Remove at index 0 with a 0.3-second fade/shrink
yield* container().removeChildAt(0, 0.3);