# Alert

- Entry point: `@koobiq/components/alert`
- NgModule: `KbqAlertModule`
- Page: https://koobiq.io/en/components/alert/overview

## Overview

Shows important information on a page. Can contain a hint, signal a status change, or indicate a problem.

### Size

Alerts come in two sizes. The normal size is the default; pass `[compact]="true"` for the denser compact size used in tight spaces such as popovers and forms.

**Example: Alert size** (`alert-overview`)

```ts alert-overview-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqAlert, KbqAlertControl, KbqAlertTitle } from '@koobiq/components/alert';
import { KbqButton, KbqButtonCssStyler } from '@koobiq/components/button';
import { KbqIcon, KbqIconItem } from '@koobiq/components/icon';

@Component({
    selector: 'alert-overview-example',
    imports: [
        KbqAlert,
        KbqAlertTitle,
        KbqAlertControl,
        KbqButton,
        KbqButtonCssStyler,
        KbqIcon,
        KbqIconItem
    ],
    templateUrl: 'alert-overview-example.html',
    styleUrls: ['alert-overview-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AlertOverviewExample {
    text =
        'If there is no heading, do not put a period at the end of the last sentence. If the message contains both body text and a heading, put a period at the end of the body text.';
}
```

```html alert-overview-example.html
<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        <kbq-alert>
            <i aria-hidden="true" kbq-icon-item="kbq-circle-info_16"></i>
            <div kbq-alert-title>Heading</div>
            {{ text }}
            <button kbq-alert-control kbq-button [color]="'theme'" [kbqStyle]="'transparent'">Action</button>
            <button kbq-alert-control kbq-button [color]="'theme'" [kbqStyle]="'transparent'">Another action</button>
        </kbq-alert>
    </div>
    <div class="layout-column example-column">
        <kbq-alert [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
            <div kbq-alert-title>Heading</div>
            {{ text }}
            <button kbq-alert-control kbq-button [color]="'theme'" [kbqStyle]="'transparent'">Action</button>
            <button kbq-alert-control kbq-button [color]="'theme'" [kbqStyle]="'transparent'">Another action</button>
        </kbq-alert>
    </div>
</div>
```

```css alert-overview-example.css
.example-column {
    width: 50%;
}

:host ::ng-deep .kbq-alert__button-stack {
    display: flex;
    flex-wrap: wrap;
    column-gap: 12px;
}

@media (1200px < width <= 1440px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}

@media (width <= 1024px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}
```

### Variant

By default, alerts are styled conservatively and stand out only by the icon color. If the block gets lost on screen, you can draw attention to it using the colored background variant.

**Example: Alert variants** (`alert-variants`)

```ts alert-variants-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqAlert, KbqAlertColors, KbqAlertStyles, KbqAlertTitle } from '@koobiq/components/alert';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIcon, KbqIconItem } from '@koobiq/components/icon';

@Component({
    selector: 'alert-variants-example',
    imports: [
        KbqAlert,
        KbqAlertTitle,
        KbqIcon,
        KbqIconItem
    ],
    template: `
        <div class="layout-row example-row layout-gap-l flex-100">
            <div class="layout-column example-column">
                <kbq-alert [alertColor]="alertColors.Error">
                    <i aria-hidden="true" kbq-icon-item="kbq-triangle-exclamation_16" [color]="colors.Error"></i>
                    <div kbq-alert-title>Default</div>
                    {{ text }}
                </kbq-alert>
            </div>
            <div class="layout-column example-column">
                <kbq-alert [alertColor]="alertColors.Error" [alertStyle]="alertStyles.Colored" [compact]="true">
                    <i aria-hidden="true" kbq-icon="kbq-triangle-exclamation_16" [color]="colors.Error"></i>
                    <div kbq-alert-title>Colored</div>
                    {{ text }}
                </kbq-alert>
            </div>
        </div>
    `,
    styleUrls: ['alert-variants-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AlertVariantsExample {
    colors = KbqComponentColors;
    alertStyles = KbqAlertStyles;
    alertColors = KbqAlertColors;
    text =
        'If there is no heading, do not put a period at the end of the last sentence. If the message contains both body text and a heading, put a period at the end of the body text.';
}
```

