<!-- GENERATED by scripts/build-llms.mjs from llms/forms.md — do not edit this file. -->

# `lr-textarea`

- **Import** `import '@aceshooting/lyra-ui/components/lr-textarea.js';` (stable tag alias; registers the tag)
- **Class** `LyraTextarea`, also available unregistered from `@aceshooting/lyra-ui/components/forms/textarea/textarea.class.js`
- **Family** `components/forms/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 13 parts, 12 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-textarea`

A multiline plain-text input primitive, form-associated (participates in native `<form>`
submission/validation/reset via `name`/`value`/`disabled`/`required`/`checkValidity()`/
`reportValidity()`). Ships an opt-in `label`/`hint`/`errorText` form-control chrome mirroring
`lr-select` -- left unset, none of it renders.

Removing `label`, `hint`, `help-text`, or `error-text` safely omits the corresponding content while
preserving native `null` property readback. Explicit empty strings stay empty; later supplied text
renders normally.

Changing own `disabled` from true to false in the same task that disables an ancestor fieldset keeps
the native editing control effectively disabled. The enabled first-legend exception and explicit
own-disabled state retain their native meaning; validity and form submission follow the effective
disabled state.

```html
<lr-textarea placeholder="Notes" rows="4"></lr-textarea>
<lr-textarea
  label="Bio"
  maxlength="280"
  with-count
  appearance="outlined"
  size="s"
  resize="auto"
></lr-textarea>
<script type="module">
  import "@aceshooting/lyra-ui/components/forms/textarea/textarea.js";
  const bio = document.querySelector('lr-textarea[label="Bio"]');
  await bio.updateComplete; // both calls are no-ops before the first render
  bio.scrollPosition({ top: 0 }); // pin a restored draft back to the top
  console.log(bio.scrollPosition()); // -> { top: 0, left: 0 }
</script>
```

### Properties

