# Textarea

Use `ui-textarea` for notes, comments, descriptions, summaries, and any input that needs more vertical room than a single-line field. It inherits the shared field API for labels, help text, size, variant, readonly, disabled, and validation messaging while exposing multiline-specific sizing through `rows`.

## Import
```ts
import { TextareaComponent } from 'ui';
```

## Basic multiline fields
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TextareaComponent } from 'ui';

@Component({
  selector: 'app-textarea-basic-demo',
  standalone: true,
  imports: [FormsModule, TextareaComponent],
  template: `
    <div style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%">
      <div
        style="flex:1 1 18rem;min-width:16rem;display:flex;flex-direction:column;gap:1rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="display:flex;flex-direction:column;gap:0.25rem">
          <div style="font-size:0.9375rem;font-weight:600">General notes</div>
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Good default for comments, descriptions, and longer form answers.
          </div>
        </div>

        <ui-textarea
          label="Notes"
          placeholder="Add any notes for the team"
          [rows]="4"
          helpText="Use concise sentences when the text will be scanned by others."
        />
      </div>

      <div
        style="flex:1 1 18rem;min-width:16rem;display:flex;flex-direction:column;gap:1rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="display:flex;flex-direction:column;gap:0.25rem">
          <div style="font-size:0.9375rem;font-weight:600">Editable summary</div>
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Useful when a longer value is already present and needs refinement.
          </div>
        </div>

        <ui-textarea
          label="Release summary"
          placeholder="Summarize the update"
          [(ngModel)]="summary"
          [ngModelOptions]="{ standalone: true }"
          [rows]="5"
          helpText="The current value shows how the field behaves with existing multiline content."
        />
      </div>
    </div>
  `,
})
export class TextareaBasicDemoComponent {
  protected summary =
    'Release includes drawer motion tuning, breadcrumb responsive fixes, and search showcase updates.\nKeep the summary short and scannable.';
}
```

## Layout, size, and rows
```ts
import { Component } from '@angular/core';
import { TextareaComponent } from 'ui';

@Component({
  selector: 'app-textarea-layout-demo',
  standalone: true,
  imports: [TextareaComponent],
  template: `
    <div style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:46rem">
      <div
        style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="flex:1 1 15rem;min-width:14rem">
          <ui-textarea
            label="Compact note"
            size="small"
            inputVariant="filled-lighter"
            [rows]="3"
            placeholder="Add a short note"
          />
        </div>

        <div style="flex:1 1 15rem;min-width:14rem">
          <ui-textarea
            label="Sidebar brief"
            size="medium"
            inputVariant="filled-gray"
            [rows]="4"
            placeholder="Summarize the issue"
          />
        </div>
      </div>

      <div
        style="display:flex;flex-direction:column;gap:0.875rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="font-size:0.9375rem;font-weight:600">Primary writing area</div>
        <ui-textarea
          label="Detailed description"
          size="large"
          inputVariant="filled"
          [rows]="8"
          placeholder="Describe the request, blockers, and expected outcome"
          helpText="Use a larger textarea when writing is the main task in the view."
        />
      </div>
    </div>
  `,
})
export class TextareaLayoutDemoComponent {}
```

## Readonly, disabled, and inline error states
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TextareaComponent } from 'ui';

@Component({
  selector: 'app-textarea-states-demo',
  standalone: true,
  imports: [FormsModule, TextareaComponent],
  template: `
    <div style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%">
      <div style="flex:1 1 16rem;min-width:15rem">
        <ui-textarea
          label="Readonly incident summary"
          [readonly]="true"
          [(ngModel)]="readonlyValue"
          [ngModelOptions]="{ standalone: true }"
          [rows]="5"
          helpText="Useful when the user can review the text here but edits happen elsewhere."
        />
      </div>

      <div style="flex:1 1 16rem;min-width:15rem">
        <ui-textarea
          label="Disabled notes"
          [disabled]="true"
          [rows]="4"
          placeholder="This note field is unavailable"
          helpText="Disabled removes interaction completely."
        />
      </div>

      <div style="flex:1 1 16rem;min-width:15rem">
        <ui-textarea
          label="Inline quality check"
          [(ngModel)]="draftNote"
          [ngModelOptions]="{ standalone: true }"
          [rows]="4"
          [errorText]="draftError"
          helpText="Aim for at least 20 characters when the note should be actionable."
        />
      </div>
    </div>
  `,
})
export class TextareaStatesDemoComponent {
  protected readonlyValue =
    'Customer reported a blocking checkout issue.\nEscalated to payments team.\nWaiting for retry confirmation.';

  protected draftNote = 'Too short';

  protected get draftError(): string {
    const value = this.draftNote.trim();
    if (!value.length) {
      return '';
    }
    return value.length < 20 ? 'Add a bit more context before saving.' : '';
  }
}
```