```css alert-variants-example.css
.example-column {
    width: 50%;
}

@media (1200px < width <= 1440px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}

@media (width <= 1024px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}
```

### Status

The `alertColor` input sets the status: `error`, `warning`, `success` or `info` (the default). It drives the background in the colored variant and auto-tints a projected status icon that has no explicit `[color]`: the icon is tinted to the matching status color, except `info`, which is tinted to `contrast`. Set `[color]` on the icon yourself to opt out of the auto-tint.

Project a single status icon. The slot renders every `[kbq-icon]` and `[kbq-icon-item]` it receives but auto-tints only one of them — a `kbq-icon` wins over a `kbq-icon-item` — so any further icon keeps whatever color it was given. Projecting one of each also logs a warning in dev mode.

**Example: Alert status** (`alert-status`)

```ts alert-status-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqAlert, KbqAlertColors, KbqAlertStyles, KbqAlertTitle } from '@koobiq/components/alert';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqIcon } from '@koobiq/components/icon';

@Component({
    selector: 'alert-status-example',
    imports: [
        KbqAlert,
        KbqAlertTitle,
        KbqIcon
    ],
    templateUrl: 'alert-status-example.html',
    styleUrls: ['alert-status-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AlertStatusExample {
    colors = KbqComponentColors;
    alertStyles = KbqAlertStyles;
    alertColors = KbqAlertColors;
    text =
        'If there is no heading, do not put a period at the end of the last sentence. If the message contains both body text and a heading, put a period at the end of the body text.';
}
```

```html alert-status-example.html
<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column layout-gap-l">
        <kbq-alert [alertColor]="alertColors.Info" [alertStyle]="alertStyles.Colored" [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-info_16" [color]="colors.Contrast"></i>
            <!-- The severity is named in the title, so it does not rely on the icon color alone. -->
            <div kbq-alert-title>Information</div>
            {{ text }}
        </kbq-alert>

        <kbq-alert [alertColor]="alertColors.Warning" [alertStyle]="alertStyles.Colored" [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-triangle-exclamation_16" [color]="colors.Warning"></i>
            <div kbq-alert-title>Warning</div>
            {{ text }}
        </kbq-alert>
    </div>
    <div class="layout-column example-column layout-gap-l">
        <kbq-alert [alertColor]="alertColors.Error" [alertStyle]="alertStyles.Colored" [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-triangle-exclamation_16" [color]="colors.Error"></i>
            <div kbq-alert-title>Error</div>
            {{ text }}
        </kbq-alert>

        <kbq-alert [alertColor]="alertColors.Success" [alertStyle]="alertStyles.Colored" [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-check_16" [color]="colors.Success"></i>
            <div kbq-alert-title>Success</div>
            {{ text }}
        </kbq-alert>
    </div>
</div>
```

```css alert-status-example.css
.example-column {
    width: 50%;
}

@media (1200px < width <= 1440px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}

@media (width <= 1024px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}
```

### Hide

The block can be hidden by clicking the icon button in the corner of the alert. The alert does not hide itself — listen to the `(closed)` output (emitted when the close control is activated) and remove or hide the alert in your own code. If the system reports a problem, the alert is displayed permanently and the user cannot close it.

**Example: Alert close** (`alert-close`)

