# Empty State

Use `ui-empty-state` for empty lists, first-run experiences, no-results states, and permission-related blanks. Focus on clear explanation and the next sensible step instead of decorative filler.

## Import
```ts
import { EmptyStateComponent, type QuickAction } from 'ui';
```

## Basic empty messaging
```ts
import { Component } from '@angular/core';
import { CardComponent, EmptyStateComponent } from 'ui';

@Component({
  selector: 'app-empty-state-basic-demo',
  standalone: true,
  imports: [CardComponent, EmptyStateComponent],
  template: `
    <ui-card style="width:100%;max-width:28rem;" ariaLabel="Basic empty state card">
      <ui-empty-state
        title="No items yet"
        description="There is nothing to show in this section right now."
      />
    </ui-card>
  `,
})
export class EmptyStateBasicDemoComponent {}
```

## Icons and sizes
```ts
import { Component } from '@angular/core';
import { CardComponent, EmptyStateComponent } from 'ui';

@Component({
  selector: 'app-empty-state-icons-sizes-demo',
  standalone: true,
  imports: [CardComponent, EmptyStateComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:1rem;width:100%;max-width:54rem"
    >
      <ui-card style="height:100%;" ariaLabel="Small empty state card">
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Small
        </p>
        <ui-empty-state
          title="No drafts"
          description="Create a draft to start iterating."
          icon="document"
          size="small"
        />
      </ui-card>

      <ui-card style="height:100%;" ariaLabel="Medium empty state card">
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Medium
        </p>
        <ui-empty-state
          title="No results"
          description="Try changing the search term or removing some filters."
          icon="search"
          size="medium"
        />
      </ui-card>

      <ui-card style="height:100%;" ariaLabel="Large empty state card">
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Large
        </p>
        <ui-empty-state
          title="Nothing scheduled"
          description="Plan the next activity to populate this timeline."
          icon="calendar"
          size="large"
        />
      </ui-card>
    </div>
  `,
})
export class EmptyStateIconsSizesDemoComponent {}
```

## Primary and secondary actions
```ts
import { Component, signal } from '@angular/core';
import { CardComponent, EmptyStateComponent, type QuickAction } from 'ui';

@Component({
  selector: 'app-empty-state-actions-demo',
  standalone: true,
  imports: [CardComponent, EmptyStateComponent],
  template: `
    <div
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:54rem"
    >
      <ui-card style="flex:0 0 28rem;" ariaLabel="Empty state with actions">
        <ui-empty-state
          title="No team members"
          description="Invite people to collaborate or import users from another workspace."
          icon="people"
          [primaryAction]="inviteAction"
          [secondaryAction]="importAction"
        />
      </ui-card>

      <ui-card appearance="outline" borderStyle="dashed" ariaLabel="Small outline card">
        <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)"
        >
          Last action
        </p>
        <div style="font-size:0.875rem;line-height:1.5;color:var(--color-neutral-foreground-rest)">
          {{ lastAction() || 'No action yet.' }}
        </div>
      </ui-card>
    </div>
  `,
})
export class EmptyStateActionsDemoComponent {
  protected readonly lastAction = signal('');

  protected readonly inviteAction: QuickAction = {
    label: 'Invite people',
    variant: 'primary',
    icon: 'person_add',
    action: () => this.lastAction.set('Invite people'),
  };

  protected readonly importAction: QuickAction = {
    label: 'Import users',
    variant: 'secondary',
    icon: 'send',
    action: () => this.lastAction.set('Import users'),
  };
}
```

## Custom projected content
```ts
import { Component } from '@angular/core';
import { ButtonComponent, CardComponent, EmptyStateComponent } from 'ui';

@Component({
  selector: 'app-empty-state-custom-content-demo',
  standalone: true,
  imports: [ButtonComponent, CardComponent, EmptyStateComponent],
  template: `
    <ui-card
      appearance="filled"
      style="width:100%;max-width:32rem;"
      ariaLabel="Empty state with custom content"
    >
      <ui-empty-state title="Connect a source" icon="plug_connected">
        <ng-template #content>
          <div
            style="display:flex;flex-direction:column;gap:0.875rem;align-items:center;text-align:center"
          >
            <p style="margin:0;max-width:24rem;color:var(--color-neutral-foreground2-rest)">
              Choose a data source to start syncing records into this workspace.
            </p>
            <div style="display:flex;flex-wrap:wrap;justify-content:center;gap:0.5rem">
              <ui-button appearance="outline" variant="secondary">Learn more</ui-button>
              <ui-button variant="primary">Connect source</ui-button>
            </div>
          </div>
        </ng-template>
      </ui-empty-state>
    </ui-card>
  `,
})
export class EmptyStateCustomContentDemoComponent {}
```

## Common empty-state scenarios
```ts
import { Component } from '@angular/core';
import { CardComponent, EmptyStateComponent } from 'ui';

@Component({
  selector: 'app-empty-state-scenarios-demo',
  standalone: true,
  imports: [CardComponent, EmptyStateComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1rem;width:100%;max-width:56rem"
    >
      <ui-card style="height:100%;" ariaLabel="No results empty state">
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          No results
        </p>
        <ui-empty-state
          title="No matches found"
          description="Try different keywords or clear some filters."
          icon="search"
        />
      </ui-card>

      <ui-card style="height:100%;" ariaLabel="First run empty state">
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          First run
        </p>
        <ui-empty-state
          title="Create your first board"
          description="Start with a new board to organize work and track progress."
          icon="board"
        />
      </ui-card>

      <ui-card style="height:100%;" ariaLabel="No access empty state">
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          No access
        </p>
        <ui-empty-state
          title="Access restricted"
          description="You do not have permission to view this content. Contact an administrator for access."
          icon="shield"
        />
      </ui-card>
    </div>
  `,
})
export class EmptyStateScenariosDemoComponent {}
```

## Embedded list or panel layout
```ts
import { Component } from '@angular/core';
import { ButtonComponent, CardComponent, EmptyStateComponent, TextComponent } from 'ui';

@Component({
  selector: 'app-empty-state-list-layout-demo',
  standalone: true,
  imports: [ButtonComponent, CardComponent, EmptyStateComponent, TextComponent],
  template: `
    <ui-card style="width:100%;max-width:60rem;" ariaLabel="Project list empty state card">
      <div uiCardBody style="display:grid;gap:1rem;">
        <div
          style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0.75rem"
        >
          <ui-text placeholder="Search projects..." style="width:16rem" />
          <ui-button variant="secondary" appearance="outline">Filters</ui-button>
        </div>

        <ui-card
          style="height:100%;"
          appearance="filled-alternative"
          borderStyle="dashed"
          ariaLabel="Small outline card"
        >
          <ui-empty-state
            title="No projects in this view"
            description="Try clearing filters or create a new project to get started."
            icon="folder"
          />
        </ui-card>
      </div>
    </ui-card>
  `,
})
export class EmptyStateListLayoutDemoComponent {}
```

## Accessibility

### Content clarity
An empty state is primarily explanatory content. The title and description should clearly communicate why nothing is shown and what the user can do next.

### Actions
If actions are present, they should have clear visible labels and remain keyboard reachable like any other buttons.

| Element | Guidance |
| --- | --- |
| Primary action | Use when there is one obvious next step. |
| Secondary action | Use for alternate paths such as import, learn more, or view docs. |
| Custom projected controls | Keep them simple and semantically clear. |

### Decorative icons
Icons in empty states are supporting visuals. They should reinforce meaning but not be the only way the state is understood.
