> ## 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.

# Transforms

> Modifiers that scale, rotate, or apply an arbitrary affine transform to a component

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 visually transform a component without changing its layout frame — surrounding components keep their original space. `scaleEffect` resizes uniformly or per-axis, `rotationEffect` rotates by a degree value around an anchor point, and `transformEffect` applies an arbitrary 2D affine matrix when you need scale, shear, and translate together.

For simple position changes that don't affect layout, see [offset](/bindjs/modifiers/layout-frame-and-padding#offset).

## scaleEffect

Scales a component uniformly or along individual axes.

```typescript theme={null}
.scaleEffect(scale: number): Component
.scaleEffect(props: { x?: number; y?: number; anchor?: UnitPoint }): Component
```

<ParamField path="scale" type="number">
  A uniform scale factor applied to both axes. `1` is the original size, `0.5` is half size, `2` is double size.
</ParamField>

<ParamField path="props" type="object">
  <Expandable title="properties">
    <ParamField path="x" type="number" optional>
      Horizontal scale factor.
    </ParamField>

    <ParamField path="y" type="number" optional>
      Vertical scale factor.
    </ParamField>

    <ParamField path="anchor" type="UnitPoint" optional>
      The point to scale from. Defaults to `"center"`. See [UnitPoint](/bindjs/types/UnitPoint).
    </ParamField>
  </Expandable>
</ParamField>

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

Scaling doesn't affect the component's layout frame. The component's original space is preserved, and the scaled content may overlap neighboring components.

**Uniform scale**

```typescript theme={null}
Text("Small")
    .scaleEffect(0.5)
```

**Per-axis scale**

```typescript theme={null}
Rectangle()
    .frame({ width: 100, height: 100 })
    .foregroundStyle(Color("blue"))
    .scaleEffect({ x: 1.5, y: 0.75 })
```

**Scale from a corner**

```typescript theme={null}
Text("Grow")
    .scaleEffect({ x: 2, y: 2, anchor: "topLeading" })
```

**Interactive scale**

```typescript theme={null}
const body = () => {
    const [enlarged, setEnlarged] = useState(false)
    return Image({ systemName: "star.fill" })
        .font("largeTitle")
        .scaleEffect(enlarged ? 1.5 : 1)
        .onTapGesture(() => setEnlarged(!enlarged))
}
```

## rotationEffect

Rotates a component by the specified angle.

```typescript theme={null}
.rotationEffect(degrees: number): Component
.rotationEffect(props: { degrees: number; anchor?: UnitPoint }): Component
```

<ParamField path="degrees" type="number" required>
  The rotation angle in degrees. Positive values rotate clockwise.
</ParamField>

<ParamField path="anchor" type="UnitPoint" optional>
  The point to rotate around. Defaults to `"center"`. See [UnitPoint](/bindjs/types/UnitPoint).
</ParamField>

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

**Simple rotation**

```typescript theme={null}
Text("Tilted")
    .rotationEffect(15)
```

**45-degree rotation**

```typescript theme={null}
Rectangle()
    .frame({ width: 50, height: 50 })
    .foregroundStyle(Color("blue"))
    .rotationEffect(45)
```

**Custom anchor point**

Rotate around the top-leading corner instead of the center.

```typescript theme={null}
Text("Rotated")
    .rotationEffect({ degrees: 30, anchor: "topLeading" })
```

**Animated rotation**

```typescript theme={null}
const body = () => {
    const [angle, setAngle] = useState(0)
    return VStack([
        Image({ systemName: "arrow.up" })
            .font("largeTitle")
            .rotationEffect(angle),
        Button("Rotate", () => setAngle(angle + 90))
    ])
}
```

## transformEffect

Applies a 2D affine transform matrix to a component.

```typescript theme={null}
.transformEffect(matrix: {
    a: number; b: number;
    c: number; d: number;
    tx: number; ty: number;
}): Component
```

<ParamField path="matrix" type="object" required>
  A 2D affine transform matrix.

  <Expandable title="properties">
    <ParamField path="a" type="number" required>
      Horizontal scale factor.
    </ParamField>

    <ParamField path="b" type="number" required>
      Vertical shear factor.
    </ParamField>

    <ParamField path="c" type="number" required>
      Horizontal shear factor.
    </ParamField>

    <ParamField path="d" type="number" required>
      Vertical scale factor.
    </ParamField>

    <ParamField path="tx" type="number" required>
      Horizontal translation in points.
    </ParamField>

    <ParamField path="ty" type="number" required>
      Vertical translation in points.
    </ParamField>
  </Expandable>
</ParamField>

<PlatformStatuses
  statuses={{
ios: { status: "supported" },
android: { status: "partial", note: "Skew parameters (b, c) are not supported; only scale (a, d) and translate (tx, ty) work" },
web: { status: "supported" },
}}
/>

The transform matrix is applied as a standard 2D affine transformation: `[a b tx; c d ty; 0 0 1]`. For simple scale, rotation, or offset operations, prefer [scaleEffect](#scaleeffect), [rotationEffect](#rotationeffect), or [offset](/bindjs/modifiers/layout-frame-and-padding#offset) for clarity. The transform doesn't affect the component's layout frame.

**Identity transform (no change)**

```typescript theme={null}
Text("Normal")
    .transformEffect({ a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 })
```

**Scale 2x**

```typescript theme={null}
Text("Big")
    .transformEffect({ a: 2, b: 0, c: 0, d: 2, tx: 0, ty: 0 })
```

**Skew (shear)**

```typescript theme={null}
Rectangle()
    .frame({ width: 100, height: 50 })
    .foregroundStyle(Color("blue"))
    .transformEffect({ a: 1, b: 0, c: 0.3, d: 1, tx: 0, ty: 0 })
```

**Translate**

```typescript theme={null}
Text("Moved")
    .transformEffect({ a: 1, b: 0, c: 0, d: 1, tx: 50, ty: -20 })
```

## See also

* [offset](/bindjs/modifiers/layout-frame-and-padding#offset) — shift a component visually without affecting layout
* [UnitPoint](/bindjs/types/UnitPoint) — anchor values for `scaleEffect` and `rotationEffect`
* [animation](/bindjs/functions/animations) — animate transform changes
