> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Layout — Frame and padding

> Modifiers that size, position, and stack components within their parent

export const PlatformStatuses = ({statuses}) => {
  const StatusBadge = ({status, label}) => {
    const styles = {
      green: {
        backgroundColor: '#dcfce7',
        color: '#166534'
      },
      orange: {
        backgroundColor: '#fed7aa',
        color: '#9a3412'
      },
      red: {
        backgroundColor: '#fecaca',
        color: '#991b1b'
      },
      gray: {
        backgroundColor: '#f3f4f6',
        color: '#4b5563'
      }
    };
    const baseStyle = {
      display: 'inline-flex',
      alignItems: 'center',
      padding: '0.125rem 0.625rem',
      borderRadius: '9999px',
      fontSize: '0.875rem',
      fontWeight: '500'
    };
    const colorStyle = styles[status] || styles.green;
    return <span style={{
      ...baseStyle,
      ...colorStyle
    }}>
        {label || status}
      </span>;
  };
  const STATUS_CONFIG = {
    supported: {
      label: "Supported",
      color: "green"
    },
    partial: {
      label: "Partial",
      color: "orange"
    },
    "not-implemented": {
      label: "Not Implemented",
      color: "gray"
    }
  };
  const renderCard = (platform, value) => {
    if (!value) return null;
    const {status, note} = typeof value === "string" ? {
      status: value
    } : value;
    const config = STATUS_CONFIG[status];
    if (!config) return null;
    const titleMap = {
      ios: "SwiftUI",
      android: "Jetpack Compose",
      web: "Web"
    };
    return <Card key={platform} title={titleMap[platform] || platform}>
          <StatusBadge status={config.color} label={config.label} />
          {note && <div style={{
      marginTop: '0.5rem',
      fontSize: '0.875rem',
      color: '#6b7280'
    }}>
              {note}
            </div>}
      </Card>;
  };
  if (statuses == null) {
    return null;
  }
  return <Columns cols="3">
      {Object.entries(statuses).map(([platform, value]) => renderCard(platform, value))}
    </Columns>;
};

export const ComposeJS = ({code, name, height}) => {
  const encodedCode = useMemo(() => {
    if (!code) return "";
    try {
      return btoa(code);
    } catch (e) {
      console.error("Failed to encode code", e);
      return "";
    }
  }, [code]);
  if (!encodedCode) {
    return null;
  }
  return <iframe src={`https://www.metabind.ai/embed?code=${encodedCode}&name=${name ?? 'Example'}`} loading="lazy" style={{
    width: "100%",
    height: height || '350px',
    border: "1px solid #e5e7eb",
    borderRadius: "var(--rounded-2xl,1rem)",
    overflow: "hidden"
  }} title="ComposeJS Preview" />;
};

These modifiers control where a component sits and how much space it claims. `frame` sets fixed or flexible dimensions; `containerRelativeFrame` sizes a component relative to its nearest scrollable container; `padding` adds space around it.

`offset` shifts the visual position without affecting layout, `zIndex` controls stacking order inside a [ZStack](/bindjs/components/ZStack), and `layoutPriority` decides which sibling wins when space is tight.

## frame

Sets the size of a component using fixed dimensions or flexible constraints.

```typescript theme={null}
.frame(props: { width?: number; height?: number; alignment?: Alignment })
.frame(props: { minWidth?: number; idealWidth?: number; maxWidth?: number; minHeight?: number; idealHeight?: number; maxHeight?: number; alignment?: Alignment })
```

<ParamField path="props" type="object" required>
  Fixed frame.

  <Expandable title="props">
    <ParamField path="width" type="number" optional>
      The exact width in points.
    </ParamField>

    <ParamField path="height" type="number" optional>
      The exact height in points.
    </ParamField>

    <ParamField path="alignment" type="Alignment" optional default="center">
      How to align the content within the frame if it is smaller than the frame. See [Alignment](/bindjs/types/Alignment).
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="props" type="object" required>
  Flexible frame.

  <Expandable title="props">
    <ParamField path="minWidth" type="number" optional>
      The minimum width in points.
    </ParamField>

    <ParamField path="idealWidth" type="number" optional>
      The ideal width in points, used when the parent offers flexibility.
    </ParamField>

    <ParamField path="maxWidth" type="number" optional>
      The maximum width in points. Use `Infinity` to expand to fill available width.
    </ParamField>

    <ParamField path="minHeight" type="number" optional>
      The minimum height in points.
    </ParamField>

    <ParamField path="idealHeight" type="number" optional>
      The ideal height in points.
    </ParamField>

    <ParamField path="maxHeight" type="number" optional>
      The maximum height in points. Use `Infinity` to expand to fill available height.
    </ParamField>

    <ParamField path="alignment" type="Alignment" optional default="center">
      How to align the content within the frame. See [Alignment](/bindjs/types/Alignment).
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

The fixed frame overload sets exact dimensions. The flexible frame overload sets constraints — the component sizes itself within those bounds based on its content and the parent's size proposal. Use `Infinity` for `maxWidth` or `maxHeight` to make the component expand to fill all available space.

