Textarea

ui-textarea wraps a native <textarea>. It starts at rows lines, grows with its content in browsers that support field-sizing: content, and can be resized vertically. It is form-associated. Its value is submitted under its name, and its validity is the inner textarea's validity.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Textarea demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

  <div class="demo-narrow">
    <ui-textarea aria-label="Message" placeholder="Type your message here."></ui-textarea>
  </div>
</body>
</html>

Examples

With a label and description

Previewindex.html
Bio Up to 160 characters.
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Textarea demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/field.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

  <div class="demo-narrow">
    <ui-field>
      <ui-label for="textarea-bio">Bio</ui-label>
      <ui-textarea id="textarea-bio" rows="4" maxlength="160" placeholder="Tell us a little about yourself"></ui-textarea>
      <ui-field-description>Up to 160 characters.</ui-field-description>
    </ui-field>
  </div>
</body>
</html>

Disabled and read-only

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Textarea demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-stack-tight {
      display: grid;
      gap: calc(var(--spacing) * 2);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

  <div class="demo-narrow demo-stack-tight">
    <ui-textarea aria-label="Disabled" placeholder="Disabled" disabled></ui-textarea>
    <ui-textarea aria-label="Release notes" value="Version 2.0 replaces the template syntax with native slots." readonly></ui-textarea>
  </div>
</body>
</html>

Invalid

invalid sets aria-invalid and a destructive border, for errors you detect yourself.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Textarea demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

  <div class="demo-narrow">
    <ui-textarea aria-label="Feedback" value="Too short" invalid></ui-textarea>
  </div>
</body>
</html>

Character count

Listen for input to react while the user types.

Previewindex.htmltextarea-count-demo.html

/ 200

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Textarea demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

  <import-element from="./textarea-count-demo.html"></import-element>
  <textarea-count-demo></textarea-count-demo>
</body>
</html>
<custom-element name="textarea-count-demo">
  <script>
    const note = useState({ length: 0 });

    function typed(field) {
      note.length = field.value.length;
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 2);
      width: 100%;
      max-width: calc(var(--spacing) * 96);
    }
    p {
      margin: 0;
      justify-self: end;
      font-size: var(--text-xs);
      color: var(--muted-foreground);
    }
  </style>

  <ui-textarea aria-label="Note" maxlength="200" placeholder="Write a note" oninput="typed(this)"></ui-textarea>
  <p><slot name="#note.length"></slot> / 200</p>
</custom-element>

In a form

The text, including line breaks, is submitted under name. required and minlength block submission with the browser's messages, and reset returns to the value attribute. Enter adds a new line and does not submit.

Previewindex.htmltextarea-form-demo.html
Post review Reset
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Textarea demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/textarea.html"></import-element>

  <import-element from="./textarea-form-demo.html"></import-element>
  <textarea-form-demo></textarea-form-demo>
</body>
</html>
<custom-element name="textarea-form-demo">
  <script>
    const result = useState({ text: "Submit the form to see its data." });

    function submit(event) {
      event.preventDefault();
      result.text = [...new FormData(event.target)].map(([name, value]) => `${name}=${JSON.stringify(value)}`).join("\n");
    }

    function reset() {
      result.text = "The form was reset.";
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 4);
      width: 100%;
      max-width: calc(var(--spacing) * 96);
    }
    form {
      display: grid;
      gap: calc(var(--spacing) * 3);
    }
    .actions {
      display: flex;
      gap: calc(var(--spacing) * 2);
    }
    output {
      display: block;
      padding: calc(var(--spacing) * 3);
      border-radius: var(--radius-md);
      background: var(--muted);
      font-family: var(--font-mono);
      font-size: var(--text-xs);
      white-space: pre-wrap;
    }
  </style>

  <form onsubmit="submit(event)" onreset="reset()">
    <ui-textarea name="review" aria-label="Review" rows="3" minlength="10" required placeholder="What did you think?"></ui-textarea>
    <div class="actions">
      <ui-button type="submit">Post review</ui-button>
      <ui-button type="reset" variant="outline">Reset</ui-button>
    </div>
  </form>
  <output><slot name="#result.text"></slot></output>
</custom-element>

API

Attributes

Attribute Type Default Description
value string "" Current text. The attribute is the initial and reset value.
placeholder string Placeholder text.
rows number 3 Visible lines before the field grows. An integer of at least 1.
minlength number Minimum length.
maxlength number Maximum length.
disabled boolean false Disables the textarea. Also true inside a disabled <fieldset>.
readonly boolean false Makes the text read-only.
required boolean false The form is invalid while the text is empty.
invalid boolean false Sets aria-invalid="true" and the error style. Does not affect validity.
aria-label string Accessible name, passed to the inner textarea. Property name: label.

Properties

Property Type Description
value string Current text. Updated as the user types. Setting it updates the field.

Events

Event Detail Description
input none The inner textarea's native input event (composed).
change none Dispatched from the element (bubbling) when the inner textarea fires change, after it loses focus.

CSS parts

Part Description
textarea The inner native <textarea>.

Forms

ui-textarea is form-associated and submits value under its name. Validity is mirrored from the inner textarea, so required, minlength and maxlength take part in checkValidity() and reportValidity(). form.reset() restores the value attribute, and a disabled <fieldset> disables the field.

Accessibility

The host delegates focus to the inner textarea. Name it with aria-label, ui-label for or a wrapping <label>.

Edit this page on GitHub · View as Markdown