| Property                 | Attribute                  | Type                                                                 | Default      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------ | -------------------------- | -------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `value`                  | `value`                    | `string`                                                             | `''`         | The current text value.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `defaultValue`           | `value` / `default-value`  | `string`                                                             | `''`         | Reset value. The Shoelace attribute alias and the native-style `value` content attribute share the same reset engine.                                                                                                                                                                                                                                                                                                                                                                                             |
| `rows`                   | `rows`                     | `number`                                                             | `4`          | Visible text rows (mapped default).                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `resize`                 | `resize`                   | `'none' \| 'vertical' \| 'horizontal' \| 'both' \| 'auto'`           | `'vertical'` | Native CSS `resize` behavior, plus `'auto'` (`ResizeObserver`-driven grow-to-content, no manual handle). An invalid runtime value falls back to `'vertical'`; `'auto'` maps native CSS resize to `none`.                                                                                                                                                                                                                                                                                                          |
| `size`                   | `size`                     | `LyraSize`                                                           | `'m'`        | Visual size on the shared control ladder — the same scale as `lr-input`/`lr-select`/`lr-button`, and both spellings of every tier are accepted (`2xs`/`xs`/`s`/`m`/`l`/`xl` and `small`/`medium`/`large`). Governs the field's padding, font size and corner radius. Reflected.                                                                                                                                                                                                                                   |
| `appearance`             | `appearance`               | `'accent' \| 'filled' \| 'outlined' \| 'filled-outlined' \| 'plain'` | `'outlined'` | Visual treatment of the field. The mapped default draws a border without a fill; the other values share `lr-input`'s vocabulary. Reflected.                                                                                                                                                                                                                                                                                                                                                                       |
| `filled`                 | `filled`                   | `boolean`                                                            | `false`      | Shoelace alias for the filled treatment.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `pill`                   | `pill`                     | `boolean`                                                            | `false`      | Fully rounded field corners, matching `lr-input`'s/`lr-select`'s own `pill` — both upstreams ship it on their textarea, so a mechanical tag rename must not drop it. It changes the private radius default to `--lr-radius-pill`, so an inherited or direct `--lr-textarea-radius` stays authoritative. Most useful on a one- or two-row field: a tall multi-line surface with fully rounded ends wastes its first and last line's inline space, which is why it is opt-in rather than tied to `size`. Reflected. |
| `withCount`              | `with-count`               | `boolean`                                                            | `false`      | Renders a character count below the field, inside `[part="footer"]`. With `maxlength` set it counts _down_ the remaining characters instead of up from zero. Reflected.                                                                                                                                                                                                                                                                                                                                           |
| `placeholder`            | `placeholder`              | `string`                                                             | `''`         | Placeholder text.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `readonly`               | `readonly`                 | `boolean`                                                            | `false`      | Native read-only behavior: prevents user edits while preserving focus, selection/copy, form submission, and silent programmatic editing methods. Reflected.                                                                                                                                                                                                                                                                                                                                                       |
| `label`                  | `label`                    | `string`                                                             | `''`         | Visible label text. Unset: no label chrome renders.                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `hint`                   | `hint`                     | `string`                                                             | `''`         | Hint text below the field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `helpText`               | `help-text`                | `string`                                                             | `''`         | Shoelace alias for `hint`; `hint` wins when both are set.                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `withLabel` / `withHint` | `with-label` / `with-hint` | `boolean`                                                            | `false`      | SSR slot-presence hints; neither is required for hydrated client-side slot detection.                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `errorText`              | `error-text`               | `string`                                                             | `''`         | Error text below the field (overridden by slotted `error` content).                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `customError`            | `custom-error`             | `string \| null`                                                     | `null`       | Reflected consumer-supplied validation message. A non-empty value blocks submission until `setCustomValidity('')` clears it.                                                                                                                                                                                                                                                                                                                                                                                      |
| `accessibleLabel`        | `aria-label`               | `string \| null`                                                     | `null`       | Accessible-name override forwarded to the internal `<textarea>`; every non-`null` value wins by presence—including an explicit empty string—over `label`, `placeholder`, and the localized default.                                                                                                                                                                                                                                                                                                               |
| `spellcheck`             | `spellcheck`               | `boolean`                                                            | `true`       | Forwarded to the native `<textarea>`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `autofocus`              | `autofocus`                | `boolean`                                                            | `false`      | Forwarded to the native `<textarea>`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `title`                  | `title`                    | `string`                                                             | `''`         | Forwarded to the native `<textarea>`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `autocapitalize`         | `autocapitalize`           | `string`                                                             | `''`         | Forwarded to the native `<textarea>`; empty omits the attribute.                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `autocorrect`            | `autocorrect`              | read: `boolean`; write: `boolean \| string`                          | `true`       | Forwarded to the native `<textarea>` (Safari/WebKit-specific). Reads are always boolean. Boolean writes preserve Web Awesome's IDL; Shoelace-compatible string writes normalize `off`/`false` to `false` and every other string to `true`. The HTML attribute uses canonical `on`/`off`. When the host attribute is absent and the state remains at its `true` default, the internal attribute is omitted so the browser keeps its default behavior.                                                              |
| `wrap`                   | `wrap`                     | `'hard' \| 'soft' \| 'off'`                                          | `'soft'`     | Native line-wrapping/submission behavior.                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `autocomplete`           | `autocomplete`             | `string`                                                             | `''`         | Forwarded to the native `<textarea>`; empty omits the attribute.                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `inputMode`              | `inputmode`                | `string`                                                             | `''`         | Virtual-keyboard input hint forwarded to the native `<textarea>`.                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `enterKeyHint`           | `enterkeyhint`             | `string`                                                             | `''`         | Virtual-keyboard Enter-key hint forwarded to the native `<textarea>`.                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `minlength`              | `minlength`                | `number \| undefined`                                                | `undefined`  | Minimum text length; forwarded to the native `<textarea>` and reported as `validity.tooShort`.                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `maxlength`              | `maxlength`                | `number \| undefined`                                                | `undefined`  | Maximum text length; forwarded to the native `<textarea>` (which also stops typing past it) and reported as `validity.tooLong`.                                                                                                                                                                                                                                                                                                                                                                                   |
| `debounce`               | `debounce`                 | `number \| undefined`                                                | `undefined`  | How long (ms) to wait after the last keystroke before emitting one `lr-input-settled`, while `input`/`lr-input` keep firing per keystroke as before. Omitted, `0`, or non-finite means no debounce: `lr-input-settled` never fires. A pending debounce is flushed immediately by `change`/Enter/blur and cancelled with no stray settle by disconnection and by a programmatic `value` write that changes the value. A write of the value already held leaves it pending, so a framework binding that writes the just-typed value back on each render (the controlled-input pattern) still settles. Shares its `DebounceController` primitive with `lr-filter-bar`'s own per-filter `debounce` and with `lr-input`'s identical property. |
| `name`                   | `name`                     | `string`                                                             | `''`         | Form field name.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `disabled`               | `disabled`                 | `boolean`                                                            | `false`      | Disables the control.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `required`               | `required`                 | `boolean`                                                            | `false`      | Participates in native constraint validation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