**Fixed size**

```typescript theme={null}
Color("blue")
    .frame({ width: 100, height: 100 })
```

**Fixed width only**

```typescript theme={null}
Text("Constrained width")
    .frame({ width: 200 })
```

**Full-width with leading alignment**

```typescript theme={null}
Text("Left aligned")
    .frame({ maxWidth: Infinity, alignment: "leading" })
```

**Minimum and maximum constraints**

```typescript theme={null}
Text("Flexible")
    .frame({ minWidth: 100, maxWidth: 300 })
```

**Full-screen background**

```typescript theme={null}
Color("blue")
    .frame({ maxWidth: Infinity, maxHeight: Infinity })
```

**Fixed size with alignment**

```typescript theme={null}
Text("Top-left")
    .frame({ width: 200, height: 200, alignment: "topLeading" })
```

## containerRelativeFrame

Sizes a component relative to its nearest container.

```typescript theme={null}
.containerRelativeFrame(axes: Axis)
.containerRelativeFrame(props: { axes?: Axis; alignment?: Alignment; count: number; span?: number; spacing: number })
.containerRelativeFrame(props: { axes?: Axis; alignment?: Alignment; fraction: number })
```

<ParamField path="axes" type="Axis" required>
  Fill container overload: the axis along which the component fills the container. See [Axis](/bindjs/types/Axis).
</ParamField>

<ParamField path="props" type="object" required>
  Grid subdivision overload.

  <Expandable title="props">
    <ParamField path="axes" type="Axis" optional>
      The axis along which to subdivide. See [Axis](/bindjs/types/Axis).
    </ParamField>

    <ParamField path="alignment" type="Alignment" optional>
      How to align the component within its cell. See [Alignment](/bindjs/types/Alignment).
    </ParamField>

    <ParamField path="count" type="number" required>
      The number of columns (horizontal) or rows (vertical) to divide the container into.
    </ParamField>

    <ParamField path="span" type="number" optional default="1">
      How many columns or rows this component spans.
    </ParamField>

    <ParamField path="spacing" type="number" required>
      The spacing between grid cells in points.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="props" type="object" required>
  Fractional sizing overload.

  <Expandable title="props">
    <ParamField path="axes" type="Axis" optional>
      The axis along which to apply fractional sizing. See [Axis](/bindjs/types/Axis).
    </ParamField>

    <ParamField path="alignment" type="Alignment" optional>
      How to align the component within its frame. See [Alignment](/bindjs/types/Alignment).
    </ParamField>

    <ParamField path="fraction" type="number" required>
      The fraction of the container's size to use (0.0 to 1.0).
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: "not-implemented",
web: "not-implemented",
}}
/>

The nearest container is the closest ancestor `ScrollView`, `List`, or `NavigationStack`. The modifier sizes the component relative to that container's visible area, not the full scrollable content. The grid subdivision form calculates the cell width as: `(containerWidth - (count - 1) * spacing) / count * span + (span - 1) * spacing`.

**Fill the container horizontally**

```typescript theme={null}
Color("blue")
    .containerRelativeFrame("horizontal")
```

**Grid layout — 3 columns with spacing**

```typescript theme={null}
Image(url)
    .containerRelativeFrame({
        axes: "horizontal",
        count: 3,
        span: 1,
        spacing: 8
    })
```

**Span multiple columns**

```typescript theme={null}
Image(url)
    .containerRelativeFrame({
        axes: "horizontal",
        count: 3,
        span: 2,
        spacing: 8
    })
```

**Fractional sizing — 80% of container width**

```typescript theme={null}
Text("Wide content")
    .containerRelativeFrame({
        axes: "horizontal",
        fraction: 0.8
    })
```

**Horizontal scroll with fixed-width cards**

```typescript theme={null}
ScrollView({ axes: "horizontal" }, [
    LazyHStack({ spacing: 16 }, [
        ForEach(items, (item) =>
            VStack([
                Image(item.image)
                    .scaledToFill(),
                Text(item.title)
                    .font("headline")
            ])
            .containerRelativeFrame({
                axes: "horizontal",
                count: 3,
                span: 2,
                spacing: 16
            })
        )
    ])
])
```

## padding

Adds padding around a component.

```typescript theme={null}
.padding(length: number): Component
.padding(edges?: EdgeSet, length?: number): Component
```

<ParamField path="length" type="number" optional>
  The padding amount in points. When called with a single number, applies equal padding on all edges.
</ParamField>

<ParamField path="edges" type="EdgeSet" optional>
  Which edges to apply padding to. See [EdgeSet](/bindjs/types/EdgeSet). When omitted, padding is applied to all edges.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

**Uniform padding**

```typescript theme={null}
Text("Hello")
    .padding(16)
```

**Edge-specific padding**

```typescript theme={null}
Text("Horizontal only")
    .padding("horizontal", 20)
```

**Vertical padding**

```typescript theme={null}
Text("Vertical spacing")
    .padding("vertical", 12)
```

**Single edge**

