# Text area

A text box for several lines: the input well grown tall, with a grip at its corner to make it taller.

Block: `smbk-text-area`. Status: candidate. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `error` | on / off | off | The value is invalid. Draws the danger border, kept while focused, and the error icon at the top end; the control also carries aria-invalid. |

Parts: `smbk-text-area__control`, `smbk-text-area__grip`

## Markup

```html
<span class="smbk-input smbk-input--size-md smbk-text-area">
  <textarea
    class="smbk-input__control smbk-text-area__control"
    rows="2"
    data-scrollbar=""
    placeholder="Tell us what happened"
    aria-label="What happened"
  ></textarea>
  <i class="ph ph-notches smbk-icon smbk-icon--size-md smbk-text-area__grip" aria-hidden="true"></i>
</span>
```

## React

```tsx
import { TextArea } from './design-system/components/text-area/text-area.js';

<TextArea placeholder="Tell us what happened" aria-label="What happened" />
```

## Stylesheet

```css
/* smbk-text-area — Text area. Figma "TextArea" 4800:64744.
 * Built on the input: the block always sits on an smbk-input well and the
 * textarea is the input's control, so the well's chrome, its text style and
 * its hover, focus, error, read-only and disabled looks are the input's own
 * and follow any change to it. Figma binds the same fills, borders, shadows
 * and text in every state. This stylesheet adds only what several lines need:
 * the well grows with the control instead of keeping the control height, the
 * error icon sits at the top end, and a grip at the corner shows the box can
 * be made taller. */

.smbk-input.smbk-text-area {
  position: relative;
  align-items: stretch;
  height: auto;
  padding: 0;
}

/* The padding is on the control, so the browser's own resize handle sits in
   the well's corner. */
.smbk-text-area__control {
  box-sizing: border-box;
  display: block;
  width: 100%;
  min-height: calc(1lh + 2 * var(--smbk-space-inset-lg));
  margin: 0;
  padding: var(--smbk-space-inset-lg);
  resize: vertical;
  /* The system's scrollbar: thin, in the subtle border color, on a clear track. */
  scrollbar-width: thin;
  scrollbar-color: var(--smbk-color-border-subtle) transparent;
}

.smbk-text-area__control:disabled,
.smbk-text-area__control[readonly] {
  resize: none;
}

/* In error the text stops short of the icon at the top end, which stays on the first line's middle
   while the text scrolls under it. */
.smbk-input--error .smbk-text-area__control {
  padding-inline-end: calc(var(--smbk-space-inset-lg) + var(--smbk-size-icon-md));
}

.smbk-text-area .smbk-input__error-icon {
  position: absolute;
  inset-block-start: var(--smbk-space-inset-lg);
  inset-inline-end: var(--smbk-space-inset-lg);
  height: calc(var(--smbk-typography-body-md-font-size) * var(--smbk-typography-body-md-line-height));
  pointer-events: none;
}

/* Figma's grip, the notches icon, a hairline step in from the corner. It only
   draws: the browser's resize handle underneath takes the pointer. Where the
   browser cannot hide its own handle (Firefox), its own handle shows instead. */
.smbk-text-area__grip {
  display: none;
  position: absolute;
  inset-block-end: var(--smbk-space-inset-xs);
  inset-inline-end: var(--smbk-space-inset-xs);
  color: var(--smbk-color-text-subtlest);
  font-size: var(--smbk-size-icon-xs);
  pointer-events: none;
}

@supports selector(::-webkit-resizer) {
  .smbk-text-area__control::-webkit-resizer {
    background: transparent;
  }

  .smbk-text-area__grip {
    display: block;
  }
}

.smbk-text-area:has(.smbk-input__control:disabled) .smbk-text-area__grip {
  color: var(--smbk-color-text-disabled);
}

/* The scrollbar drawn over the control (scrollbar.ts) stops short of the corner, where the grip and the browser's
   resize handle are. */
.smbk-text-area > .smbk-scrollbar .smbk-scrollbar__track--y {
  inset-block-end: calc(var(--smbk-space-inset-xs) + var(--smbk-size-icon-xs));
}

/* A read-only text area cannot be made taller, so it has no grip, as Figma draws it. */
.smbk-text-area:has(.smbk-input__control[readonly]) .smbk-text-area__grip {
  display: none;
}
```

