# Loading State

Use `ui-loading-state` when users need feedback that work is in progress. Choose the lightest pattern that still explains the delay: inline when content is not yet present, overlay when existing content is being refreshed, and fullscreen only for blocking transitions.

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

## Basic inline loading
```ts
import { Component } from '@angular/core';
import { LoadingStateComponent } from 'ui';

@Component({
  selector: 'app-loading-state-basic-demo',
  standalone: true,
  imports: [LoadingStateComponent],
  template: `
    <div
      style="width:100%;max-width:26rem;padding:1.5rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <ui-loading-state />
    </div>
  `,
})
export class LoadingStateBasicDemoComponent {}
```

## Loading messages and scale
```ts
import { Component } from '@angular/core';
import { LoadingStateComponent } from 'ui';

@Component({
  selector: 'app-loading-state-messaging-demo',
  standalone: true,
  imports: [LoadingStateComponent],
  template: `
    <div
      style="display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1rem;width:100%;max-width:56rem"
    >
      <div
        style="padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Compact
        </p>
        <ui-loading-state title="Loading data..." size="small" spinnerSize="small" />
      </div>

      <div
        style="padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Standard
        </p>
        <ui-loading-state
          title="Loading your workspace"
          description="Fetching the latest information and preparing the view."
        />
      </div>

      <div
        style="padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Large
        </p>
        <ui-loading-state
          title="Preparing report"
          description="This can take a moment for larger datasets."
          size="large"
          spinnerSize="large"
        />
      </div>
    </div>
  `,
})
export class LoadingStateMessagingDemoComponent {}
```

## Custom projected progress content
```ts
import { Component } from '@angular/core';
import { LoadingStateComponent } from 'ui';

@Component({
  selector: 'app-loading-state-custom-content-demo',
  standalone: true,
  imports: [LoadingStateComponent],
  template: `
    <div
      style="width:100%;max-width:32rem;padding:1.5rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <ui-loading-state
        title="Initializing workspace"
        description="Setting up everything you need."
      >
        <ng-template #content>
          <div
            style="display:flex;flex-direction:column;gap:0.5rem;align-items:center;text-align:center"
          >
            <div style="font-size:0.8125rem;color:var(--color-neutral-foreground2-rest)">
              Step 2 of 3
            </div>
            <div
              style="width:100%;max-width:14rem;height:0.375rem;border-radius:999px;background:var(--color-neutral-background3-rest);overflow:hidden"
            >
              <div
                style="width:66%;height:100%;border-radius:999px;background:var(--color-brand-background-rest)"
              ></div>
            </div>
          </div>
        </ng-template>
      </ui-loading-state>
    </div>
  `,
})
export class LoadingStateCustomContentDemoComponent {}
```

## Overlay on existing card content
```ts
import { Component, signal } from '@angular/core';
import { ButtonComponent, CardComponent, LoadingStateComponent } from 'ui';

@Component({
  selector: 'app-loading-state-card-overlay-demo',
  standalone: true,
  imports: [ButtonComponent, CardComponent, LoadingStateComponent],
  template: `
    <div style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:34rem">
      <div style="display:flex;justify-content:flex-start">
        <ui-button variant="secondary" appearance="outline" size="small" (click)="toggleLoading()">
          {{ isLoading() ? 'Stop loading' : 'Show loading' }}
        </ui-button>
      </div>

      <ui-loading-state
        [overlay]="true"
        [isLoading]="isLoading()"
        [blurContent]="true"
        title="Refreshing card"
        description="Updating metrics and recent activity."
      >
        <ui-card appearance="filled" ariaLabel="Workspace summary">
          <div uiCardHeader><strong>Workspace summary</strong></div>
          <div uiCardBody style="display:flex;flex-direction:column;gap:0.5rem">
            <div>Active projects: 12</div>
            <div>Pending reviews: 4</div>
            <div>Last sync: 2 minutes ago</div>
          </div>
        </ui-card>
      </ui-loading-state>
    </div>
  `,
})
export class LoadingStateCardOverlayDemoComponent {
  protected readonly isLoading = signal(true);

  protected toggleLoading(): void {
    this.isLoading.update(value => !value);
  }
}
```