The lowercase native IDLs `inputmode: string` and `enterkeyhint: string` delegate to
`inputMode`/`enterKeyHint`; `autocorrect` is one public state with a boolean read type and a wider
cross-upstream write vocabulary.

**8.0 migration:** the former camel-case string property `autoCorrect` is not retained as a public
alias. Prefer boolean `autocorrect` writes in new code. A migrated Shoelace string write remains
valid (`'off'` and `'false'` read back as `false`; other strings read back as `true`), while markup
uses `autocorrect="on"` / `autocorrect="off"`.

### Constraint validation

`validity` reports `valueMissing` (from `required`), `tooShort` (from `minlength`), and `tooLong`
(from `maxlength`) — the complete set a native `<textarea>` can produce. Leaving `minlength` and
`maxlength` unset constrains nothing, exactly as before they existed.

While `readonly`, all three constraint flags are suspended and `checkValidity()` succeeds, matching
native textarea behavior. The value remains a successful form value and is restored by form reset;
unsetting `readonly` restores the applicable required and length constraints.

Two behaviors are worth knowing, both inherited from the platform and both shared with `lr-input`:

- **An empty value is never `tooShort`.** Native `minlength` only applies to a non-empty value, so
  an optional field left blank stays valid; `required` is what rejects empty.
- **A script-assigned value is validated too.** The native `tooShort`/`tooLong` flags are raised
  only for a value the _user_ edited, so the component recomputes both from its own `value` and
  ORs them in — `el.value = <over-length>` reports `tooLong` rather than silently submitting.
  Lengths count UTF-16 code units, matching the native control (one emoji counts as two).
  `validationMessage` is the browser's own localized message when the native control flagged the
  value, and the localized `valueInvalid` string when only the script-value check did.

The visible label, hint, and error live in the same shadow tree as the native control, so their
generated ids safely drive the native `<label>`/`aria-describedby` relationships. Name precedence
is `accessibleLabel`/host `aria-label`, visible `label`, `placeholder`, then the localized
`textareaLabel` message. External `aria-labelledby`/`aria-describedby` idrefs are not copied across
the shadow boundary.

### Getters and selection properties

- `input: HTMLTextAreaElement | null` — the internal native control.
- `selectionStart: number | null`, `selectionEnd: number | null`, and
  `selectionDirection: 'forward'|'backward'|'none'|null` — readable and writable passthroughs to
  the native selection state.
- The shared form-associated getters `form`, `getForm()`, `labels`, `validity`, `validationMessage`,
  `willValidate`, and `effectiveDisabled` are also available.

### Methods

