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

# Typography

> Modifiers that control the font, weight, design, decoration, and layout of text

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 shape how text is drawn. `font`, `fontWeight`, and `fontDesign` choose the typeface, weight, and variant. `bold`, `italic`, `monospaced`, `strikethrough`, and `underline` are convenience toggles for common styles and decorations.

`tracking` and `lineSpacing` adjust spacing between letters and lines. `textCase` transforms case at render time. `lineLimit` and `multilineTextAlignment` control how text wraps and aligns across multiple lines.

## font

Sets the font for text content in a component.

```typescript theme={null}
.font(value?: TextStyle | number | CustomFont)
```

<ParamField path="value" type="TextStyle | number | CustomFont" optional>
  The font to apply. Can be:

  * A semantic text style name (e.g., `"title"`, `"body"`, `"caption"`) — see [TextStyle](/bindjs/types/TextStyle)
  * A number for a custom point size using the system font
  * A `CustomFont` for a custom font family with a specific size
</ParamField>

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

**Semantic text style**

```typescript theme={null}
Text("Title").font("title")
```

```typescript theme={null}
Text("Small text").font("caption")
```

**Custom point size**

```typescript theme={null}
Text("Custom size").font(24)
```

**Custom font family**

```typescript theme={null}
Text("Custom font").font(
    CustomFont({ url: "https://example.com/Inter.ttf", family: "Inter", size: 16 })
)
```

**Combined with weight and design**

```typescript theme={null}
Text("Styled")
    .font("title2")
    .fontWeight("semibold")
    .fontDesign("rounded")
```

## fontWeight

Sets the font weight for text content.

```typescript theme={null}
.fontWeight(weight: FontWeight)
```

<ParamField path="weight" type="FontWeight" required>
  The font weight to apply. See [FontWeight](/bindjs/types/FontWeight) for available values (e.g., `"ultraLight"`, `"thin"`, `"light"`, `"regular"`, `"medium"`, `"semibold"`, `"bold"`, `"heavy"`, `"black"`).
</ParamField>

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

**Semibold text**

```typescript theme={null}
Text("Heading")
    .fontWeight("semibold")
```

**Light text**

```typescript theme={null}
Text("Subtitle")
    .fontWeight("light")
```

**Combined with font style**

```typescript theme={null}
Text("Heavy Title")
    .font("largeTitle")
    .fontWeight("heavy")
```

## fontDesign

Sets the font design variant for text content.

```typescript theme={null}
.fontDesign(design: FontDesign)
```

<ParamField path="design" type="FontDesign" required>
  The font design variant. See [FontDesign](/bindjs/types/FontDesign) for available values (e.g., `"default"`, `"rounded"`, `"monospaced"`, `"serif"`).
</ParamField>

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

**Rounded design**

```typescript theme={null}
Text("Friendly text")
    .fontDesign("rounded")
```

**Monospaced design**

```typescript theme={null}
Text("let x = 42")
    .fontDesign("monospaced")
```

**Serif design**

```typescript theme={null}
Text("Elegant heading")
    .font("title")
    .fontDesign("serif")
```

## bold

Makes text bold.

```typescript theme={null}
.bold(isActive?: boolean)
```

<ParamField path="isActive" type="boolean" optional default="true">
  Whether bold is applied. Pass `false` to explicitly disable bold. Defaults to `true`.
</ParamField>

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

**Make text bold**

```typescript theme={null}
Text("Important")
    .bold()
```

**Conditionally bold**

```typescript theme={null}
Text(item.title)
    .bold(item.isUnread)
```

**Combined with other text modifiers**

```typescript theme={null}
Text("Headline")
    .bold()
    .italic()
    .foregroundStyle(Color("blue"))
```

## italic

Makes text italic.

```typescript theme={null}
.italic(isActive?: boolean): Component
```

<ParamField path="isActive" type="boolean" optional default="true">
  Whether italic is applied. Pass `false` to explicitly disable italic.
</ParamField>

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

**Basic italic text**

