Textarea

Multi-line text field.

Forms Textarea.jsx Textarea.d.ts Textarea.md

Playground

Change a prop and watch it. The code below follows along.

Install and import

Textarea is in @dovetail-ds/react. Install it with React, if you haven't:

npm install @dovetail-ds/react react react-dom
import { Textarea } from "@dovetail-ds/react";

Import @dovetail-ds/react/styles.css once at your app's root, then your theme after it. Setup covers the stylesheets, fonts and dark mode.

Guidelines

Multi-line text field.

Use it when

  • The answer is a sentence or longer: a description, a message, a note.

Don't use it when

  • The answer is one short value. Use Input; a large box invites a long answer.

Example

``jsx <Textarea label="What changed?" rows={5} hint="Shown in the changelog" /> ``

Horizontal resize is disabled: a textarea wider than its container breaks the layout and the user gains nothing.

Tokens

--dt-input-*, shared with Input and Select.

Props

The typed contract, from Textarea.d.ts.

import * as React from "react";

/** Multi-line text field. Resizes vertically only. */
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
  label?: string;
  hint?: string;
  error?: string;
  required?: boolean;
  /** @default 4 */
  rows?: number;
}

export declare function Textarea(props: TextareaProps): React.JSX.Element;

Tokens it reads

Every custom property Textarea.jsx resolves, read from the source: 12 component, 1 semantic, 1 primitive. Component tier first, because that is the one to reach for.

  • component Its own tier. Override one of these and only this component moves.
  • semantic Shared roles. Override one and everything using that role moves with it.
  • primitive Raw values. A component reading one directly is a deliberate exception.
TokenTierDeclared as
--dt-input-bgcomponentvar(--dt-surface-base)
--dt-input-bg-disabledcomponentvar(--dt-surface-disabled)
--dt-input-bordercomponentvar(--dt-border-default)
--dt-input-border-errorcomponentvar(--dt-surface-danger)
--dt-input-border-focuscomponentvar(--dt-focus-ring-color)
--dt-input-border-widthcomponentvar(--dt-border-width-default)
--dt-input-fgcomponentvar(--dt-text-primary)
--dt-input-font-familycomponentvar(--dt-text-body-md-family)
--dt-input-font-sizecomponentvar(--dt-font-size-sm)
--dt-input-padding-xcomponentvar(--dt-space-inset-sm)
--dt-input-radiuscomponentvar(--dt-radius-control)
--dt-input-transitioncomponentvar(--dt-motion-micro)
--dt-space-inset-xssemanticvar(--dt-dim-2)
--dt-line-height-smprimitive20px

Source

React only, no dependencies, every value a token.

import React from "react";
import { Field } from "./Field.jsx";

export function Textarea({ label, hint, error, required, rows = 4, id, style, ...rest }) {
  const [focus, setFocus] = React.useState(false);
  const auto = React.useId();
  const areaId = id || auto;
  const border = error ? "var(--dt-input-border-error)" : focus ? "var(--dt-input-border-focus)" : "var(--dt-input-border)";

  const control = (
    <textarea
      id={areaId}
      rows={rows}
      required={required}
      aria-invalid={error ? true : undefined}
      onFocus={() => setFocus(true)}
      onBlur={() => setFocus(false)}
      style={{
        width: "100%",
        padding: "var(--dt-space-inset-xs) var(--dt-input-padding-x)",
        fontFamily: "var(--dt-input-font-family)",
        fontSize: "var(--dt-input-font-size)",
        lineHeight: "var(--dt-line-height-sm)",
        color: "var(--dt-input-fg)",
        background: rest.disabled ? "var(--dt-input-bg-disabled)" : "var(--dt-input-bg)",
        border: `var(--dt-input-border-width) solid ${border}`,
        borderRadius: "var(--dt-input-radius)",
        outline: "none",
        resize: "vertical",
        transition: "border-color var(--dt-input-transition)",
        boxSizing: "border-box",
      }}
      {...rest}
    />
  );

  if (!label && !hint && !error) return <div style={style}>{control}</div>;
  return <Field label={label} hint={hint} error={error} required={required} htmlFor={areaId} style={style}>{control}</Field>;
}

In context

Textarea also appears on the forms reference cards, alongside the rest of its family.