# Textarea

A multi-line text box that grows with its content.

- **Import:** `import { Textarea } from "@booleanpress/ui/textarea"`
- **Page:** <https://ui.booleanpress.com/components/textarea> · @booleanpress/ui 0.1.0

## Usage

`Textarea` is a styled native `textarea`. Name it with a `Label` whose `htmlFor` is its `id`.

```tsx
import { Label } from "@booleanpress/ui/label"
import { Textarea } from "@booleanpress/ui/textarea"

export function Signature() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="signature">Email signature</Label>
      <Textarea id="signature" />
    </div>
  )
}
```

It takes every attribute of a native `textarea`. It is uncontrolled with `defaultValue`, or controlled with `value` and `onChange`. It is at least 64 px tall and grows with its text (`field-sizing: content`); set `rows` or a `max-h-*` class to limit it. To put buttons or a counter inside the box, use `InputGroupTextarea`.

## Examples

### Basic

A labelled textarea with a placeholder.

```tsx
import { Label } from "@booleanpress/ui/label"
import { Textarea } from "@booleanpress/ui/textarea"

export default function TextareaBasic() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Label htmlFor="signature">Email signature</Label>
      <Textarea id="signature" placeholder="Best regards, the Acme team" />
    </div>
  )
}
```

### Grows with its text

The box is as tall as its content, never shorter than 64 px.

```tsx
import { Label } from "@booleanpress/ui/label"
import { Textarea } from "@booleanpress/ui/textarea"

export default function TextareaGrows() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Label htmlFor="ticket-reply">Reply</Label>
      <Textarea
        id="ticket-reply"
        defaultValue={"Hi Sam,\n\nThanks for the details. We have found the failed message.\nThe mailbox was full, so the provider rejected it.\n\nWe will resend it today."}
      />
    </div>
  )
}
```

### Disabled

A disabled textarea cannot be focused or edited.

```tsx
import { Label } from "@booleanpress/ui/label"
import { Textarea } from "@booleanpress/ui/textarea"

export default function TextareaDisabled() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Label htmlFor="bounce-reason">Bounce reason</Label>
      <Textarea id="bounce-reason" defaultValue="550 5.1.1 The email account does not exist." disabled />
    </div>
  )
}
```

### Invalid

`aria-invalid` draws the error border; `aria-describedby` reads the message with the name.

```tsx
import { Label } from "@booleanpress/ui/label"
import { Textarea } from "@booleanpress/ui/textarea"

export default function TextareaInvalid() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Label htmlFor="note">Internal note</Label>
      <Textarea id="note" aria-invalid aria-describedby="note-error" />
      <p id="note-error" className="text-sm text-destructive">
        Write a note before you save.
      </p>
    </div>
  )
}
```

## Accessibility

**Semantics.** A native `textarea`, role `textbox`, multi-line.

**Labels.** Name every textarea: a `Label` with `htmlFor`, or `aria-label`. A placeholder is not a name. Put hints and errors in `aria-describedby`.

**Focus.** It is in the tab order unless disabled. Tab moves on; it does not insert a tab character. The focus ring shows on keyboard focus.

**Known limits.**

- Growing with content depends on `field-sizing: content`. A browser without it shows the 64 px box with a scrollbar.

### Keyboard

| Key | Behaviour |
| --- | --- |

## API

### Textarea

Renders a `textarea` and passes it every other prop.

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="textarea"` (Textarea).

## Theming

| Token | Used for |
| --- | --- |
| `--control` | border |
| `--destructive` | border, focus ring |
| `--input` | border, background |
| `--muted-foreground` | text |
| `--ring` | border, focus ring |
