# Search

Use `ui-search` for filtering lists, querying content, and compact toolbar search patterns. It inherits the shared field API for labels, help text, size, variant, readonly, disabled, and validation messaging while keeping the behavior aligned with native search inputs.

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

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

@Component({
  selector: 'app-search-basic-demo',
  standalone: true,
  imports: [FormsModule, SearchComponent],
  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">Global search</div>
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Good default for workspace-wide queries, command bars, or header search.
          </div>
        </div>

        <ui-search
          label="Search"
          placeholder="Search people, files, and projects"
          helpText="Start typing a keyword to search across the workspace."
        />
      </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">Search with value</div>
          <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
            Shows the built-in clear affordance when the field already contains a query.
          </div>
        </div>

        <ui-search
          label="Search documents"
          placeholder="Search documents"
          [(ngModel)]="query"
          [ngModelOptions]="{ standalone: true }"
          helpText="Use clear to reset the query quickly."
        />
      </div>
    </div>
  `,
})
export class SearchBasicDemoComponent {
  protected query = 'Quarterly planning';
}
```

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

@Component({
  selector: 'app-search-layout-demo',
  standalone: true,
  imports: [SearchComponent],
  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-search
            label="Compact table search"
            size="small"
            inputVariant="filled-lighter"
            placeholder="Search rows"
          />
        </div>

        <div style="flex:1 1 15rem;min-width:14rem">
          <ui-search
            label="Sidebar search"
            labelPosition="before"
            size="medium"
            inputVariant="filled-gray"
            placeholder="Search templates"
          />
        </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 content search</div>
        <ui-search
          label="Knowledge base"
          size="large"
          inputVariant="filled"
          placeholder="Search articles, how-tos, and release notes"
          helpText="Use a larger search field when finding content is the main action on the page."
        />
      </div>
    </div>
  `,
})
export class SearchLayoutDemoComponent {}
```

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

@Component({
  selector: 'app-search-states-demo',
  standalone: true,
  imports: [FormsModule, SearchComponent],
  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-search
          label="Readonly search"
          [readonly]="true"
          [(ngModel)]="readonlyValue"
          [ngModelOptions]="{ standalone: true }"
          helpText="Useful when showing the active query without allowing edits."
        />
      </div>

      <div style="flex:1 1 16rem;min-width:15rem">
        <ui-search
          label="Disabled search"
          [disabled]="true"
          placeholder="Search is unavailable"
          helpText="Disabled removes interaction completely."
        />
      </div>

      <div style="flex:1 1 16rem;min-width:15rem">
        <ui-search
          label="Minimum query length"
          [(ngModel)]="shortQuery"
          [ngModelOptions]="{ standalone: true }"
          [errorText]="shortQueryError"
          helpText="Require at least 3 characters before searching."
        />
      </div>
    </div>
  `,
})
export class SearchStatesDemoComponent {
  protected readonlyValue = 'Archived incidents';
  protected shortQuery = 'ab';

  protected get shortQueryError(): string {
    if (!this.shortQuery) {
      return '';
    }
    return this.shortQuery.trim().length < 3 ? 'Enter at least 3 characters.' : '';
  }
}
```

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

@Component({
  selector: 'app-search-validation-demo',
  standalone: true,
  imports: [ButtonComponent, ReactiveFormsModule, SearchComponent],
  template: `
    <form
      [formGroup]="searchForm"
      style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:34rem"
      (ngSubmit)="markTouched()"
    >
      <ui-search
        label="Search policy articles"
        placeholder="Enter at least 3 characters"
        helpText="Required. Use 3 or more characters to reduce noisy results."
        formControlName="query"
        [errorText]="queryError"
      />

      <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 SearchValidationDemoComponent {
  protected readonly searchForm = new FormGroup({
    query: new FormControl('', {
      nonNullable: true,
      validators: [Validators.required, Validators.minLength(3)],
    }),
  });

  protected get queryError(): string {
    const control = this.searchForm.controls.query;
    if (!control.touched && !control.dirty) {
      return '';
    }
    if (control.hasError('required')) {
      return 'Search query is required.';
    }
    if (control.hasError('minlength')) {
      return 'Enter at least 3 characters.';
    }
    return '';
  }

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

  protected fillExample(): void {
    this.searchForm.controls.query.setValue('remote work policy');
    this.searchForm.controls.query.markAsTouched();
  }
}
```

## Filter bar composition
```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonComponent, SearchComponent } from 'ui';

@Component({
  selector: 'app-search-filter-bar-demo',
  standalone: true,
  imports: [ButtonComponent, FormsModule, SearchComponent],
  template: `
    <div
      style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:44rem;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">Issues filter bar</div>
        <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
          Search often lives beside quick filters and reset actions, not as a standalone input.
        </div>
      </div>

      <div
        style="display:flex;gap:0.75rem;flex-wrap:wrap;align-items:flex-end;padding:0.75rem 0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
      >
        <div style="flex:1 1 16rem;min-width:14rem">
          <ui-search
            label="Search issues"
            placeholder="Search title or id"
            [(ngModel)]="searchQuery"
            [ngModelOptions]="{ standalone: true }"
          />
        </div>
        <ui-button type="button" variant="secondary" appearance="outline">Open only</ui-button>
        <ui-button type="button" variant="secondary" appearance="outline">Assigned to me</ui-button>
        <ui-button type="button" appearance="subtle" (click)="clearFilters()">Reset</ui-button>
      </div>

      <div
        style="padding:0.875rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-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)"
        >
          Active query
        </p>
        <div style="font-size:0.875rem;color:var(--color-neutral-foreground-rest)">
          {{ searchQuery || 'No query applied.' }}
        </div>
      </div>
    </div>
  `,
})
export class SearchFilterBarDemoComponent {
  protected searchQuery = 'drawer animation';

  protected clearFilters(): void {
    this.searchQuery = '';
  }
}
```

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

type SearchResult = {
  title: string;
  meta: string;
};

@Component({
  selector: 'app-search-results-panel-demo',
  standalone: true,
  imports: [ButtonComponent, FormsModule, SearchComponent],
  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">Workspace search panel</div>
        <div
          style="font-size:0.875rem;color:var(--color-neutral-foreground2-rest);line-height:1.45"
        >
          A realistic search surface includes the query field, lightweight result count, and a
          compact list of matching entities.
        </div>
      </div>

      <ui-search
        label="Search workspace"
        placeholder="Search by project, document, or teammate"
        [(ngModel)]="query"
        [ngModelOptions]="{ standalone: true }"
        helpText="Results update from the current query preview below."
      />

      <div
        style="display:flex;justify-content:space-between;gap:1rem;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)"
      >
        <span style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
          {{ filteredResults.length }} result{{ filteredResults.length === 1 ? '' : 's' }}
        </span>
        <ui-button type="button" appearance="subtle" (click)="query = ''">Clear query</ui-button>
      </div>

      <div style="display:flex;flex-direction:column;gap:0.625rem">
        @for (result of filteredResults; track result.title) {
          <div
            style="display:flex;flex-direction:column;gap:0.2rem;padding:0.875rem 1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:0.875rem;background:var(--color-neutral-background-rest)"
          >
            <div style="font-size:0.9375rem;font-weight:600">{{ result.title }}</div>
            <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
              {{ result.meta }}
            </div>
          </div>
        }
      </div>
    </div>
  `,
})
export class SearchResultsPanelDemoComponent {
  protected query = 'release';

  private readonly results: SearchResult[] = [
    { title: 'Release handoff checklist', meta: 'Document | Updated 2 hours ago' },
    { title: 'Q3 release plan', meta: 'Project | Owned by Nina Woods' },
    { title: 'Release notes template', meta: 'Template | Shared with Product Ops' },
    { title: 'Design review board', meta: 'Channel | Last active yesterday' },
  ];

  protected get filteredResults(): SearchResult[] {
    const query = this.query.trim().toLowerCase();
    if (!query) {
      return this.results;
    }

    return this.results.filter(result =>
      `${result.title} ${result.meta}`.toLowerCase().includes(query),
    );
  }
}
```

## Accessibility

### Label and name
Prefer a visible `label` so the search scope is explicit, especially when several search fields can appear in the same product. Help and error text contribute through `aria-describedby`.

### Keyboard and actions
This is a standard text-entry field backed by a native `type="search"` input. Search and clear are exposed as separate actions in the field chrome when the field is editable.

| Aspect | Behavior |
| --- | --- |
| focus | native search input receives focus |
| typing | native search input behavior |
| clear action | appears only when the field has a value and is editable |
| search action | stays visible while editable to reinforce the field purpose |
| readonly / disabled | actions are hidden and direct editing is blocked |

### Autocomplete and semantics
The inner input uses `type="search"` and falls back to `autocomplete="off"` unless you override it. Use explicit autocomplete values only when the search query should participate in browser-assisted history or form completion.
