# Email

Use `ui-email` when the value is specifically an email address and should benefit from native browser validation, mobile keyboard hints, and shared field behavior. It inherits the design-system field API for labels, help text, size, variant, readonly, disabled, and validation messaging.

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

## Basic email field
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { EmailComponent } from 'ui';

@Component({
  selector: 'app-email-basic-demo',
  standalone: true,
  imports: [EmailComponent, FormsModule],
  template: `
    <div style="display:grid;gap:1rem;max-width:32rem">
      <ui-email
        label="Work email"
        placeholder="name@company.com"
        helpText="Use the address where you receive workspace notifications."
        [(ngModel)]="workEmail"
      />

      <div
        style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0.75rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <div style="display:grid;gap:0.125rem">
          <strong style="font-size:0.875rem;color:var(--color-neutral-foreground-rest)"
            >Current value</strong
          >
          <span style="font-size:0.8125rem;color:var(--color-neutral-foreground3-rest)">
            {{ workEmail || 'Empty' }}
          </span>
        </div>
      </div>
    </div>
  `,
})
export class EmailBasicDemoComponent {
  protected workEmail = 'alex.taylor@contoso.com';
}
```

## Label position and size
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { EmailComponent } from 'ui';

@Component({
  selector: 'app-email-layout-demo',
  standalone: true,
  imports: [EmailComponent, FormsModule],
  template: `
    <div style="display:grid;gap:1rem">
      <div
        style="display:grid;gap:1rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <ui-email
          label="Before label"
          labelPosition="before"
          placeholder="owner@project.com"
          [(ngModel)]="beforeValue"
        />

        <ui-email
          label="After label"
          labelPosition="after"
          placeholder="finance@company.com"
          [(ngModel)]="afterValue"
        />
      </div>

      <div
        style="display:grid;gap:0.875rem;padding:1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <ui-email label="Small" size="small" placeholder="small@contoso.com" />
        <ui-email label="Medium" size="medium" placeholder="medium@contoso.com" />
        <ui-email label="Large" size="large" placeholder="large@contoso.com" />
      </div>
    </div>
  `,
})
export class EmailLayoutDemoComponent {
  protected beforeValue = '';
  protected afterValue = '';
}
```

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