## Validation with reactive forms
```ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { ButtonComponent, TextareaComponent } from 'ui';

@Component({
  selector: 'app-textarea-validation-demo',
  standalone: true,
  imports: [ButtonComponent, ReactiveFormsModule, TextareaComponent],
  template: `
    <form
      [formGroup]="feedbackForm"
      style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:34rem"
      (ngSubmit)="markTouched()"
    >
      <ui-textarea
        label="Reason for escalation"
        placeholder="Explain why this item should be escalated"
        [rows]="5"
        helpText="Required. Use at least 20 characters."
        formControlName="reason"
        [errorText]="reasonError"
      />

      <div style="display:flex;flex-wrap:wrap;gap:0.75rem">
        <ui-button type="submit">Validate</ui-button>
        <ui-button type="button" appearance="outline" (click)="fillExample()"
          >Use example</ui-button
        >
      </div>
    </form>
  `,
})
export class TextareaValidationDemoComponent {
  protected readonly feedbackForm = new FormGroup({
    reason: new FormControl('', {
      nonNullable: true,
      validators: [Validators.required, Validators.minLength(20)],
    }),
  });

  protected get reasonError(): string {
    const control = this.feedbackForm.controls.reason;
    if (!control.touched && !control.dirty) {
      return '';
    }
    if (control.hasError('required')) {
      return 'Reason is required.';
    }
    if (control.hasError('minlength')) {
      return 'Use at least 20 characters.';
    }
    return '';
  }

  protected markTouched(): void {
    this.feedbackForm.markAllAsTouched();
  }

  protected fillExample(): void {
    this.feedbackForm.controls.reason.setValue(
      'Escalating because the incident affects multiple enterprise customers and blocks checkout.',
    );
    this.feedbackForm.controls.reason.markAsTouched();
  }
}
```

