← All UI specs

Component contract

textarea

Native <textarea> wrapped as a design-system custom element — multi-line text field with rows prop, invalid+describedBy for error states, and dsInput event fired on every keystroke.

Version
v1.0.0

Props

NameTypeValuesDefault
valuestring
namestring
placeholderstring
disabledbooleanfalse
requiredbooleanfalse
invalidbooleanfalse
describedBystring
labelstring
rowsnumber3
sizeenummd, lgmd

Events

NamePayloadCondition
dsInputstring

Tokens

spacing
sm, md, lg, xl
radius
sm, md
font
family, size-md

Accessibility

native_element
textarea
labeling
aria-label via the label prop (canonical) or ambient <label> wrapper (secondary, browser-computed accessible name).
keyboard
["Tab focuses (does not insert a tab character by default)","Enter inserts a newline (does not submit the enclosing form)","Printable characters insert at caret","Backspace/Delete edit natively","Arrow keys move caret"]
disabled_semantics
native_disabled_attribute (also disables the resize handle in our CSS)
error_semantics
aria-invalid (from `invalid` prop) + aria-describedby (from `describedBy` prop, expected to point at a light-DOM node id containing the error message)
required_semantics
aria-required (from `required` prop); visual asterisk is the consumer's responsibility
notes
label prop is REQUIRED for WCAG conformance — a <textarea> with no accessible name is an axe `label` violation. `placeholder` is a hint, not a label — always pair with `label`. Native vertical resize is kept enabled (`resize: vertical`); consumers that need a pinned height can override with `resize: none`.
Raw validated JSON
{
  "name": "textarea",
  "version": "1.0.0",
  "props": {
    "value": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "placeholder": {
      "type": "string"
    },
    "disabled": {
      "type": "boolean",
      "default": false
    },
    "required": {
      "type": "boolean",
      "default": false
    },
    "invalid": {
      "type": "boolean",
      "default": false
    },
    "describedBy": {
      "type": "string"
    },
    "label": {
      "type": "string"
    },
    "rows": {
      "type": "number",
      "default": 3
    },
    "size": {
      "type": "enum",
      "values": [
        "md",
        "lg"
      ],
      "default": "md"
    }
  },
  "events": {
    "dsInput": {
      "payload": "string"
    }
  },
  "tokens_used": {
    "spacing": [
      "sm",
      "md",
      "lg",
      "xl"
    ],
    "radius": [
      "sm",
      "md"
    ],
    "font": [
      "family",
      "size-md"
    ]
  },
  "a11y": {
    "native_element": "textarea",
    "labeling": "aria-label via the label prop (canonical) or ambient <label> wrapper (secondary, browser-computed accessible name).",
    "keyboard": [
      "Tab focuses (does not insert a tab character by default)",
      "Enter inserts a newline (does not submit the enclosing form)",
      "Printable characters insert at caret",
      "Backspace/Delete edit natively",
      "Arrow keys move caret"
    ],
    "disabled_semantics": "native_disabled_attribute (also disables the resize handle in our CSS)",
    "error_semantics": "aria-invalid (from `invalid` prop) + aria-describedby (from `describedBy` prop, expected to point at a light-DOM node id containing the error message)",
    "required_semantics": "aria-required (from `required` prop); visual asterisk is the consumer's responsibility",
    "notes": "label prop is REQUIRED for WCAG conformance — a <textarea> with no accessible name is an axe `label` violation. `placeholder` is a hint, not a label — always pair with `label`. Native vertical resize is kept enabled (`resize: vertical`); consumers that need a pinned height can override with `resize: none`."
  }
}