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

# Text refinements

> Modifiers for fitting, animating, and badging text content

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 iOS-only modifiers refine text rendering — shrinking long strings to fit, tightening character spacing, varying font width, animating content changes, and attaching badges to list rows or tab items.

Use `minimumScaleFactor` and `allowsTightening` together with [lineLimit](/bindjs/modifiers/lineLimit) to gracefully fit text in fixed frames. Use `contentTransition` to animate text updates — particularly numeric counters.

## minimumScaleFactor

Sets the minimum scale factor for text before it truncates.

```typescript theme={null}
.minimumScaleFactor(factor: number): Component
```

<ParamField path="factor" type="number" required>
  The minimum scale factor, from `0` to `1`. For example, `0.5` allows text to shrink to 50% of its original size before truncating.
</ParamField>

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

**Auto-shrinking text**

```typescript theme={null}
Text("This text will shrink to fit")
    .minimumScaleFactor(0.5)
    .lineLimit(1)
```

**Fitting a title in a fixed frame**

```typescript theme={null}
Text("A Very Long Title That Needs to Fit")
    .font("title")
    .minimumScaleFactor(0.7)
    .lineLimit(1)
    .frame({ maxWidth: 200 })
```

<Note>
  The system progressively reduces the font size down to `minimumScaleFactor * originalSize` before falling back to truncation. A value of `1` (the default) means no shrinking is allowed. Best used in combination with [lineLimit](/bindjs/modifiers/lineLimit) to constrain the text to a fixed number of lines.
</Note>

## allowsTightening

Allows text to tighten character spacing to fit available space before truncating.

```typescript theme={null}
.allowsTightening(isEnabled?: boolean)
```

<ParamField path="isEnabled" type="boolean" optional default="true">
  Whether to allow the text to tighten its character spacing. Defaults to `true`.
</ParamField>

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

**Allow text to compress before truncating**

```typescript theme={null}
Text("A long title that should fit")
    .lineLimit(1)
    .allowsTightening()
```

**Disable tightening**

```typescript theme={null}
Text("Fixed spacing")
    .allowsTightening(false)
```

<Note>
  Text tightening reduces the inter-character spacing slightly to fit more text in the available width. It is applied before truncation but after the minimum scale factor (if set).
</Note>

## fontWidth

Sets the font width variant for text content.

```typescript theme={null}
.fontWidth(width: FontWidth)
```

<ParamField path="width" type="FontWidth" required>
  The font width to apply. See [FontWidth](/bindjs/types/FontWidth) for available values (e.g., `"compressed"`, `"condensed"`, `"standard"`, `"expanded"`).
</ParamField>

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

**Condensed text**

```typescript theme={null}
Text("Condensed Heading")
    .fontWidth("condensed")
```

**Expanded text**

```typescript theme={null}
Text("WIDE")
    .fontWidth("expanded")
```

**Combined with weight and design**

```typescript theme={null}
Text("Compact Bold")
    .fontWeight("bold")
    .fontWidth("compressed")
```

## badge

Adds a badge displaying a count or text to a component, typically used in tab bars or list rows.

```typescript theme={null}
.badge(value: number | string)
```

<ParamField path="value" type="number | string" required>
  The badge content. Pass a number for a count badge, or a string for a text badge.
</ParamField>

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

**Numeric badge on a list row**

```typescript theme={null}
Text("Inbox")
    .badge(5)
```

**Text badge**

```typescript theme={null}
Text("Updates")
    .badge("New")
```

**Badge on a tab item**

```typescript theme={null}
Label("Messages", { systemName: "message" })
    .badge(unreadCount)
```

<Note>
  Badges are most commonly used with `List` rows and `TabView` items. On iOS, the system renders the badge in a rounded capsule aligned to the trailing edge.
</Note>

## contentTransition

Controls how content changes are animated within a view.

```typescript theme={null}
.contentTransition(type: ContentTransitionType | { countsDown: boolean })
```

<ParamField path="type" type="ContentTransitionType | { countsDown: boolean }" required>
  The content transition type. Can be:

  * `"numericText"` -- animates numeric text changes with a rolling digit effect
  * `"opacity"` -- cross-fades between old and new content
  * `"identity"` -- no animation, content updates immediately
  * `"interpolate"` -- smoothly interpolates between old and new content
  * `{ countsDown: boolean }` -- numeric text transition with direction hint (`true` for counting down, `false` for counting up)
</ParamField>

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

**Animate a numeric counter**

```typescript theme={null}
Text(String(count))
    .contentTransition("numericText")
```

**Numeric text with direction hint**

```typescript theme={null}
Text(String(countdown))
    .contentTransition({ countsDown: true })
```

**Cross-fade content changes**

```typescript theme={null}
Text(statusMessage)
    .contentTransition("opacity")
```

## See also

* [font](/bindjs/modifiers/font) — overall font style
* [fontWeight](/bindjs/modifiers/fontWeight) — font weight (e.g., bold, regular)
* [fontDesign](/bindjs/modifiers/fontDesign) — font design (e.g., serif, monospaced)
* [lineLimit](/bindjs/modifiers/lineLimit) — limits the number of displayed lines
* [transition](/bindjs/modifiers/transition) — view-level appearance and disappearance transitions
* [FontWidth](/bindjs/types/FontWidth) — font width values