## Brief authoring form
```ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { ButtonComponent, TextareaComponent } from 'ui';

@Component({
  selector: 'app-textarea-form-demo',
  standalone: true,
  imports: [ButtonComponent, ReactiveFormsModule, TextareaComponent],
  template: `
    <form
      [formGroup]="draftForm"
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:54rem"
    >
      <div
        style="flex:1 1 22rem;min-width:18rem;display:flex;flex-direction:column;gap:1rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <div style="display:flex;flex-direction:column;gap:0.25rem">
          <div style="font-size:0.9375rem;font-weight:600">Brief authoring form</div>
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Textareas often come in groups: short summary, context block, and handoff notes.
          </div>
        </div>

        <ui-textarea
          label="Summary"
          [rows]="3"
          placeholder="Summarize the request"
          formControlName="summary"
        />

        <ui-textarea
          label="Context"
          [rows]="5"
          placeholder="Describe background, constraints, and goals"
          formControlName="context"
          inputVariant="filled-gray"
        />

        <ui-textarea
          label="Handoff notes"
          [rows]="4"
          placeholder="Anything the next owner should know"
          formControlName="handoff"
        />

        <div
          style="display:flex;gap:0.75rem;flex-wrap:wrap;align-items:center;padding:0.75rem 0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
        >
          <ui-button type="button" variant="primary">Save brief</ui-button>
          <ui-button type="button" variant="secondary" appearance="outline" (click)="reset()">
            Reset
          </ui-button>
        </div>
      </div>

      <div
        style="flex:0 0 18rem;min-width:16rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <p
          style="margin:0 0 0.5rem;font-size:0.75rem;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:var(--color-neutral-foreground2-rest)"
        >
          Content lengths
        </p>
        <div
          style="display:flex;flex-direction:column;gap:0.5rem;font-size:0.875rem;line-height:1.4"
        >
          <div style="display:flex;justify-content:space-between;gap:1rem">
            <span style="color:var(--color-neutral-foreground2-rest)">Summary</span>
            <strong style="font-weight:600;color:var(--color-neutral-foreground-rest)">{{
              draftForm.controls.summary.value.length
            }}</strong>
          </div>
          <div style="display:flex;justify-content:space-between;gap:1rem">
            <span style="color:var(--color-neutral-foreground2-rest)">Context</span>
            <strong style="font-weight:600;color:var(--color-neutral-foreground-rest)">{{
              draftForm.controls.context.value.length
            }}</strong>
          </div>
          <div style="display:flex;justify-content:space-between;gap:1rem">
            <span style="color:var(--color-neutral-foreground2-rest)">Handoff</span>
            <strong style="font-weight:600;color:var(--color-neutral-foreground-rest)">{{
              draftForm.controls.handoff.value.length
            }}</strong>
          </div>
        </div>
      </div>
    </form>
  `,
})
export class TextareaFormDemoComponent {
  protected readonly defaults = {
    summary: 'Refresh the project detail view and reduce visual noise around the activity panel.',
    context:
      'Users struggle to scan the current panel because metadata, comments, and actions sit at the same visual weight.',
    handoff: 'Keep the layout compatible with existing drawer and tabs patterns.',
  };

  protected readonly draftForm = new FormGroup({
    summary: new FormControl(this.defaults.summary, { nonNullable: true }),
    context: new FormControl(this.defaults.context, { nonNullable: true }),
    handoff: new FormControl(this.defaults.handoff, { nonNullable: true }),
  });

  protected reset(): void {
    this.draftForm.reset(this.defaults);
  }
}
```

## Review feedback panel
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent, TextareaComponent } from 'ui';

@Component({
  selector: 'app-textarea-feedback-panel-demo',
  standalone: true,
  imports: [ButtonComponent, FormsModule, TextareaComponent],
  template: `
    <div
      style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:42rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <div style="display:flex;flex-direction:column;gap:0.375rem">
        <div style="font-size:1rem;font-weight:600">Review feedback panel</div>
        <div
          style="font-size:0.875rem;color:var(--color-neutral-foreground2-rest);line-height:1.45"
        >
          A realistic textarea surface usually combines guidance, enough writing space, and the next
          action in one focused card.
        </div>
      </div>

      <ui-textarea
        label="Feedback for the author"
        placeholder="Summarize the main revisions and any blockers"
        [(ngModel)]="feedback"
        [ngModelOptions]="{ standalone: true }"
        [rows]="7"
        helpText="Keep feedback actionable and group related points together."
      />

      <div
        style="display:flex;gap:0.75rem;flex-wrap:wrap;align-items:center;padding:0.75rem 0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
      >
        <ui-button type="button" variant="primary">Send feedback</ui-button>
        <ui-button type="button" variant="secondary" appearance="outline">Save draft</ui-button>
      </div>
    </div>
  `,
})
export class TextareaFeedbackPanelDemoComponent {
  protected feedback =
    'The overall direction is solid. Please tighten the spacing in the stats section and clarify the empty state copy before the next review.';
}
```

## Accessibility

### Label and description
Prefer a visible `label` so the purpose of the multiline field is explicit, especially when several writing areas appear in the same view. Help and error text contribute through the standard field description model.

### Keyboard and editing model
This is a native multiline text input, so users rely on standard textarea behavior for typing, selection, line breaks, and cursor movement.

| Aspect | Behavior |
| --- | --- |
| focus | native textarea receives focus |
| typing | native multiline editing behavior |
| Enter | inserts a new line |
| Tab | moves focus according to normal document order |
| readonly / disabled | editing is blocked, but readonly content remains visible |

### Rows and visible space
`rows` affects the visible writing area only. It does not limit content length, so if the flow needs length rules or character guidance, pair the textarea with helper text or form validation.
