# AlertBanner

> Inline alert for info, warning, success, and error feedback.

- Status: stable
- Group: feedback
- Page: https://www.digitaltableteur.com/design-system/components/alert-banner
- Contract: https://www.digitaltableteur.com/contracts/v1/AlertBanner.contract.json

## When to use

AlertBanner is the persistent, page-level status surface: it encodes severity through both color and an announced icon, carries an optional title, rich description, and action slot, and stays put until the user reads (or dismisses) it. Errors interrupt assistive tech automatically (role=alert, assertive); every other tone is a polite status. It has no auto-dismiss by design; transient confirmations belong to Toast.

## When not to use

- Do not pair AlertBanner with a blocking Modal for the same user outcome; pick one surface.
- Do not make error banners dismissible: letting the user hide a blocking problem strands them mid-flow.
- Do not stack more than one banner on the same page; two competing persistent messages cancel each other out.
- Do not animate the banner in on every render; it is persistent chrome, not a transition.
- Do not use AlertBanner for transient feedback; it has no auto-dismiss because "sit here until acknowledged" is its job. Use Toast for post-action confirmations.

## Look-alikes

- Card: Do not use the muted variant to encode status; status belongs to AlertBanner or Badge.
- EmptyState: Do not use EmptyState for errors or loading; AlertBanner owns failures and Skeleton owns loading.
- Toast: Do not use AlertBanner for transient feedback; it has no auto-dismiss because "sit here until acknowledged" is its job. Use Toast for post-action confirmations.
- ToastStack: Do not use it for persistent or compliance-critical messaging; toasts auto-dismiss (use AlertBanner)

## Props

| Prop | Type | Default | Description | Required |
| --- | --- | --- | --- | --- |
| `tone` | `"info" \| "success" \| "warning" \| "error"` | `info` | Semantic tone controlling icon and surface colors. | No |
| `title` | `string` | none | Alert title, rendered as a bold `<strong>` label, not a heading, so it adds nothing to the page outline. | No |
| `description` | `React.ReactNode` | none | Supporting body copy. | No |
| `action` | `React.ReactNode` | none | Optional action slot rendered under the description (e.g. a tertiary Button). | No |
| `showIcon` | `boolean` | `true` | Show the semantic tone icon. The icon is derived from `tone`; set false for a text-only banner. | No |
| `dismissible` | `boolean` | `false` | Shows a localized dismiss control when true and onDismiss is set. The banner does not hide itself: remove it in onDismiss. Without onDismiss no control renders and a development warning names the gap. Error banners must not be dismissible. | No |
| `onDismiss` | `() => void` | none | Called when the user activates the dismiss control. The consumer removes the banner. Required for the control to render. | No |
| `aria-live` | `"polite" \| "assertive" \| "off"` | none | Live region politeness for assistive tech (an explicit value wins over the tone default). | No |
| `focusOnMount` | `boolean` | `false` | Moves focus to the banner when it mounts, for form error summaries: keyboard and screen reader users land on the list of problems after a failed submit. | No |

## Variants

- Available tones: `info`, `success`, `warning`, `error` (default `info`).
- Allowed `aria-live` values: `polite`, `assertive`, `off`.

## Invalid combinations

- MUST NOT: When `dismissible` is set and `onDismiss` is not set, then also set `onDismiss`. The banner does not hide itself, so dismissible without onDismiss renders no Close button. Pass a handler that removes the banner, e.g. <AlertBanner dismissible onDismiss={() => setShown(false)} />.
- MUST NOT: When `tone` is `error` and `dismissible` is set, then do not set `dismissible`. An error banner explains a blocked flow; letting the user hide it strands them. Drop dismissible and keep the banner until the problem is resolved.

## Keyboard

| Key | Behaviour |
| --- | --- |
| Tab | Moves focus to the control and on to the next focusable element. (standard) |
| Enter | Activates the focused control. (standard) |
| Space | Activates or toggles the focused control. (standard) |

Rows marked (standard) name a key the contract lists without a description; the behaviour shown is the usual one for that key.

## Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

| Level | Rule | How it is checked |
| --- | --- | --- |
| MUST NOT | When `dismissible` is set and `onDismiss` is not set, then also set `onDismiss`. The banner does not hide itself, so dismissible without onDismiss renders no Close button. Pass a handler that removes the banner, e.g. <AlertBanner dismissible onDismiss={() => setShown(false)} />. | Checked by the MCP tool validate_component_usage (rule `alertbanner-dismissible-needs-ondismiss`). Also enforced in code: AlertBanner.tsx development warning (dismissible && !onDismiss); the dismiss control renders only with onDismiss. |
| MUST NOT | When `tone` is `error` and `dismissible` is set, then do not set `dismissible`. An error banner explains a blocked flow; letting the user hide it strands them. Drop dismissible and keep the banner until the problem is resolved. | Checked by the MCP tool validate_component_usage (rule `alertbanner-error-not-dismissible`). Also enforced in code: AlertBanner.tsx development warning (dismissible && tone === "error"). |
| MUST | ARIA: Role=alert + assertive for error tone; role=status + polite otherwise. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: Tone icon announced by name (color is never the sole signal) | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: Dismiss button carries a localized aria-label. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by `npm run test:stories:matrix:ci`. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by `npm run test:stories:matrix:ci`. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by `npm run test:stories:hc`. |
| MUST | Every documented keyboard interaction works as specified. | Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2. |
| MUST | A human completed an end-to-end accessibility review of this component. | Manual review, last on 2026-11-04. |
| SHOULD | Pick tone by outcome severity; the error tone switches to role=alert with assertive announcement on its own; no ARIA wiring needed. | Not stated in the contract. |
| SHOULD | Pass both title and description for messages that read as a small document; description accepts nodes, so links and inline formatting are fine. | Not stated in the contract. |
| SHOULD | Put the follow-up in the action slot (a tertiary Button works best) instead of burying a link inside the prose. | Not stated in the contract. |
| SHOULD | Use dismissible with onDismiss for non-critical notices the user should acknowledge once; persist the dismissal in the consumer. | Not stated in the contract. |
| SHOULD NOT | Do not make error banners dismissible; the user should not be able to hide the explanation of a blocked flow. | Not stated in the contract. |
| SHOULD NOT | Do not stack multiple banners on one page; consolidate into the single most severe message. | Not stated in the contract. |
| SHOULD | For a form error summary, use tone=error with a List of Links to the fields as the description and focusOnMount, so focus lands on the summary after a failed submit. | Not stated in the contract. |

## Install and import

- Install: `npm install @digitaltableteur/react` (version 0.9.1, restricted access on npm).
- Import: `import { AlertBanner } from "@digitaltableteur/react";`
- Entry point: `@digitaltableteur/react/feedback` exports it too.
- Styles: import `@digitaltableteur/react/style.css` once at the app boundary.
- Also available as the custom element `<dt-alert-banner>`; see the custom elements manifest.

## Links

- Machine-readable contract (Design System Contract 1.0): https://www.digitaltableteur.com/contracts/v1/AlertBanner.contract.json
- Markdown version of this page: https://www.digitaltableteur.com/design-system/components/alert-banner.md
- Component index as JSON: https://www.digitaltableteur.com/design-system/components.json
- All components: https://www.digitaltableteur.com/design-system/components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.
