import type { IntentName } from "@charcuterie/tokens"; import type { ComponentPropsWithRef, ReactNode } from "react"; export type AlertSize = "md" | "sm"; export type AlertProps = Omit, "children"> & { /** Buttons. Laid out beside the text at `cq-sm` and up. */ actions?: ReactNode; /** The quiet second line — counts, timestamps, provenance. */ description?: ReactNode; /** * The evidence, one line each. A list rather than a paragraph * because that is what it is, and because a screen reader then * announces "list, 3 items" instead of running three measurements * together into one sentence. */ details?: readonly string[]; /** * The sentence. A `ReactNode` rather than a `string` because * rip-deck's verdicts qualify themselves inline — *"…(suspected — * retry in another drive to confirm)"* — and splitting that across * two props would let the qualifier drift away from the claim it * qualifies. */ heading: ReactNode; intent?: IntentName; /** * Present ⇒ this is a **named landmark** and * `getByRole("region", { name })` finds it. Absent ⇒ a plain * block, because a `
` with no accessible name is * `generic` rather than `region`. See below; the default is * deliberate. */ label?: string; size?: AlertSize; }; /** * The thing four of rip-deck's components each spell differently, * and the reason `TONE_CLASS` was declared twice in one package. * * `TowerAlerts`, `UsbAlertBanner`, `LoadedDiscsBanner` and * `VerdictBadge` are one shape: an intent-coloured block holding a * sentence, a quieter line under it, and sometimes a list of * evidence. Two of them carried a byte-identical `TONE_CLASS` map of * hardcoded hexes and `amber-`/`red-` utilities; mux-magic spells the * same idea a third way in `statusClassMap`. Here it is * `INTENT_APPEARANCE_CLASS`, which every other component in this * package already reads. * * ### It is a block by default, and a landmark only when named * * `label` is what decides, because the evidence divides cleanly: * * - The **banners** are one alert at the top of a page and want to * be addressable — `getByRole("region", { name: "USB connection * alert" })`. They pass a `label`. * - The **verdict** renders inside every bay card, and one of its * two forms is the single sentence *"Part of the tower-wide * problem above."* Nine landmarks sharing one name is axe's * `landmark-unique`, so it passes none. * * A component that always took the landmark would have made the * second case fail its own a11y gate, and one that never took it * would leave three `
` wrappers hand-rolled in * the consumer — which is what rip-deck has today. * * ### The heading is not a heading element * * An alert's headline is a *sentence* — "Bay 7 is reading with * errors; clean the disc and try again" — not a node in the document * outline. Nine bays' worth of those as `

`s builds a table of * contents out of nine complaints, which is worse for a screen * reader than no outline entry at all. `Card` and `EmptyState` take * a real `headingLevel` precisely because their headings *are* short * outline nodes; this one is styled text, and `label` is how it * becomes findable. * * ### It does not announce, and that is a component boundary * * No `role="alert"`, no `aria-live`. A live region takes **no * accessible name from its content** * ([decision](../../../docs/decisions/2026-07-29-status-regions-carry-an-aria-label.md)), * so an announcing variant would need a name that duplicates the * sentence it is announcing — and `role="alert"` on a `
` * overrides the landmark, so it cannot be both. A thing that * interrupts you is a `Toast` (M6). This is a thing that is *on the * page*. */ export declare const Alert: ({ actions, className, description, details, heading, intent, label, size, ...sectionProps }: AlertProps) => ReactNode; //# sourceMappingURL=Alert.d.ts.map