Layout

Rect is both a shape and a flex layout container. Set group to arrange its children automatically.

Layout modes

groupBehavior
'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);