# Textarea

> A multi-line text field. A CSS component with one class and no JavaScript.

The same look and states as [Input](/components/input.md), for answers longer than one line.

## How to use it

**Plain HTML**

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/tokens/tokens.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/textarea/textarea.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/label/label.css">
<!-- Or everything in one file: https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/dist/lotek.css -->
```

```html
<label class="lt-label" for="reason">Reason</label>
<textarea class="lt-textarea" id="reason" rows="3"
placeholder="Write a short reason"></textarea>


<label class="lt-label" for="message">Message to the caseworker</label>
<textarea class="lt-textarea" id="message"
data-state="success">I am moving to a new address on 1 June.</textarea>


<label class="lt-label" for="answer">Detailed answer</label>
<textarea class="lt-textarea" id="answer"
data-state="invalid" aria-invalid="true">Too short</textarea>


<label class="lt-label" for="note">Note</label>
<textarea class="lt-textarea" id="note"
disabled>The case is closed and cannot be changed.</textarea>
```

**TypeScript**

```ts
import "@lo-tek/interfaces/tokens.css"
import "@lo-tek/interfaces/textarea.css"
import "@lo-tek/interfaces/label.css"
```

`lt` is used in two ways, and which one depends on whether you create the element or find it.

**If you create the markup,** spread the attributes in. That is the way in React, Astro and anything else that renders:

```tsx
import { lt } from "@lo-tek/interfaces/react"


<textarea {...lt.textarea({ state: "invalid" })} id="reason" rows={3} />
```

The import is from `/react` here, since the block is JSX. From there the keys are `className` and `htmlFor`. In Astro and anything else without React, import `@lo-tek/interfaces` and you get `class` and `for`.

**If the element is already there,** set them on it. That is the way when you enhance HTML a server or a template has sent:

```ts
import { lt, type FieldState } from "@lo-tek/interfaces"


const field = document.querySelector<HTMLTextAreaElement>("#reason")
if (!field) throw new Error("element not found")


field.addEventListener("blur", () => {
  const state: FieldState = field.value.trim().length >= 20 ? "success" : "invalid"


  lt.setAttributes(field, lt.textarea({ state }))
})
```

`state` is the same type as in [Input](/components/input.md) and [Select](/components/select.md), so the value can be passed between them. `lt.setAttributes` clears the state from the previous call, so the field does not stay red after the error is fixed.

| `data-state` | Used for |
| - | - |
| *(omitted)* | Normal state |
| `invalid` | Validation has failed |
| `success` | The content is confirmed valid |

`data-state` is colour. Screen readers need `aria-invalid="true"` as well, or [Field](/components/field.md), which sets both.

## Classes you can style

| Class | What it is |
| - | - |
| `.lt-textarea` | The field. The state comes from `data-state`. |

| Variable | Default |
| - | - |
| `--lt-textarea-min-height` | `calc(var(--lt-spacing-4) * 5)` |
| `--lt-textarea-padding` | `var(--lt-spacing-2) var(--lt-spacing-3)` |
| `--lt-textarea-radius` | `var(--lt-spacing-1)` |

`--lt-textarea-min-height` is a minimum height, not a fixed height. `rows` works above it, and the user can still drag the field taller. With the default font and padding the minimum height is a little under three rows, so `rows="1"` and `rows="2"` give the same field, and only `rows="3"` is taller. If `rows` alone should decide the height, set `--lt-textarea-min-height: 0`. The width is locked, since the class sets `resize: vertical`.

See [Customising](/customising.md) for how the variables and the layer work.

## Accessibility

- Every field needs a visible `<label>` with `for` pointing to the `id`.
- If the field has an upper limit, say so in the help text before the user types. `maxlength` alone blocks typing without explaining why.
- If you use a character counter, put it in an element with `aria-live="polite"` and let it announce only when the limit is near. Otherwise it interrupts the reading on every keystroke.
- Do not turn off `resize`. It is often the only way the user gets an overview of a long text.