```ts alert-close-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqAlert, KbqAlertCloseButton } from '@koobiq/components/alert';
import { kbqAnimationsDisabled, KbqComponentColors } from '@koobiq/components/core';
import { KbqIcon, KbqIconButton } from '@koobiq/components/icon';

@Component({
    selector: 'alert-close-example',
    imports: [
        KbqAlert,
        KbqAlertCloseButton,
        KbqIcon,
        KbqIconButton
    ],
    template: `
        @if (state) {
            <kbq-alert
                animate.leave="example-alert_leave"
                class="flex-100"
                [class.kbq-animations-disabled]="animationsDisabled"
                [compact]="true"
                (closed)="state = false"
            >
                <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
                The alert is dismissed with the close icon in the corner; do not duplicate this with a button below the
                message text
                <button
                    kbq-alert-close-button
                    kbq-icon-button="kbq-xmark-s_16"
                    aria-label="Close"
                    [color]="colors.ContrastFade"
                ></button>
            </kbq-alert>
        }
    `,
    styles: `
        .example-alert_leave {
            animation: example-alert-fade-out 0.2s forwards;
        }

        .example-alert_leave.kbq-animations-disabled {
            animation: none;
        }

        @media (prefers-reduced-motion: reduce) {
            .example-alert_leave {
                animation: none;
            }
        }

        @keyframes example-alert-fade-out {
            to {
                opacity: 0;
            }
        }
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AlertCloseExample {
    protected readonly animationsDisabled = kbqAnimationsDisabled();
    colors = KbqComponentColors;
    state = true;
}
```

### Content

The alert can contain links and buttons; the icon and heading can be omitted. In a lengthy notification, the text can be split into paragraphs.

**Example: Alert content** (`alert-content`)

```ts alert-content-example.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { KbqAlert, KbqAlertCloseButton, KbqAlertControl, KbqAlertTitle } from '@koobiq/components/alert';
import { KbqButton, KbqButtonCssStyler } from '@koobiq/components/button';
import { kbqAnimationsDisabled, KbqComponentColors } from '@koobiq/components/core';
import { KbqIcon, KbqIconButton, KbqIconItem } from '@koobiq/components/icon';
import { KbqLink, KbqLinkSuffix } from '@koobiq/components/link';

