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

# Clipping and masking

> Modifiers that clip a component to its frame, to a shape, or to an image-based mask

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" />;
};

Clipping and masking modifiers control which parts of a component are visible. `clipped` trims content to the component's rectangular bounds; `clipShape` clips to any [Shape](/bindjs/components/shapes) — circle, capsule, rounded rectangle; `mask` uses an image's luminance to drive transparency.

`clipShape` clips both visuals and hit testing, so taps land only inside the shape. To change just the hit area without altering visuals, see [contentShape](/bindjs/modifiers/contentShape). For overall transparency, see [opacity](/bindjs/modifiers/opacity).

## clipped

Clips a component's content to its bounding frame.

```typescript theme={null}
.clipped()
```

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

**Clip overflowing content**

```typescript theme={null}
Image({ url: "wide-image.jpg" })
    .resizable()
    .frame({ width: 100, height: 100 })
    .clipped()
```

**Clip after aspect ratio fill**

```typescript theme={null}
Image({ url: "photo.jpg" })
    .resizable()
    .aspectRatio(1, "fill")
    .frame({ width: 150, height: 150 })
    .clipped()
```

`clipped()` clips to the component's rectangular bounds. For clipping to a custom shape, use [clipShape](#clipshape) instead.

## clipShape

Clips a component's content to a specific shape.

```typescript theme={null}
.clipShape(shape: Shape)
```

<ParamField path="shape" type="Shape" required>
  The shape to clip to. Common shapes include `Circle()`, `Capsule()`, `RoundedRectangle(cornerRadius)`, `Rectangle()`, and `Ellipse()`.
</ParamField>

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

**Clip an image to a circle**

```typescript theme={null}
Image({ url: "avatar.jpg" })
    .resizable()
    .frame({ width: 64, height: 64 })
    .clipShape(Circle())
```

**Clip to a rounded rectangle**

```typescript theme={null}
Image({ url: "photo.jpg" })
    .resizable()
    .frame({ width: 200, height: 150 })
    .clipShape(RoundedRectangle(16))
```

**Clip to a capsule**

```typescript theme={null}
Text("Tag")
    .padding({ leading: 12, trailing: 12, top: 6, bottom: 6 })
    .background(Color("blue"))
    .foregroundStyle(Color("white"))
    .clipShape(Capsule())
```

`clipShape` clips both the visual content and the hit-testing area. To change only the hit-testing shape without clipping visuals, use [contentShape](/bindjs/modifiers/contentShape).

## mask

Masks the component using an image, controlling which areas are visible.

```typescript theme={null}
.mask(image: Image): Component
```

<ParamField path="image" type="Image" required>
  The image used as a mask. White areas show the underlying content, black areas hide it. Gray values produce partial transparency.
</ParamField>

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

**Masking with an image**

```typescript theme={null}
Image({ url: "photo.jpg" })
    .resizable()
    .frame({ width: 200, height: 200 })
    .mask(
        Image({ url: "mask.png" })
            .resizable()
    )
```

**Gradient fade using a shape mask**

```typescript theme={null}
Text("Fading text that trails off at the bottom")
    .frame({ maxWidth: 200 })
    .mask(
        LinearGradient({
            colors: ["black", "clear"],
            startPoint: "top",
            endPoint: "bottom"
        })
    )
```

The mask image's luminance determines visibility: white (or light) areas show the content, black (or dark) areas hide it. On web, this is implemented using CSS `mask-image`.

## See also

* [contentShape](/bindjs/modifiers/contentShape) — change the hit-testing shape without clipping visuals
* [cornerRadius](/bindjs/modifiers/cornerRadius) — round the corners of a component
* [opacity](/bindjs/modifiers/opacity) — control overall transparency
* [overlay](/bindjs/modifiers/overlay) — layer a component on top