```typescript theme={null}
Text("Emphasis").italic()
```

**Conditionally italic**

```typescript theme={null}
const body = () => {
    const [isQuote, setIsQuote] = useState(true)

    return Text("To be or not to be")
        .italic(isQuote)
}
```

**Combined with other typography modifiers**

```typescript theme={null}
Text("Bold Italic")
    .bold()
    .italic()
    .font("title2")
```

## strikethrough

Adds a strikethrough line to text.

```typescript theme={null}
.strikethrough(isActive?: boolean): Component
```

<ParamField path="isActive" type="boolean" optional default="true">
  Whether the strikethrough is visible. Defaults to `true`. Pass `false` to remove.
</ParamField>

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

**Basic strikethrough**

```typescript theme={null}
Text("Completed task")
    .strikethrough()
```

**Conditional strikethrough**

```typescript theme={null}
const body = () => {
    const [done, setDone] = useState(false)
    return Text("Buy groceries")
        .strikethrough(done)
        .onTapGesture(() => setDone(!done))
}
```

**Sale price display**

```typescript theme={null}
VStack({ alignment: "leading" }, [
    Text("$49.99")
        .strikethrough()
        .foregroundStyle(Color("gray")),
    Text("$29.99")
        .foregroundStyle(Color("red"))
        .bold(),
])
```

## underline

Adds an underline to text.

```typescript theme={null}
.underline(isActive?: boolean): Component
```

<ParamField path="isActive" type="boolean" optional default="true">
  Whether the underline is visible. Defaults to `true`. Pass `false` to remove.
</ParamField>

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

**Basic underline**

```typescript theme={null}
Text("Underlined text")
    .underline()
```

**Conditional underline**

```typescript theme={null}
const body = () => {
    const [active, setActive] = useState(false)
    return Text("Toggle underline")
        .underline(active)
        .onTapGesture(() => setActive(!active))
}
```

**Link-style text**

```typescript theme={null}
Text("Learn more")
    .underline()
    .foregroundStyle(Color("blue"))
```

## monospaced

Uses a monospaced font variant for text.

```typescript theme={null}
.monospaced(isActive?: boolean): Component
```

<ParamField path="isActive" type="boolean" optional default="true">
  Whether to use the monospaced variant. Pass `false` to explicitly disable.
</ParamField>

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

**Code-style text**

```typescript theme={null}
Text("let x = 42").monospaced()
```

**Tabular numbers**

Useful for aligning numbers in columns, since each digit takes the same width.

```typescript theme={null}
VStack([
    Text("$1,234.56").monospaced(),
    Text("$   99.00").monospaced(),
    Text("$    5.50").monospaced()
])
```

**Conditionally monospaced**

```typescript theme={null}
const body = () => {
    const [isCode, setIsCode] = useState(false)

    return Text("Hello World")
        .monospaced(isCode)
}
```

## tracking

Adjusts letter spacing in text.

```typescript theme={null}
.tracking(amount: MilliEm): Component
```

<ParamField path="amount" type="MilliEm" required>
  The letter spacing in milli-em units. `1000` equals 1em (the width of the current font size). Use positive values to increase spacing or negative values to tighten.
</ParamField>

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

`tracking` uses milli-em units where 1000 = 1em. For example, `tracking(500)` at a 16pt font size adds 8pt of spacing between each character. This is equivalent to CSS `letter-spacing` but uses a different unit scale.

**Wide letter spacing**

```typescript theme={null}
Text("SPACED")
    .tracking(500)
```

**Tight letter spacing**

```typescript theme={null}
Text("Tight")
    .tracking(-200)
```

**Heading with tracking**

```typescript theme={null}
Text("FEATURED")
    .font("caption")
    .fontWeight("semibold")
    .tracking(800)
    .foregroundStyle(Color("gray"))
    .textCase("uppercase")
```

## lineSpacing

Adjusts spacing between lines of text.

```typescript theme={null}
.lineSpacing(spacing: number): Component
```