@Component({
    selector: 'alert-content-example',
    imports: [
        KbqAlert,
        KbqAlertTitle,
        KbqAlertControl,
        KbqAlertCloseButton,
        KbqButton,
        KbqButtonCssStyler,
        KbqLink,
        KbqLinkSuffix,
        KbqIcon,
        KbqIconItem,
        KbqIconButton
    ],
    templateUrl: 'alert-content-example.html',
    styleUrls: ['alert-content-example.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AlertContentExample {
    protected readonly animationsDisabled = kbqAnimationsDisabled();
    colors = KbqComponentColors;
    firstState = true;
    secondState = true;
}
```

```html alert-content-example.html
<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        <kbq-alert>
            <i aria-hidden="true" kbq-icon-item="kbq-circle-info_16"></i>
            <div kbq-alert-title>Failed to upload the file</div>
            When a hyperlink is at the end of a sentence, the period takes the link color,
            <a href="https://koobiq.io/en/components/link" kbqLink>
                but is not underlined
                <span kbqLinkSuffix>.</span>
            </a>
            <button kbq-alert-control kbq-button class="example-overview__button" [color]="colors.Contrast">
                Choose another file
            </button>
            <button kbq-alert-control kbq-button [color]="colors.Theme" [kbqStyle]="'transparent'">Replace</button>
        </kbq-alert>
    </div>
    <div class="layout-column example-column">
        <kbq-alert [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
            <div kbq-alert-title>Failed to upload the file</div>
            When a hyperlink is at the end of a sentence, the period takes the link color,
            <a href="https://koobiq.io/en/components/link" kbqLink>
                but is not underlined
                <span kbqLinkSuffix>.</span>
            </a>
            <button kbq-alert-control kbq-button class="example-overview__button" [color]="colors.Contrast">
                Choose another file
            </button>
            <button kbq-alert-control kbq-button [color]="colors.Theme" [kbqStyle]="'transparent'">Replace</button>
        </kbq-alert>
    </div>
</div>

<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        <kbq-alert>
            <i aria-hidden="true" kbq-icon-item="kbq-circle-info_16"></i>
            Failed to update the agent
            <span class="kbq-text-normal-strong">ekb-mail-server</span>
            <button kbq-alert-control kbq-button [kbqStyle]="'transparent'">Retry</button>
            <button kbq-alert-control kbq-button [kbqStyle]="'transparent'">Install a different version</button>
        </kbq-alert>
    </div>
    <div class="layout-column example-column">
        <kbq-alert [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
            Failed to update the agent
            <span class="kbq-text-normal-strong">ekb-mail-server</span>
            <button kbq-alert-control kbq-button [kbqStyle]="'transparent'">Retry</button>
            <button kbq-alert-control kbq-button [kbqStyle]="'transparent'">Install a different version</button>
        </kbq-alert>
    </div>
</div>

<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        @if (firstState) {
            <kbq-alert
                animate.leave="example-alert_leave"
                [class.kbq-animations-disabled]="animationsDisabled"
                (closed)="firstState = false"
            >
                <i aria-hidden="true" kbq-icon-item="kbq-circle-info_16"></i>
                The module is configured in the policy
                <a href="https://koobiq.io" kbqLink>Hypervisors</a>
                <button
                    kbq-alert-close-button
                    kbq-icon-button="kbq-xmark-s_16"
                    aria-label="Close"
                    [color]="colors.ContrastFade"
                ></button>
            </kbq-alert>
        }
    </div>
    <div class="layout-column example-column">
        @if (secondState) {
            <kbq-alert
                animate.leave="example-alert_leave"
                [class.kbq-animations-disabled]="animationsDisabled"
                [compact]="true"
                (closed)="secondState = false"
            >
                <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
                The module is configured in the policy
                <a href="https://koobiq.io" kbqLink>Hypervisors</a>
                <button
                    kbq-alert-close-button
                    kbq-icon-button="kbq-xmark-s_16"
                    aria-label="Close"
                    [color]="colors.ContrastFade"
                ></button>
            </kbq-alert>
        }
    </div>
</div>

<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        <kbq-alert>
            <i aria-hidden="true" kbq-icon-item="kbq-circle-info_16"></i>
            After recovery, check the agent groups' links to policies
        </kbq-alert>
    </div>
    <div class="layout-column example-column">
        <kbq-alert [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
            After recovery, check the agent groups' links to policies
        </kbq-alert>
    </div>
</div>

<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        <kbq-alert>
            <i aria-hidden="true" kbq-icon-item="kbq-circle-info_16"></i>
            Short text in the alert
        </kbq-alert>
    </div>
    <div class="layout-column example-column">
        <kbq-alert [compact]="true">
            <i aria-hidden="true" kbq-icon="kbq-circle-info_16"></i>
            Short text in the alert
        </kbq-alert>
    </div>
</div>

<div class="layout-row example-row layout-gap-l flex-100">
    <div class="layout-column example-column">
        <kbq-alert>
            <p>
                A paragraph should convey and develop only one idea. For example, if you mix the causes, the essence of
                the problem, and possible solutions to the situation you are describing in a single paragraph, the
                reader will quickly get tired and most likely will not understand what they just read.
            </p>
            <p>
                Try to state the key idea in the first sentence of the paragraph, then expand on it in more detail and
                back it up with examples. That way the reader can skim the text diagonally and immediately see what each
                part is about. Sometimes the first sentences of a paragraph are set in bold — these are called run-in
                headings.
            </p>
        </kbq-alert>
    </div>
    <div class="layout-column example-column">
        <kbq-alert [compact]="true">
            <p>
                A paragraph should convey and develop only one idea. For example, if you mix the causes, the essence of
                the problem, and possible solutions to the situation you are describing in a single paragraph, the
                reader will quickly get tired and most likely will not understand what they just read.
            </p>
            <p>
                Try to state the key idea in the first sentence of the paragraph, then expand on it in more detail and
                back it up with examples. That way the reader can skim the text diagonally and immediately see what each
                part is about. Sometimes the first sentences of a paragraph are set in bold — these are called run-in
                headings.
            </p>
        </kbq-alert>
    </div>
</div>
```

```css alert-content-example.css
p {
    margin: 0 0;
}

p + p {
    margin-top: 8px;
}

:host {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
}

.example-column {
    width: 50%;
}

:host ::ng-deep .kbq-alert__button-stack {
    display: flex;
    flex-wrap: wrap;
    column-gap: 12px;
}

@media (1200px < width <= 1440px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}

@media (width <= 1024px) {
    .example-row {
        flex-wrap: wrap;
    }

    .example-column {
        width: 100%;
    }
}

.example-alert_leave {
    animation: example-alert-fade-out 0.2s forwards;
}

.example-alert_leave.kbq-animations-disabled {
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    .example-alert_leave {
        animation: none;
    }
}

@keyframes example-alert-fade-out {
    to {
        opacity: 0;
    }
}
```

### Accessibility

- **Static alerts.** The host applies only CSS classes and no ARIA role, which is correct for an alert rendered with the page: forcing `role="alert"` would make a screen reader announce page-load content unexpectedly.
- **Dynamically shown alerts.** When an alert appears in response to a user action (a validation error, a status change), add `role="alert"` for urgent messages, or `aria-live="polite"` with `aria-atomic="true"` for non-urgent status, so assistive technology announces it. Alternatively, announce the message with `LiveAnnouncer` from `@angular/cdk/a11y`.
- **Status by icon alone.** The status is conveyed by the icon color, which is not perceivable by every user. Repeat the severity in the title or body text (or add visually hidden text, e.g. `<span class="cdk-visually-hidden">Error:</span>`), and mark a decorative status icon `aria-hidden="true"`.
- **Close control.** Put the close button on a native `<button>` with an accessible name (`aria-label="Close"`) so it is reachable and operable by keyboard.
- **Focus on dismiss.** Dismissing an alert while focus is on its close control drops focus to `<body>`. Move focus to a logical target after removal — prefer `FocusMonitor.focusVia` from `@angular/cdk/a11y` so the focus ring is preserved.

## API

### Components

#### KbqAlert (component)

Shows important information on a page. Can carry a hint, signal a status change, or indicate a problem.

```html
<kbq-alert [alertColor]="'error'" [alertStyle]="'colored'">Something went wrong</kbq-alert>
```

For a dynamically inserted alert, add `role="alert"` (or `aria-live`) on the host so assistive
technology announces it — see the component guide.

```ts
@Component({ selector: 'kbq-alert', exportAs: 'kbqAlert' })
class KbqAlert {
    readonly compact = input<boolean>(false);
    readonly alertStyle = input<KbqEnumValues<KbqAlertStyles>>(
        KbqAlertStyles.Default
    );
    readonly alertColor = input<"error" | "warning" | "success" | "info">(
        KbqAlertColors.Info
    );
    readonly closed = output<void>();
}
```

- `[compact]`: `boolean` — Whether the alert uses the denser compact size.
- `[alertStyle]`: `KbqEnumValues<KbqAlertStyles>` — Visual style — neutral `default` or the attention-drawing `colored` background.
- `[alertColor]`: `"error" | "warning" | "success" | "info"` — Status of the alert. Drives the background in the `colored` style and auto-tints an uncolored
  projected status icon to the matching color (`info` icons become `contrast`). Defaults to `'info'`.
- `(closed)`: `void` — Emitted when the projected close control is activated. The consumer is responsible for hiding the alert.

### Directives

#### KbqAlertCloseButton (directive)

Marks the projected close control (place it on a native `<button>` with an accessible name).

```ts
@Directive({
    selector: '[kbq-alert-close-button]',
    exportAs: 'kbqAlertCloseButton'
})
class KbqAlertCloseButton {}
```

#### KbqAlertControl (directive)

Marks a projected action control (link or button) shown in the alert's button stack.

```ts
@Directive({ selector: '[kbq-alert-control]', exportAs: 'kbqAlertControl' })
class KbqAlertControl {}
```

#### KbqAlertTitle (directive)

Marks the projected alert heading.

```ts
@Directive({ selector: '[kbq-alert-title]', exportAs: 'kbqAlertTitle' })
class KbqAlertTitle {}
```

### Enums

#### KbqAlertColors (enum)

Status conveyed by an alert. Drives the background (colored style) and the auto-tinted status icon.

```ts
enum KbqAlertColors {
    Error = 'error',
    Warning = 'warning',
    Success = 'success',
    Info = 'info'
}
```

#### KbqAlertStyles (enum)

Visual style of an alert.

```ts
enum KbqAlertStyles {
    Default = 'default',
    Colored = 'colored'
}
```

- `Default`: `'default'` — Neutral background; the status is conveyed by the icon color only.
- `Colored`: `'colored'` — Colored background matching the status — use to draw attention when a default alert gets lost on screen.
