Textarea

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless textarea
Source View Source

Overview of the Phoenix and LiveView Headless Textarea Component

The Mishka Chelekom Headless Textarea is a multi-line text control for Phoenix forms. It ships no styling. The root is a div that acts as the control box, and the native textarea sits inside it. Paint the border and background on the root and leave the textarea transparent, and a browser resize handle will not cut across the border.

Hand it a form field with field={@form[:bio]} and it takes its id, name, value and errors from the form. Without a field, pass name and value yourself. With autosize the box grows with its content between min_rows and max_rows, driven by the Autosize LiveView hook. Without autosize there is no JavaScript at all. Each part takes its own class attribute: class for the root and textarea_class for the textarea.

Base UI Textarea example - multi-line field (Phoenix headless)

Base UI Textarea example - multi-line field (Phoenix headless)

heex
<.textarea
  id="baseui-textarea-hero"
  name="bio"
  rows={3}
  placeholder="Tell us about yourself"
  class="w-full max-w-80 rounded-md border border-neutral-200 bg-white text-sm text-neutral-950 dark:border-neutral-800 dark:bg-neutral-950 dark:text-white focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-neutral-950 dark:focus-within:outline-white"
  textarea_class="w-full bg-transparent p-3 outline-none"
/>

Base UI Textarea example - autosize between two and six rows (Phoenix headless)

Base UI Textarea example - autosize between two and six rows (Phoenix headless)

heex
<.textarea
  id="baseui-textarea-autosize"
  name="notes"
  autosize
  min_rows={2}
  max_rows={6}
  placeholder="Keep typing, this grows to six rows, then scrolls"
  class="w-full max-w-80 rounded-md border border-neutral-200 bg-white text-sm text-neutral-950 dark:border-neutral-800 dark:bg-neutral-950 dark:text-white focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-neutral-950 dark:focus-within:outline-white"
  textarea_class="w-full bg-transparent p-3 outline-none"
/>

Using it in a form

heex
<.form for={@form} phx-change="validate" phx-submit="save">
  <.textarea
    field={@form[:bio]}
    rows={3}
    placeholder="Tell us about yourself"
    class="..."
    textarea_class="..."
  />
</.form>

With field, errors show up only after the user has touched the field, so an untouched form shows no errors. Pass errors yourself with a plain name and a non-empty list marks the control invalid. valid marks it valid, and describedby sets aria-describedby for a hint or an error message. disabled, readonly and required map to the native attributes, and readonly blocks editing but keeps the field focusable. Attributes such as maxlength, minlength, autocomplete, autofocus, spellcheck, wrap, form and phx-change pass through to the textarea.

Rows, autosize and resize

rows sets the visible rows. When you leave it out, the textarea starts at min_rows, which defaults to 2. With autosize the hook measures the content on every input and after every server patch, and sets the height between min_rows and max_rows. Once max_rows is reached the textarea scrolls instead of growing. Leave max_rows unset and it grows without a limit.

resize is vertical (the default), none, horizontal or both. It writes data-resize on the textarea and leaves the styling to you, for example resize-y data-[resize=none]:resize-none data-[resize=horizontal]:resize-x data-[resize=both]:resize. While autosize is on, resize is forced to none, because a resize handle and an autosizing box would overwrite each other's height.

Parts & data attributes

Each part carries a stable data-part and a chelekom-textarea* class you can target. The root reflects the state of the control.

Partdata-partState / ARIAPurpose
Rootrootdata-invalid, data-valid, data-disabled, data-readonly, data-required, data-autosizeThe control box. Paint the border and background here.
Textareatextareaaria-invalid, aria-describedby, data-resize, data-min-rows and data-max-rows (with autosize)The native textarea. It carries the Autosize hook only when autosize is set.

Accessibility

The control is a native textarea, so Tab focuses it, Enter adds a new line and text entry works as in any browser. Give it a visible label, for example with a Field, and use describedby for hints and errors. An invalid control sets aria-invalid="true". Autosize changes the height only, so keyboard and screen reader behavior stays native.