## Tokens it reads

```json
{
  "space": {
    "inset": {
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Padding inside a component, large."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{space.4}}",
        "$description": "Padding inside a component, extra small."
      }
    }
  },
  "color": {
    "border": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.300}}",
        "$description": "Dividers and quiet edges."
      }
    },
    "text": {
      "subtlest": {
        "$type": "color",
        "$value": "{{color.grey.600}}",
        "$description": "Quietest text: hints, captions, placeholders."
      },
      "disabled": {
        "$type": "color",
        "$value": "{{color.grey.500}}",
        "$description": "Text in unavailable controls."
      }
    }
  },
  "size": {
    "icon": {
      "md": {
        "$type": "dimension",
        "$value": "{{size.20}}",
        "$description": "Icon box, medium."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{size.12}}",
        "$description": "Icon box, extra small."
      }
    }
  },
  "typography": {
    "body-md": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "16px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    }
  }
}
```

## Purpose

A text box for answers that run to sentences: a message, a description, a note. It is the input's well grown tall, so it looks and reacts exactly like the input, with a grip in its corner that the reader can drag to make it taller. For a single short value, use the input.

## Anatomy

- The block, the well. It always sits on the input's block, which draws the sunken surface, the hairline highlight edge, the inset shadow and every state.
- The control part, the native textarea, carrying the input's control part as well. It holds the padding, so the browser's resize handle sits in the well's corner.
- The grip part, the notches icon a hairline step in from the corner. It only draws; the browser's handle under it takes the pointer.
- In error, the input's error icon, a warning sign at the top end, on the first line.

## When to use

- Free text likely to wrap or hold several sentences: a support request, a comment, a project description.

## When not to use

- A name, an email, a number or any other short value. Use the [input](input.md); a tall box asks for more than is wanted.
- Rich text with formatting. That needs an editor, which this system does not have.
- A choice from known values. Use the [select](select.md), [radios](radio.md) or [checkboxes](checkbox.md).

## Behavior

It starts two rows tall, the height Figma draws, and takes any other number of rows. The reader can drag the corner to make it taller; its width follows the layout. Text longer than the box scrolls inside it with the system's scrollbar: a thin, see-through bar over its text that takes no room and shows while the mouse is over it or it scrolls, stopping short of the corner the reader drags. The text is the input's body text. Hover lifts the well to the raised surface, focus draws the field focus border, and the error flag draws the danger border, which stays while focused, puts the warning icon at the top end with the text stopping short of it, and marks the control invalid. A read-only text area sits flat on the page with a faint edge, has no grip and cannot be resized; its text can be selected and copied. A disabled text area grays the well, cannot be typed in and cannot be resized. All of these are the input's own looks, so a change to the input reaches the text area.

Firefox cannot hide its own resize handle, so there the grip is not drawn and Firefox's handle shows in the corner instead.

## Accessibility
### What it does for you

- It is the browser's own textarea, so typing, pasting, undo, selection and line breaks behave as everywhere else.
- The error flag sets aria-invalid.
- The grip is decorative and hidden from assistive technology; the browser's own handle does the resizing.

### What you must do

- Give it a label: the field component around it gives one, or the label component with the for attribute. In development, a text area with no name logs a warning in the console.
- Let the field link the message that explains an error.
- Never use placeholder text as a label: it disappears when typing starts.

### How to test

- Tab to it: the focus border shows, in both themes and in a forced-color mode. Type, paste, undo and break lines as in any text box.
- With a screen reader, it is read as a text box by its label, and an error as invalid with its message.
- With text at 200% and the page 320 pixels wide, the box follows the width the layout gives it and long text scrolls inside it.

### Keyboard

| Key | What it does |
|---|---|
| Tab | Moves focus to the text area, and on from it. |
| Enter | Starts a new line. |

## Usage rules

### Do

- Set the starting rows to the length of answer expected, so the box says how much to write.
- Say any length limit before the reader starts, in the field's help text.

### Don't

- Use it for a short single value.
- Clear what the reader wrote when the form shows an error.
- Round it into a capsule. Its corner is a control corner, as every field's is; a capsule reads as a one-line search or a tag.
