# Text area with budget
> Auto-growing text area with a pixel character budget that turns amber near the limit and blinks red when over, marking the form invalid.
- React: `import { PxTextarea } from "@/components/lumesec/px-textarea"`
- Collection: Pixel UI (https://elements.lumesec.ai/components/pixel-ui)
- Registry item: https://elements.lumesec.ai/r/px-textarea.json
- Page: https://elements.lumesec.ai/components/pixel-ui/textarea



Live preview: https://elements.lumesec.ai/view/px-textarea

Demo source:

```tsx
import { PxTextarea } from "@/components/lumesec/px-textarea";

export default function PxTextareaDemo() {
  return (
    <div className="w-full rounded-[14px] border border-border bg-card px-5 py-[18px] shadow-[0_14px_34px_-20px_rgb(0_0_0/0.4)]">
      <PxTextarea name="bio" label="Short bio" limit={120} placeholder="A sentence or two about you" />
    </div>
  );
}
```

## Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

## Installation

```bash
npx shadcn@latest add @lumesec/px-textarea
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/px-textarea.json
```

## Usage

React:

```tsx
import { PxTextarea } from "@/components/lumesec/px-textarea";

export function Example() {
  return <PxTextarea name="bio" label="Short bio" limit={120} placeholder="A sentence or two about you" />;
}
```

## Behaviour

Grows with its content and shows the character budget as pixels that turn amber near the limit. Going over is allowed but blinks red and marks the form invalid.

## API reference

### Props

Also accepts every prop of `<div>` (`React.ComponentProps<"div">`), spread onto the root element.

| Prop            | Type                      | Default | Description                                                                                   |
| --------------- | ------------------------- | ------- | --------------------------------------------------------------------------------------------- |
| `value`         | `string`                  | —       | Controlled text. Use with `onValueChange`.                                                    |
| `defaultValue`  | `string`                  | `""`    | Initial text when uncontrolled. A form reset restores it.                                     |
| `limit`         | `number`                  | `280`   | Character budget. Typing past it is allowed but marks the field invalid.                      |
| `label`         | `React.ReactNode`         | —       | Label above the field, linked to the textarea.                                                |
| `placeholder`   | `string`                  | —       | Placeholder of the textarea.                                                                  |
| `name`          | `string`                  | —       | Form field name; the text submits under it.                                                   |
| `disabled`      | `boolean`                 | `false` | Disables the textarea and dims the field.                                                     |
| `required`      | `boolean`                 | `false` | Marks the textarea as required for form validation.                                           |
| `readOnly`      | `boolean`                 | `false` | Makes the textarea read-only.                                                                 |
| `onValueChange` | `(value: string) => void` | —       | Called on every edit with the new text.                                                       |
| `onValueCommit` | `(value: string) => void` | —       | Called when the textarea commits a change: on blur after editing (the native `change` event). |

### Ref

`ref` points at the root `HTMLDivElement`.

## Accessibility

* A native `<textarea>` labelled by a `<label htmlFor>`.
* The counter ("40 left", or "−5" when over) is linked with `aria-describedby` and is an `aria-live="polite"` region.
* Over the limit the textarea has `aria-invalid` and a custom validity message.
* The pixel budget bar is `aria-hidden`.
* A native `<textarea>` submits the text under `name`. Over `limit` it sets the custom validity message "Keep it under N characters", which blocks submission and anchors the browser's validation bubble to the field. Form reset restores `defaultValue` and calls `onValueChange`.
* Reduced motion: the budget bar jumps to the new length and the over-limit pixels do not blink.

## Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--foreground`, `--muted-foreground`, `--card`, `--border`, `--lumesec`, `--lumesec-warning` and `--destructive`.

## Notes

* The textarea grows with its content from a minimum height of 84 px, refits when its width changes, and has no resize handle.
* The root carries `data-zone` set to `ok`, `warn` (from 80% of the limit) or `over`.
* `className` and other `div` props go to the wrapper; textarea attributes are the props listed above.