| Method                                                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `focus(options?)` / `blur()`                           | Focus or blur the internal native control.                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `select()`                                             | Select all text.                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `setSelectionRange(start, end, direction?)`            | Set the native selection range and optional direction.                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `setRangeText(replacement, start?, end?, selectMode?)` | Apply a native range edit, then synchronize the component `value`, form value, validity, and auto-grown size without emitting a user event.                                                                                                                                                                                                                                                                                                                                           |
| `scrollPosition(position?)`                            | Read or write the internal textarea's scroll offsets. Called with no argument it returns the current `{ top, left }`; called with a partial `{ top?, left? }` it writes only the axes present and returns `undefined`. Returns `undefined` either way before the internal textarea has rendered, and a non-finite offset leaves that axis alone. This is the one piece of scroll state no other public member reaches — use it to restore a draft, or to pin a long value to its end. |
| `setFormValue(value)`                                  | Set the reactive and submitted value synchronously.                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `setCustomValidity(message)`                           | Set a consumer-supplied validation message. A non-empty message raises `customError`, becomes `validationMessage`, and blocks submission; `''` clears it and restores intrinsic required/length validity.                                                                                                                                                                                                                                                                             |
| `resetValidity()`                                      | Clear only consumer-supplied custom validity and recompute current intrinsic constraints. It does not change `value`/`defaultValue`, clear prior interaction state, or force an intrinsically invalid field valid.                                                                                                                                                                                                                                                                    |
| `checkValidity()` / `reportValidity()`                 | Run native constraint validation through `ElementInternals`.                                                                                                                                                                                                                                                                                                                                                                                                                          |

### Events

| Event        | Detail              | Description                                                                                                                                                               |
| ------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `input`      | none                | Native-style composed event fired on every user-driven edit.                                                                                                              |
| `change`     | none                | Native-style composed event fired at native `change` timing.                                                                                                              |
| `lr-input`   | `{ value: string }` | Compatibility alias fired on every user-driven edit.                                                                                                                      |
| `lr-change`  | `{ value: string }` | Compatibility alias fired on native `change` timing (blur after a committed edit).                                                                                        |
| `lr-input-settled` | `{ value: string }` | Fires once, `debounce` ms after the last keystroke, alongside the per-keystroke `input`/`lr-input` pair. **Non-cancelable.** See `debounce` above for the flush and cancellation rules. Never fires while `debounce` is unset, `0`, or non-finite. |
| `blur`       | none                | Re-dispatched from the internal native `<textarea>`'s own `blur` -- bubbling and composed, unlike the native event.                                                       |
| `focus`      | none                | Re-dispatched from the internal native `<textarea>`'s own `focus`, for the same reason as `blur`.                                                                         |
| `lr-invalid` | none                | Fired when a validity check finds the control invalid. **Cancelable** — `preventDefault()` suppresses the native validation bubble and `reportValidity()`'s focus/scroll. |

Programmatic property assignments, selection changes, `setRangeText()`, form reset, and form-state
restoration are silent. User edits update `value`, submitted form data, and required validity before
the corresponding `lr-input`/`lr-change` event is dispatched. `form.reset()` restores the
original declarative `value`, matching native `defaultValue` behavior.

### Slots

| Slot        | Description                |
| ----------- | -------------------------- |
| `label`     | Custom label content.      |
| `hint`      | Custom hint content.       |
| `help-text` | Shoelace alias for `hint`. |
| `error`     | Custom error content.      |

### CSS Parts

| Part                                                                     | Description                                                                                                                                                                                                                                  |
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `form-control`                                                           | The outer wrapper around label, textarea, error and hint.                                                                                                                                                                                    |
| `form-control-label`                                                     | The `<label>` element.                                                                                                                                                                                                                       |
| `label`                                                                  | The visible label-content wrapper.                                                                                                                                                                                                           |
| `base` / `form-control-input` / `textarea-adjuster` / `textarea-wrapper` | Compatibility names on the plain block wrapper around the native `<textarea>`. It deliberately imposes no size of its own — the native resize grip writes inline `width`/`height` onto the `<textarea>` itself, so the field drives the box. |
| `textarea`                                                               | The native `<textarea>` element.                                                                                                                                                                                                             |
| `footer`                                                                 | The row below the field carrying the character count. Always in the DOM but `hidden` without `with-count`.                                                                                                                                   |
| `count`                                                                  | The character count text, rendered only with `with-count`.                                                                                                                                                                                   |
| `hint` / `form-control-help-text`                                        | Compatibility names on the hint message.                                                                                                                                                                                                     |
| `error`                                                                  | The error message.                                                                                                                                                                                                                           |

