Skip to content

Type to search the docs, API, and blog.

↑↓ navigate↵ openesc close
Zazz 0.7 alpha
API navigation

API

Textarea

Multi-line text input.

Installation

pnpm dlx zazz-ui add textarea
npx zazz-ui add textarea
yarn dlx zazz-ui add textarea
bunx zazz-ui add textarea

Also adds fields, which it depends on. Run zazz-ui init first if the project has no zazz/ folder yet.

Examples

<div data-ui="field">
  <label data-field-slot="label" for="f-message">Message</label>
  <textarea
    data-ui="textarea"
    id="f-message"
    name="message"
    inputmode="text"
    placeholder="Your message"
  ></textarea>
</div>
/**
 * textarea.css — Textarea ([data-ui~="textarea"])
 *
 * @layer      variables, components
 * @requires   _layers.css, _variables.css, _properties.css (--_*-resolved, --_focus-shadow), fields.css
 * @uses       field-sizing: content — auto-grow with text (modern browsers;
 *             falls back to fixed min/max block-size where unsupported)
 * @tokens     --ui-textarea-* (@layer variables;
 *             --ui-textarea-min-h / -max-h default to lh units and are read raw;
 *             surface colors read the --ui-field-* family directly)
 */

@layer variables {
  :root {
    --ui-textarea-w: 100%;
    --ui-textarea-min-w: 0;
    --ui-textarea-min-h: 5lh;
    --ui-textarea-max-h: 12lh;
    --ui-textarea-p: var(--ui-field-px);
    --ui-textarea-font-size: var(--ui-field-font-size);
    --ui-textarea-leading: var(--leading-sm);
    --ui-textarea-border-width: var(--ui-field-border-width);
    --ui-textarea-border-style: var(--ui-field-border-style);
    --ui-textarea-border-color: var(--ui-field-border-color);
    --ui-textarea-rounded: var(--ui-field-rounded);
    --ui-textarea-resize: vertical;
  }
}

@layer ui.components {
  :where([data-ui~="textarea"]) {
    field-sizing: content;
    /* dual-mode metrics go through the registered typed pair */
    --_w-len: var(--_w-resolved, var(--ui-textarea-w));
    --_w-num: var(--_w-resolved, var(--ui-textarea-w));
    inline-size: calc(var(--_w-len) + var(--_w-num) * var(--spacing));
    --_min-w-len: var(--_min-w-resolved, var(--ui-textarea-min-w));
    --_min-w-num: var(--_min-w-resolved, var(--ui-textarea-min-w));
    min-inline-size: calc(var(--_min-w-len) + var(--_min-w-num) * var(--spacing));
    min-block-size: var(--_min-h-resolved, var(--ui-textarea-min-h));
    max-block-size: var(--_max-h-resolved, var(--ui-textarea-max-h));
    --_p-len: var(--_p-resolved, var(--ui-textarea-p));
    --_p-num: var(--_p-resolved, var(--ui-textarea-p));
    padding: calc(var(--_p-len) + var(--_p-num) * var(--spacing));

    font-size: var(--_font-size-resolved, var(--ui-textarea-font-size));
    line-height: var(--_leading-resolved, var(--ui-textarea-leading));
    color: var(--_text-resolved, var(--ui-field-text));
    background-color: var(--_bg-resolved, var(--ui-field-bg));

    border-width: var(--_border-width-resolved, var(--ui-textarea-border-width));
    border-style: var(--_border-style-resolved, var(--ui-textarea-border-style));
    border-color: var(--_border-color-resolved, var(--ui-textarea-border-color));
    border-radius: var(--_rounded-resolved, var(--ui-textarea-rounded));
    resize: var(--ui-textarea-resize);

    /* The ring is published to --_focus-shadow so a --shadow utility keeps
       it; the transparent outline twin keeps focus visible in forced-colors */
    --_ring-offset-width: 0px;
    --_ring-width: 0px;
    --_focus-in: max(0, -1 * sign(var(--_focus-ring-w, 1px)));
    --_ring-fill: color-mix(in oklch, var(--ui-field-ring-color) var(--ring-opacity), transparent);
    --_focus-shadow:
      0 0 0 calc(var(--_ring-offset-width) * (1 - var(--_focus-in))) var(--ring-offset-shadow-color),
      0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * (1 - var(--_focus-in)))
        var(--_ring-fill, var(--ring-shadow-color)),
      inset 0 0 0 calc(var(--_ring-offset-width) * var(--_focus-in)) var(--ring-offset-shadow-color),
      inset 0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * var(--_focus-in))
        var(--_ring-fill, var(--ring-shadow-color));
    box-shadow: var(--_focus-shadow, 0 0 #0000), var(--_shadow-resolved, 0 0 #0000);
    outline: abs(var(--_focus-ring-w, var(--outline-width))) var(--outline-style) transparent;
    outline-offset: calc(
      var(--outline-offset) - var(--_focus-in) *
        (2 * var(--outline-offset) + abs(var(--_focus-ring-w, var(--outline-width))))
    );

    transition: var(--default-transition);
  }

  @media (hover: hover) {
    :where([data-ui~="textarea"]):hover {
      border-color: var(--_border-color-resolved, var(--ui-field-border-color--hover));
      background-color: var(--_bg-resolved, var(--ui-field-bg--hover));
    }
  }

  :where([data-ui~="textarea"]):focus-visible {
    background-color: var(--_bg-resolved, var(--ui-field-bg--focus-within));
    border-color: var(--_border-color-resolved, var(--ui-field-border-color--focus-within));
    --_ring-offset-width: var(--ring-offset-width);
    --_ring-width: abs(var(--_focus-ring-w, var(--ring-width)));
    outline-color: transparent;
  }
}

/* an invalid control remaps the --ui-field-* family on itself */
@layer ui.components {
  :where([data-ui~="textarea"]):disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  :where([data-ui~="textarea"]):user-invalid {
    --ui-field-bg: color-mix(in oklch, var(--color-destructive) 5%, var(--color-input));
    --ui-field-bg--hover: color-mix(in oklch, var(--color-destructive) 10%, var(--color-input));
    --ui-field-border-color: var(--color-destructive);
    --ui-field-border-color--hover: var(--color-destructive);
    --ui-field-border-color--focus-within: var(--color-destructive);
    --ui-field-ring-color: var(--color-destructive);

    border-color: var(--color-destructive);
  }
}

Attributes

textarea attributes
AttributeKindValuesNotes
data-ui="textarea"Identityfree valueIdentity token in data-ui.

Requires: fields.

Hooks

Set a hook on :root, on a subtree, or inline on one element. A state hook (--…--hover) themes that state only; the primitive keeps its other states.

textarea hooks
HookDefaultState
--ui-textarea-border-colorvar(--ui-field-border-color)—
--ui-textarea-border-stylevar(--ui-field-border-style)—
--ui-textarea-border-widthvar(--ui-field-border-width)—
--ui-textarea-font-sizevar(--ui-field-font-size)—
--ui-textarea-leadingvar(--leading-sm)—
--ui-textarea-max-h12lh—
--ui-textarea-min-h5lh—
--ui-textarea-min-w0—
--ui-textarea-pvar(--ui-field-px)—
--ui-textarea-resizevertical—
--ui-textarea-roundedvar(--ui-field-rounded)—
--ui-textarea-w100%—