# Textarea

Several lines of text, growing with what is typed until it scrolls instead.

```slint
import { Textarea } from "@glint/components/textarea.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    in-out property <string> feedback-text: "";

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;

                Textarea {
                    placeholder: "Type your feedback here…";
                    text <=> root.feedback-text;
                }
            }
        }

        Text {
            text: root.feedback-text != "" ? "Character count: " + root.feedback-text.character-count : "Textarea is empty";
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

## Usage

```slint
import { Textarea } from "@glint/components/textarea.slint";

export component AppWindow inherits Window {
    in-out property <string> notes: "";

    VerticalLayout {
        alignment: center;

        Textarea {
            placeholder: "Add project notes…";
            text <=> root.notes;
            edited(updated) => {
                // handle multiline edit
            }
        }
    }
}
```

`Textarea` provides a multi-line text entry field that wraps words and automatically expands its preferred height as content grows.

## Examples

### Field-sizing content expansion

When placed inside a vertical layout without fixed height, `Textarea` begins at a comfortable minimum height and expands smoothly as additional lines are typed.

```slint
import { Textarea } from "@glint/components/textarea.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;

                Textarea {
                    text: "Glint is an open-source design system for Slint.\n\nIt provides accessible, keyboard-friendly primitives styled with fine-grained design tokens.";
                }
            }
        }
    }
}
```

### Read-only and disabled states

Set `read-only: true` to allow copying and scrolling while preventing text modification. Set `enabled: false` to dim the container and disable interaction.

```slint
import { Textarea } from "@glint/components/textarea.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 320px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;
                spacing: 12px;

                Textarea {
                    read-only: true;
                    text: "System Log: Initialized services\n2026-08-13: Deployment successful.";
                }

                Textarea {
                    enabled: false;
                    text: "Disabled textarea content";
                }
            }
        }
    }
}
```

### Validation error state

Set `invalid: true` to render a destructive error border.

```slint
import { Textarea } from "@glint/components/textarea.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;

                Textarea {
                    invalid: true;
                    text: "Short comment";
                }
            }
        }
    }
}
```

## API Reference

### Properties

| Property        | Type            | Default    | Description                                                                                                                                                                             |
| --------------- | --------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text`          | `in-out string` | no default | Two-way bound to the contents.                                                                                                                                                          |
| `placeholder`   | `in string`     | no default | Hint shown when empty.                                                                                                                                                                  |
| `enabled`       | `in bool`       | `true`     | When false, the textarea dims and stops accepting keystrokes.                                                                                                                           |
| `read-only`     | `in bool`       | `false`    | When true, the user can focus and select but not edit. Announced apart from `enabled`, like Input's: a log pane a user may still read and copy must not be published as a dead control. |
| `invalid`       | `in bool`       | `false`    | When true, paints the border in destructive state. The message that explains the error belongs to the surrounding `Field`.                                                              |
| `has-focus`     | `out bool`      | no default | Whether the box currently holds keyboard focus — also whether the focus ring is drawn, which is what makes the ring assertable.                                                         |
| `scroll-offset` | `out length`    | no default | How far the text is scrolled under the box, as a non-positive offset. Zero until the content passes `max-height`, since up to there the box grows instead of scrolling.                 |

### Callbacks

| Callback         | Description                                     |
| ---------------- | ----------------------------------------------- |
| `edited(string)` | Fired on every keystroke with the current text. |

## Accessibility

- **Text input role.** Publishes a single `accessible-role: text-input` node with the complete multi-line value.
- **Read-only vs Disabled.** `accessible-read-only` and `accessible-enabled` are kept distinct so assistive technology does not announce read-only content as dead.
- **`invalid` announces the word “Invalid”.** Slint 1.17 publishes no `accessible-invalid`, so the state rides the description channel ADR-0013 opened: the control’s own description first, then `"Invalid"` after it. A red border is not the announcement — this is.
- **Caret auto-reveal.** Viewport automatically scrolls to keep the active typing caret in view.