The visible `[part="count"]` is `aria-hidden`; the internal shadow `.count-announcement` node is
also an `aria-hidden` mirror (it is not a public CSS part), while the debounced spoken update is
appended to the shared light-DOM
`[data-lr-live-region="polite"]` sink about a second after the user stops typing. A screen reader is
therefore not told the count on every keystroke, and the announcement remains reliable across the
shadow boundary. The sink stays silent while the textarea or a composed ancestor is hidden, inert,
`aria-hidden`, or hidden by rendered CSS. Lengths count UTF-16 code units (one emoji counts as two),
matching the native `maxlength` the count reports against, and the remaining count floors at zero — only a
script-assigned value can exceed `maxlength`, and the `tooLong` validity flag already reports that
state better than a negative number would. An unparseable `maxlength` (`maxlength="oops"`) is
dropped rather than rendered as `NaN`, and the count counts up from zero instead.

`required` with a non-empty `label` paints the library's shared marker on `form-control-label` —
the one `::after` rule described under "The required-field marker" above, not a copy of it, so
`--lr-form-control-required-content`, `--lr-form-control-required-color` and
`--lr-form-control-required-offset` retune or suppress it here exactly as they do on `lr-input`.
With no label text the part is hidden and no glyph is painted.

### Themeable custom properties

- `--lr-textarea-max-block-size` (default `none`) — bounds `resize="auto"`; content beyond the
  bound scrolls inside the native textarea. Auto-resize remeasures after user edits, programmatic
  `value`/`rows` changes, range edits, and container-width changes.
- `--lr-textarea-padding` (default `var(--lr-form-control-padding-inline)`),
  `--lr-textarea-font-size` (default `var(--lr-form-control-font-size)`) and
  `--lr-textarea-radius` (default `var(--lr-form-control-radius)`) — the native textarea's padding,
  font size and corner radius. All three read the active `size` tier of the shared control ladder,
  so their private defaults follow the tier; the two tightest tiers take a smaller radius. Public
  values inherited from an ancestor or set directly on the host win in every tier. `pill` changes
  the private `--lr-textarea-radius` default to `--lr-radius-pill`.
- `--lr-textarea-fill` (default `transparent`) and `--lr-textarea-border-color` (default
  `var(--lr-color-border)`) — the field's background and border color, whose private defaults
  change per `appearance` rather than per `size`. The documented defaults are
  `appearance="outlined"`'s private values. Set either on an ancestor or directly on the host to
  retune the surface without a `::part(textarea)` rule; the public value remains authoritative
  across appearances.
- `--lr-textarea-hover-border-color` (default `var(--lr-color-brand)`) — the field border while the
  native textarea is hovered, independent of its resting border and every other brand-colored
  component state.
- `--lr-textarea-focus-border-color` (default `var(--lr-textarea-border-color)`) — the field
  border while the native textarea is focused. Unset, it resolves to this field's own resting
  border color, so a textarea with no override renders exactly as before this hook existed; set it
  to give focus its own border color independent of the hover color above and the halo below.
- `--lr-form-control-focus-shadow` (default `none`) — the shared field halo, painted as a
  `box-shadow` while the field holds focus. One name for every field-shaped control in the library,
  so a halo is configured once instead of per component; additive, so the `:focus-visible` outline
  is untouched.

`<lr-textarea>` now fills a definite-height host: place it inside a container with a resolved
block size (a flex/grid item stretched to a track, or an ancestor with an explicit height) and
the field's `form-control` → `textarea-wrapper` → the native `<textarea>` chain fills it instead
of staying at its `rows`-derived intrinsic height. This has no effect and needs no opt-in for the
ordinary case (an unsized host) — `resize="auto"`'s own growth and its
`--lr-textarea-max-block-size` cap are unaffected either way.

**Additional API surface:**

- `click()` — Activates the internal textarea.

---
