Skip to content

Textarea

Terminal-styled multi-line text area with size variants, resize handles that sit outside the scroll area, and a scrollbar that keeps the final line reachable.

Preview

Installation

$pnpm exec dgadd add ui/textarea
[Installs to]src/components/ui/textarea[Item]ui/textarea

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { Field } from "@/components/ui/field";import { Textarea } from "@/components/ui/textarea";export default function TextareaDefault() {  return (    <Field className="w-full max-w-md">      <Field.Label>Commit message</Field.Label>      <Field.Control>        <Textarea          defaultValue={            "fix: keep focus above error in field chrome\n\nRing on focus; tint at rest.\n\nScroll to review the full message, or drag the handle below the field to grow it."          }        />      </Field.Control>      <Field.Description>Scroll inside the field, or drag the handle below it.</Field.Description>    </Field>  );}

Examples

Resize directions

Preview

Variants

Preview

Focus

Preview

API Reference

Textarea

NameTypeDefaultDescription
size"sm" | "md" | "lg""md"Padding, font-size, and min-height token. Resize behavior is configured independently.
resize"none" | "vertical" | "horizontal" | "both""vertical"Axes/edges that expose a resize handle. Read-only and disabled textareas suppress every handle.
resizeHandleTextareaResizeHandle | { vertical?: TextareaResizeHandle; horizontal?: TextareaResizeHandle }"line"How each resize handle is drawn: "line" for a 30px mark held off the field border, "box" for an arrow-only chip centered on the border, or "box-label" for that chip with a RESIZE label. A single value styles both edges; an object styles each edge independently.
resizeLabels{ vertical?: string; horizontal?: string }"Resize textarea vertically" / "Resize textarea horizontally"Accessible names for the resize handles, per axis.
aria-invalidboolean | "true" | "false" | "grammar" | "spelling"The native ARIA invalid state. The error border applies to true/"true"; the error ring joins on focus. Field.Control sets this automatically when Field.invalid is true.

Accessibility

Notes

Resize Direction

Set resize to "vertical", "horizontal", "both", or "none". The default vertical mode puts a handle on the bottom edge, horizontal puts one on the right edge, and both exposes each edge independently and leaves the corner between them empty. Every handle is a button: drag it, or focus it and press ArrowDown/ArrowUp (vertical) or ArrowRight/ArrowLeft (horizontal) to step by 8px, or press Enter or Space to grow by the same step. Each resizable edge reserves a 16px band outside the field, so the scrollbar stays inside the textarea and can reach the final line.

Resize Handle Style

Pass resizeHandle to choose how each handle is drawn. "line" is a 30px mark centered on the edge and held 10px clear of the field border, so it paints nothing over the field. "box" is a 20px chip carrying only the direction arrow, centered on the field border: the border runs unbroken behind it and half the chip overlaps the field at the edge midpoint. "box-label" is the same chip widened to carry a RESIZE label beside the arrow. Give one value to style both edges, or an object with vertical and horizontal keys to style them independently. The drawing is decorative; the handle button keeps its accessible label and keyboard behavior either way.

Read-only vs Disabled

Textarea styles the two states on different channels so they cannot be confused. Disabled keeps a faint surface, dashed edge, and muted ink: the field is not part of this form. Read-only uses a subtle recessed surface plus an inset start rail while preserving full-contrast ink: the value matters, it just cannot be edited. A read-only field stays focusable and copyable, and the focus ring still applies on top. Both states suppress every resize handle.

Invalid State

Set aria-invalid=true to apply the error border. The error ring joins on focus, so focus stays the strongest edge a field shows. Field.Control sets aria-invalid automatically when Field.invalid is true.

Source

Install via CLI: pnpm exec dgadd add ui/textarea.

Highlighted source loads after this disclosure opens. Browse the source repository.