```typescript theme={null}
Text("Bottom only")
    .padding("bottom", 24)
```

**Combined with background**

Padding is applied before the background, so the background extends to cover the padded area.

```typescript theme={null}
Text("Padded card")
    .padding(16)
    .background(Color("blue"))
    .cornerRadius(12)
```

## offset

Offsets the component's visual position without affecting layout.

```typescript theme={null}
.offset(offset: { x?: number; y?: number }): Component
```

<ParamField path="offset" type="object" required>
  The offset values in points.

  <Expandable title="properties">
    <ParamField path="x" type="number" optional default="0">
      Horizontal offset. Positive moves right, negative moves left.
    </ParamField>

    <ParamField path="y" type="number" optional default="0">
      Vertical offset. Positive moves down, negative moves up.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

The offset is purely visual. The component's original layout space is preserved — surrounding components aren't affected by the offset. For layout-affecting position changes, use [padding](#padding) instead.

**Shifting a component**

```typescript theme={null}
Circle()
    .frame({ width: 50, height: 50 })
    .foregroundStyle(Color("blue"))
    .offset({ x: 20, y: -10 })
```

**Overlapping badge**

```typescript theme={null}
ZStack([
    Image({ systemName: "bell" })
        .font("title"),
    Circle()
        .frame({ width: 12, height: 12 })
        .foregroundStyle(Color("red"))
        .offset({ x: 10, y: -10 })
])
```

**Vertical offset only**

```typescript theme={null}
Text("Shifted down")
    .offset({ y: 20 })
```

## zIndex

Controls stacking order in a ZStack.

```typescript theme={null}
.zIndex(order: number): Component
```

<ParamField path="order" type="number" required>
  The stacking priority. Higher values render on top of lower values. The default is `0`.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "supported" },
web: { status: "supported" },
}}
/>

`zIndex` only affects rendering order within the same parent container. Without it, components in a ZStack render in order: first child at the bottom, last child on top. Negative values are valid and place the component behind siblings with the default `zIndex(0)`.

**Bring a component to front**

```typescript theme={null}
ZStack([
    Circle()
        .frame({ width: 100, height: 100 })
        .foregroundStyle(Color("blue")),
    Circle()
        .frame({ width: 100, height: 100 })
        .foregroundStyle(Color("red"))
        .offset({ x: 30, y: 30 })
        .zIndex(1),
])
```

**Layered cards**

```typescript theme={null}
ZStack([
    RoundedRectangle(12)
        .frame({ width: 200, height: 120 })
        .foregroundStyle(Color("gray"))
        .offset({ y: -10 })
        .zIndex(0),
    RoundedRectangle(12)
        .frame({ width: 220, height: 130 })
        .foregroundStyle(Color("blue"))
        .zIndex(1),
    RoundedRectangle(12)
        .frame({ width: 200, height: 120 })
        .foregroundStyle(Color("gray"))
        .offset({ y: 10 })
        .zIndex(0),
])
```

**Dynamic z-ordering**

```typescript theme={null}
const body = () => {
    const [selected, setSelected] = useState(0)
    return ZStack(
        items.map((item, index) =>
            Text(item.name)
                .padding(16)
                .background(Color(item.color))
                .cornerRadius(8)
                .offset({ x: index * 20, y: index * 20 })
                .zIndex(index === selected ? 10 : 0)
                .onTapGesture(() => setSelected(index))
        )
    )
}
```

## layoutPriority

Sets the layout priority for space distribution among siblings.

```typescript theme={null}
.layoutPriority(priority: number): Component
```

<ParamField path="priority" type="number" required>
  The layout priority. Higher values receive preference for available space. Default is `0`.
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "partial", note: "Uses value as fillMaxWidth fraction instead of layout priority ordering" },
web: "not-implemented",
}}
/>

Layout priority only matters when sibling components compete for limited space within a parent container (e.g., `HStack`, `VStack`). All components default to a priority of `0`. A component with priority `1` is offered its ideal size before components with priority `0`. Negative values are valid and cause the component to shrink before siblings.

**Prioritizing one text over another**

When two texts compete for space in an HStack, the one with higher layout priority gets its full width before the other.

```typescript theme={null}
HStack([
    Text("This text should get more space")
        .layoutPriority(1),
    Text("Secondary text")
])
```

**Preventing truncation**

```typescript theme={null}
HStack([
    Text("Important label")
        .layoutPriority(1)
        .lineLimit(1),
    Spacer(),
    Text("$99.99")
        .layoutPriority(1)
        .lineLimit(1)
])
```

## See also

* [aspectRatio](/bindjs/modifiers/layout-sizing#aspectratio) — sets the width-to-height ratio of content
* [fixedSize](/bindjs/modifiers/layout-sizing#fixedsize) — uses ideal size instead of the parent's proposal
* [Alignment](/bindjs/types/Alignment) — alignment values for `frame` and corner-aligned overlays
* [Axis](/bindjs/types/Axis) — axis values for `containerRelativeFrame`
* [EdgeSet](/bindjs/types/EdgeSet) — edge values for `padding`