## Overlay on dense lists or records
```ts
import { Component, signal } from '@angular/core';
import { ButtonComponent, LoadingStateComponent } from 'ui';

@Component({
  selector: 'app-loading-state-list-overlay-demo',
  standalone: true,
  imports: [ButtonComponent, LoadingStateComponent],
  template: `
    <div style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:42rem">
      <div style="display:flex;justify-content:flex-start">
        <ui-button variant="secondary" appearance="outline" size="small" (click)="toggleLoading()">
          {{ isLoading() ? 'Stop loading' : 'Reload list' }}
        </ui-button>
      </div>

      <ui-loading-state
        [overlay]="true"
        [isLoading]="isLoading()"
        [blurContent]="false"
        title="Loading records"
        spinnerSize="small"
        size="small"
      >
        <div
          style="display:grid;gap:0.5rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
        >
          <div
            style="display:flex;justify-content:space-between;gap:1rem;padding:0.75rem 0.875rem;border-radius:0.75rem;background:var(--color-neutral-background2-rest)"
          >
            <span>Quarterly report</span>
            <span style="color:var(--color-neutral-foreground2-rest)">Ready</span>
          </div>
          <div
            style="display:flex;justify-content:space-between;gap:1rem;padding:0.75rem 0.875rem;border-radius:0.75rem;background:var(--color-neutral-background2-rest)"
          >
            <span>Customer import</span>
            <span style="color:var(--color-neutral-foreground2-rest)">Pending</span>
          </div>
          <div
            style="display:flex;justify-content:space-between;gap:1rem;padding:0.75rem 0.875rem;border-radius:0.75rem;background:var(--color-neutral-background2-rest)"
          >
            <span>Review queue</span>
            <span style="color:var(--color-neutral-foreground2-rest)">3 items</span>
          </div>
        </div>
      </ui-loading-state>
    </div>
  `,
})
export class LoadingStateListOverlayDemoComponent {
  protected readonly isLoading = signal(true);

  protected toggleLoading(): void {
    this.isLoading.update(value => !value);
  }
}
```

## Fullscreen blocking transitions
```ts
import { Component, signal } from '@angular/core';
import { ButtonComponent, LoadingStateComponent } from 'ui';

@Component({
  selector: 'app-loading-state-fullscreen-demo',
  standalone: true,
  imports: [ButtonComponent, LoadingStateComponent],
  template: `
    <div
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:52rem"
    >
      <div
        style="flex:1 1 18rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;line-height:1.5;color:var(--color-neutral-foreground2-rest)"
        >
          Use fullscreen loading only for blocking transitions such as app bootstrap, secure
          redirects, or major workspace switches.
        </p>
        <ui-button variant="primary" (click)="showDemo()">Show fullscreen loading</ui-button>
      </div>

      <div
        style="flex:1 1 16rem;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.5rem;font-size:0.875rem;line-height:1.5">
          <div>Keep the message short and reassuring.</div>
          <div>Avoid fullscreen loading for small inline refreshes.</div>
        </div>
      </div>
    </div>

    @if (showOverlay()) {
      <ui-loading-state
        [overlay]="true"
        [fullScreen]="true"
        title="Switching workspace"
        description="Preparing your environment and restoring recent context."
        size="large"
        spinnerSize="large"
      />
    }
  `,
})
export class LoadingStateFullscreenDemoComponent {
  protected readonly showOverlay = signal(false);

  protected showDemo(): void {
    this.showOverlay.set(true);
    setTimeout(() => this.showOverlay.set(false), 2200);
  }
}
```

## Embedded panel or dashboard layout
```ts
import { Component } from '@angular/core';
import { ButtonComponent, LoadingStateComponent, MessageBarComponent, TextComponent } from 'ui';

@Component({
  selector: 'app-loading-state-panel-layout-demo',
  standalone: true,
  imports: [ButtonComponent, LoadingStateComponent, MessageBarComponent, TextComponent],
  template: `
    <div
      style="display:flex;flex-direction:column;gap:1rem;width:100%;max-width:60rem;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:center;justify-content:space-between;gap:0.75rem"
      >
        <ui-text placeholder="Search updates..." style="width:16rem" />
        <ui-button variant="secondary" appearance="outline">Filter</ui-button>
      </div>

      <ui-message-bar
        title="Sync in progress"
        message="Recent updates are still being aggregated. The panel stays readable while the content refreshes."
        variant="secondary"
        appearance="subtle"
        [dismissible]="false"
      />

      <ui-loading-state
        [overlay]="true"
        [isLoading]="true"
        [blurContent]="true"
        title="Updating activity"
        spinnerSize="small"
      >
        <div
          style="display:grid;gap:0.75rem;padding:1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
        >
          <div
            style="padding:0.875rem;border-radius:0.75rem;background:var(--color-neutral-background-rest)"
          >
            Latest deployment completed
          </div>
          <div
            style="padding:0.875rem;border-radius:0.75rem;background:var(--color-neutral-background-rest)"
          >
            Three new comments on design review
          </div>
          <div
            style="padding:0.875rem;border-radius:0.75rem;background:var(--color-neutral-background-rest)"
          >
            Import queue is processing 12 files
          </div>
        </div>
      </ui-loading-state>
    </div>
  `,
})
export class LoadingStatePanelLayoutDemoComponent {}
```

## Accessibility

### Status communication
A loading state should clearly communicate that work is in progress and, when possible, what is being loaded or updated. Short messages are usually better than generic filler.

### Overlay behavior
When overlay mode is used, ensure the surrounding experience still makes sense for keyboard and screen-reader users.

| Mode | Guidance |
| --- | --- |
| Inline loading | Use when content has not appeared yet. |
| Container overlay | Use when existing content is refreshing and context should remain visible. |
| Fullscreen overlay | Reserve for blocking transitions that truly affect the whole page. |

### Custom projected content
Projected content should add useful status context such as setup steps or progress, not duplicate the same message with more noise.