<ParamField path="spacing" type="number" required>
  The spacing between lines, in points. Higher values increase the gap between lines.
</ParamField>

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

**Increasing line spacing**

```typescript theme={null}
Text("Line one\nLine two\nLine three")
    .lineSpacing(8)
```

**Readability for long-form text**

```typescript theme={null}
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
    .lineSpacing(6)
    .font("body")
    .frame({ maxWidth: 300 })
```

## textCase

Transforms the text case of a component.

```typescript theme={null}
.textCase(case: TextCase): Component
```

<ParamField path="case" type="TextCase" required>
  The text transformation to apply: `"uppercase"` or `"lowercase"`.
</ParamField>

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

**Uppercase**

```typescript theme={null}
Text("hello world")
    .textCase("uppercase")
```

**Lowercase**

```typescript theme={null}
Text("LOUD TEXT")
    .textCase("lowercase")
```

**Section headers**

```typescript theme={null}
VStack({ alignment: "leading", spacing: 8 }, [
    Text("Featured")
        .textCase("uppercase")
        .font("caption")
        .foregroundStyle(Color("gray"))
        .tracking(500),
    Text("Today's top picks")
        .font("title2"),
])
```

## lineLimit

Limits text to a maximum number of lines with truncation.

```typescript theme={null}
.lineLimit(lines?: number): Component
```

<ParamField path="lines" type="number" optional>
  The maximum number of lines to display. Excess text is truncated with an ellipsis. Pass `undefined` or omit to remove any line limit.
</ParamField>

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

When text exceeds the line limit, it is truncated with an ellipsis (`...`) at the end. Passing `undefined` or omitting the argument removes any previously set line limit, allowing unlimited lines.

**Single-line text**

```typescript theme={null}
Text("This is a very long piece of text that will be truncated")
    .lineLimit(1)
```

**Two-line description**

```typescript theme={null}
Text("A longer description that may wrap to a second line but will truncate after that with an ellipsis at the end.")
    .lineLimit(2)
```

**Combined with minimum scale factor**

Allow text to shrink before truncating.

```typescript theme={null}
Text("Shrink then truncate")
    .lineLimit(1)
    .minimumScaleFactor(0.5)
```

## multilineTextAlignment

Sets horizontal text alignment within the component's frame.

```typescript theme={null}
.multilineTextAlignment(alignment: TextAlignment): Component
```

<ParamField path="alignment" type="TextAlignment" required>
  The horizontal alignment for multi-line text. One of `"leading"`, `"center"`, or `"trailing"`.
</ParamField>

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

This modifier only affects text that wraps to multiple lines. Single-line text alignment is controlled by the parent container's alignment or the `.frame()` alignment. The default alignment is `"leading"`.

**Center-aligned text**

```typescript theme={null}
Text("This is a longer piece of text that will wrap to multiple lines and be centered.")
    .multilineTextAlignment("center")
    .frame({ maxWidth: 250 })
```

**Trailing-aligned text**

```typescript theme={null}
Text("Right-aligned text\non multiple lines")
    .multilineTextAlignment("trailing")
```

**Combined with frame alignment**

```typescript theme={null}
Text("Centered text in a full-width frame")
    .multilineTextAlignment("center")
    .frame({ maxWidth: Infinity, alignment: "center" })
```

## See also

* [minimumScaleFactor](/bindjs/modifiers/minimumScaleFactor) — allows text to shrink before truncating
* [foregroundStyle](/bindjs/modifiers/appearance-color-and-style#foregroundstyle) — set text color
* [dynamicTypeSize](/bindjs/modifiers/appearance-type-size-and-color-scheme#dynamictypesize) — override the user's preferred text size
* [fontWidth](/bindjs/modifiers/fontWidth) — set the font width variant
* [TextStyle](/bindjs/types/TextStyle) — semantic text style names for `font`
* [FontWeight](/bindjs/types/FontWeight) — available font weights
* [FontDesign](/bindjs/types/FontDesign) — available font design variants
