Textarea
Multiline input with optional auto-resize, packaged with its label and help/error text. Form-associated via ElementInternals; emits a composed change event.
Preview
<esa-textarea label="Description" placeholder="Describe the project…" help-text="Plain text only."></esa-textarea> Rows + auto-resize
Set a fixed rows count, or add auto-resize to grow with content up to max-rows.
<esa-textarea label="Fixed 5 rows" rows="5" placeholder="Five rows tall"></esa-textarea>
<esa-textarea label="Auto-resize" auto-resize max-rows="8" placeholder="Grows as you type, up to 8 rows"></esa-textarea> Sizes
<esa-textarea label="Extra small" size="xs" placeholder="Extra small"></esa-textarea>
<esa-textarea label="Small" size="sm" placeholder="Small"></esa-textarea>
<esa-textarea label="Medium" size="md" placeholder="Medium"></esa-textarea>
<esa-textarea label="Large" size="lg" placeholder="Large"></esa-textarea> States
<esa-textarea label="Required" required placeholder="Cannot be empty"></esa-textarea>
<esa-textarea label="With error" error-text="A description is required." required></esa-textarea>
<esa-textarea label="Disabled" disabled value="Read only content"></esa-textarea> Form participation
The element is form-associated. Submitting logs the FormData entry for notes.
<form>
<esa-textarea label="Notes" name="notes" placeholder="Type then submit"></esa-textarea>
<button type="submit">Submit</button>
</form> API
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | '' | Visible label rendered above the textarea. |
value | string | '' | Current value; mirrored to the form on every input. |
placeholder | string | '' | Placeholder shown when empty. |
rows | number | 3 | Initial visible row count. |
auto-resize | boolean | false | Grows the textarea to fit content as the user types. |
max-rows | number | 10 | Row cap when auto-resize is on. |
help-text | string | '' | Helper text shown below the textarea. |
error-text | string | '' | Error message; replaces help text and turns the field red when present. |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Control size. |
required | boolean | false | Marks the field required and renders an asterisk on the label. |
disabled | boolean | false | Disables interaction. |
Events
| Event | Type | Default | Description |
|---|---|---|---|
change | CustomEvent<{ value: string }> | — | Fired on every input. Composed and bubbling; detail.value is the current string. |
Accessibility
- Renders a native
<textarea>associated to its<label>viafor/id. - Sets
aria-invalid="true"whileerror-textis present. - The required asterisk carries an
aria-label="required". - Visible focus ring via
--focus-ring-*tokens.
Theming surface
Every public token this component reads, extracted from its source at build time.
The Resolves to column walks each token's real lineage —
component → semantic → primitive → raw value — so you
can see exactly where re-pointing it lands. A spoke re-skins by overriding these in
its theme-<slug>.css; component internals are never edited. No
hook for what you need? File it with /request-lego.
| Token | Tier | Resolves to |
|---|---|---|
--focus-ring-color | component | #65ba74 via --color-border-focus → --color-grass-8 |
--focus-ring-width | component | 2px |
--form-bg | component | #fcfcfc via --color-surface → --color-gray-1 |
--form-bg-disabled | component | #f0f0f0 via --color-disabled-bg → --color-gray-3 |
--form-border-color | component | #cecece via --color-border → --color-gray-7 |
--form-border-color-error | component | #e5484d via --color-danger → --color-red-9 |
--form-border-color-focus | component | #65ba74 via --color-border-focus → --color-grass-8 |
--form-border-width | component | 1px |
--form-error-color | component | #ce2c31 via --color-danger-strong → --color-red-11 |
--form-font-size-lg | component | clamp(0.875rem, 0.77rem + 0.52vw, 1.125rem) via --type-size-300 |
--form-font-size-md | component | clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) via --type-size-200 |
--form-font-size-sm | component | clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --type-size-100 |
--form-font-size-xs | component | clamp(0.5rem, 0.44rem + 0.3vw, 0.625rem) via --type-size-050 |
--form-help-color | component | #838383 via --color-text-muted → --color-gray-10 |
--form-help-gap | component | 0.25rem via --spacing-100 |
--form-label-color | component | #646464 via --color-text-secondary → --color-gray-11 |
--form-label-gap | component | 0.25rem via --spacing-100 |
--form-line-height | component | 1.6 via --line-height-normal |
--form-padding-x-lg | component | 1rem via --spacing-400 |
--form-padding-x-md | component | 0.75rem via --spacing-300 |
--form-padding-x-sm | component | 0.625rem via --spacing-250 |
--form-padding-x-xs | component | 0.5rem via --spacing-200 |
--form-padding-y-lg | component | 0.75rem via --spacing-300 |
--form-padding-y-md | component | 0.5rem via --spacing-200 |
--form-padding-y-sm | component | 0.375rem via --spacing-150 |
--form-padding-y-xs | component | 0.25rem via --spacing-100 |
--form-placeholder-color | component | #838383 via --color-text-muted → --color-gray-10 |
--form-radius-lg | component | 0.5rem via --radius-200 |
--form-radius-md | component | 0.5rem via --radius-200 |
--form-radius-sm | component | 0.25rem via --radius-100 |
--form-radius-xs | component | 0.25rem via --radius-100 |
--form-text-color | component | #202020 via --color-text-primary → --color-gray-12 |
--color-danger-strong | semantic | #ce2c31 via --color-red-11 |
--font-sans | semantic | 'DM Sans', sans-serif |
--transition-fast | semantic | 150ms ease |
--type-size-050 | semantic | clamp(0.5rem, 0.44rem + 0.3vw, 0.625rem) |
--type-size-100 | semantic | clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) |
--type-size-150 | semantic | clamp(0.6875rem, 0.61rem + 0.38vw, 0.875rem) |
--type-size-200 | semantic | clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) |
--type-size-300 | semantic | clamp(0.875rem, 0.77rem + 0.52vw, 1.125rem) |
--font-weight-medium | primitive | 500 |