@Component({
  selector: 'app-email-validation-demo',
  standalone: true,
  imports: [EmailComponent, ButtonComponent, ReactiveFormsModule],
  template: `
    <form
      [formGroup]="inviteForm"
      style="display:grid;gap:1rem;max-width:34rem"
      (ngSubmit)="markTouched()"
    >
      <ui-email
        label="Invite email"
        placeholder="person@company.com"
        helpText="Required. Must be a valid business email."
        formControlName="inviteEmail"
        [errorText]="emailError"
      />

      <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 EmailValidationDemoComponent {
  protected readonly inviteForm = new FormGroup({
    inviteEmail: new FormControl('', {
      nonNullable: true,
      validators: [Validators.required, Validators.email],
    }),
  });

  protected get emailError(): string {
    const control = this.inviteForm.controls.inviteEmail;
    if (!control.touched && !control.dirty) {
      return '';
    }
    if (control.hasError('required')) {
      return 'Email is required.';
    }
    if (control.hasError('email')) {
      return 'Enter a valid email address.';
    }
    return '';
  }

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

  protected fillExample(): void {
    this.inviteForm.controls.inviteEmail.setValue('sam.rivers@contoso.com');
    this.inviteForm.controls.inviteEmail.markAsTouched();
  }
}
```

## Readonly, disabled, and autocomplete
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { EmailComponent } from 'ui';

@Component({
  selector: 'app-email-states-demo',
  standalone: true,
  imports: [EmailComponent, FormsModule],
  template: `
    <div style="display:grid;gap:1rem;max-width:34rem">
      <ui-email
        label="Readonly address"
        [readonly]="true"
        helpText="Useful when the identifier can be copied but not changed."
        [(ngModel)]="readonlyValue"
      />

      <ui-email
        label="Disabled address"
        [disabled]="true"
        helpText="Disabled fields stay visible for context but are not editable."
        [(ngModel)]="disabledValue"
      />

      <ui-email
        label="Recovery email"
        autocomplete="email"
        placeholder="backup@example.com"
        helpText="Autocomplete stays aligned with native email semantics."
        [(ngModel)]="recoveryValue"
      />
    </div>
  `,
})
export class EmailStatesDemoComponent {
  protected readonlyValue = 'ops@contoso.com';
  protected disabledValue = 'billing@contoso.com';
  protected recoveryValue = '';
}
```

## Multiple address form
```ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { EmailComponent } from 'ui';

@Component({
  selector: 'app-email-form-demo',
  standalone: true,
  imports: [EmailComponent, ReactiveFormsModule],
  template: `
    <div
      style="display:grid;gap:1rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <div style="display:grid;gap:0.25rem">
        <strong style="font-size:0.9375rem;color:var(--color-neutral-foreground-rest)"
          >Notification settings</strong
        >
        <span style="font-size:0.875rem;color:var(--color-neutral-foreground3-rest)">
          Separate addresses for primary contact, invoices, and incident routing.
        </span>
      </div>

      <form [formGroup]="settingsForm" style="display:grid;gap:0.875rem">
        <ui-email label="Primary contact" formControlName="primary" placeholder="owner@team.com" />
        <ui-email label="Billing" formControlName="billing" placeholder="billing@team.com" />
        <ui-email
          label="Incident alerts"
          formControlName="alerts"
          placeholder="alerts@team.com"
          helpText="Use a shared mailbox or distribution list."
        />
      </form>

      <div
        style="display:flex;flex-wrap:wrap;gap:0.75rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <div style="display:grid;gap:0.125rem;min-width:10rem">
          <strong style="font-size:0.8125rem">Primary</strong>
          <span style="font-size:0.75rem;color:var(--color-neutral-foreground3-rest)">
            {{ settingsForm.controls.primary.getRawValue() || 'Not set' }}
          </span>
        </div>
        <div style="display:grid;gap:0.125rem;min-width:10rem">
          <strong style="font-size:0.8125rem">Billing</strong>
          <span style="font-size:0.75rem;color:var(--color-neutral-foreground3-rest)">
            {{ settingsForm.controls.billing.getRawValue() || 'Not set' }}
          </span>
        </div>
        <div style="display:grid;gap:0.125rem;min-width:10rem">
          <strong style="font-size:0.8125rem">Alerts</strong>
          <span style="font-size:0.75rem;color:var(--color-neutral-foreground3-rest)">
            {{ settingsForm.controls.alerts.getRawValue() || 'Not set' }}
          </span>
        </div>
      </div>
    </div>
  `,
})
export class EmailFormDemoComponent {
  protected readonly settingsForm = new FormGroup({
    primary: new FormControl('owner@contoso.com', {
      nonNullable: true,
      validators: [Validators.required, Validators.email],
    }),
    billing: new FormControl('billing@contoso.com', {
      nonNullable: true,
      validators: [Validators.email],
    }),
    alerts: new FormControl('', {
      nonNullable: true,
      validators: [Validators.email],
    }),
  });
}
```

## Invite panel composition
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BadgeComponent, ButtonComponent, EmailComponent } from 'ui';

@Component({
  selector: 'app-email-invite-panel-demo',
  standalone: true,
  imports: [BadgeComponent, ButtonComponent, EmailComponent, FormsModule],
  template: `
    <div
      style="display:grid;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-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:0.875rem"
      >
        <div style="display:grid;gap:0.375rem;max-width:28rem">
          <div style="display:flex;flex-wrap:wrap;gap:0.5rem">
            <ui-badge text="Invite flow" variant="info" appearance="tint" />
            <ui-badge text="Email required" variant="warning" appearance="outline" />
          </div>
          <strong style="font-size:0.9375rem;color:var(--color-neutral-foreground-rest)"
            >Invite a collaborator</strong
          >
          <span
            style="font-size:0.875rem;line-height:1.55;color:var(--color-neutral-foreground2-rest)"
          >
            A stronger composition pattern than a raw field: short context, one email input, and a
            direct action.
          </span>
        </div>

        <ui-button size="small" appearance="subtle">View pending invites</ui-button>
      </div>

      <div style="display:grid;gap:0.875rem;max-width:32rem">
        <ui-email
          label="Email address"
          placeholder="teammate@contoso.com"
          helpText="The address will receive a sign-in link and workspace role details."
          [(ngModel)]="inviteEmail"
        />

        <div style="display:flex;flex-wrap:wrap;gap:0.75rem">
          <ui-button>Send invite</ui-button>
          <ui-button appearance="outline">Copy link instead</ui-button>
        </div>
      </div>
    </div>
  `,
})
export class EmailInvitePanelDemoComponent {
  protected inviteEmail = '';
}
```

## Accessibility

### Label and name
Accessible naming follows the shared field model: prefer a visible `label`, or provide `ariaLabel` when no visible label is appropriate. Help and error text contribute through `aria-describedby`.

### Native email semantics
The inner input uses native `type="email"`, so browsers can offer email-specific keyboards, autofill behavior, and baseline validity checks. `autocomplete` defaults to `email` when you do not override it.

### Keyboard and actions
This is a standard text-entry field, so focus and editing use native input behavior. When the field is editable and non-empty, the built-in clear action becomes available beside the mail affordance.

| Aspect | Behavior |
| --- | --- |
| focus | native input receives focus |
| typing | native email input behavior |
| clear action | appears only when editable and value is present |
| described by | help and error text IDs when present |
| autocomplete | defaults to `email` |
