{
  "name": "Alert",
  "type": "component",
  "description": "Inline feedback about something that happened or needs attention, in four severities.",
  "export": "Alert",
  "path": "@gtmi/strix-react/atoms/alert",
  "figma": null,
  "figmaAbsent": "Base UI primitive. Figma draws no alert component; sourced from Base UI behaviour + shadcn composition.",
  "props": [
    {
      "name": "variant",
      "type": "info | success | warning | error",
      "default": "info",
      "description": "Severity. Drives the surface, the glyph and the hidden severity label. Also emitted as data-variant."
    },
    {
      "name": "title",
      "type": "string",
      "required": true,
      "description": "The headline."
    },
    {
      "name": "children",
      "type": "ReactNode",
      "description": "Optional detail below the title."
    },
    {
      "name": "titleAs",
      "type": "h2 | h3 | h4 | h5 | h6 | div",
      "default": "div",
      "description": "Element for the title. A div by default so an alert does not silently join the heading outline."
    },
    {
      "name": "icon",
      "type": "ReactNode",
      "description": "Override the severity glyph. Decorative; null removes it."
    },
    {
      "name": "variantLabels",
      "type": "Record<AlertVariant, string>",
      "default": "{ info: 'Information:', success: 'Success:', warning: 'Warning:', error: 'Error:' }",
      "description": "Visually-hidden prefix naming the severity, so severity is not conveyed by colour alone."
    },
    {
      "name": "action",
      "type": "ReactNode",
      "description": "A single inline action."
    },
    {
      "name": "dismissible",
      "type": "boolean",
      "default": "false",
      "description": "Render a dismiss button."
    },
    {
      "name": "visible",
      "type": "boolean",
      "description": "Controlled visibility. Dismissing then only reports."
    },
    {
      "name": "defaultVisible",
      "type": "boolean",
      "default": "true",
      "description": "Initial visibility when uncontrolled."
    },
    {
      "name": "onDismiss",
      "type": "() => void",
      "description": "Called when the dismiss button is pressed."
    },
    {
      "name": "closeLabel",
      "type": "string",
      "default": "Dismiss alert",
      "description": "Accessible name for the dismiss button."
    }
  ],
  "ai_tags": ["alert", "banner", "callout", "inline", "feedback", "notification", "severity", "primitive"],
  "since": "0.0.2",
  "tokens_used": [
    "--color-icon-accent",
    "--color-icon-error",
    "--color-icon-success",
    "--color-icon-warning",
    "--color-surface-error",
    "--color-surface-info",
    "--color-surface-success",
    "--color-surface-warning",
    "--color-text-primary",
    "--radius-md",
    "--spacing-gap-100",
    "--spacing-gap-300",
    "--spacing-padding-horizontal-md",
    "--spacing-padding-vertical-md"
  ],
  "notes": "Fills the largest functional hole in the package: Strix had Toast and no inline alert, so every consumer built one. A toast is transient, floats above the page and announces itself; an alert sits in the layout, stays until the condition clears, and belongs beside the thing it is about. FOUR SEVERITIES, NOT MAKER-DS'S TWO. Its alert-message offers success and error only, but this system already states severity in four steps twice over -- ChatLog's event banners and Toast's variants -- and a third vocabulary for one idea is how a design system stops being one. The glyphs and the default hidden labels are deliberately identical to ChatLog's. `error` announces assertively (role=\"alert\"), the rest politely (role=\"status\"). That split is why the role is not a prop: an error interrupting a screen reader is the point and a confirmation doing it is rude. Severity is never carried by colour alone (WCAG 1.4.1): each alert states it in visually-hidden text before the title, which is also what a screen reader announces for a live region. Note the test asserts the region's textContent and NOT its accessible name -- `status` and `alert` do not take their name from content, so a getByRole({ name }) query fails against a correct component, which is how the first version of that test failed. THE SURFACES ARE TRANSLUCENT IN DARK MODE, which is why the contrast test does not use checkPairings. --color-surface-{success,error,warning,info} resolve to alpha-* tokens at 20% opacity on dark, and checkPairings correctly files a translucent value under `unmeasurable` rather than guessing at a backdrop. The test composites each surface over --color-surface-base and then measures: the body ink clears AA against all four in both themes (11.28:1 to 17.61:1). One known gap, inherited not introduced: the warning glyph is --color-icon-warning (#ff7a00), which is 2.37:1 on --color-surface-warning (#fff2e5) in light mode. That is question 3 in docs/DESIGN_DECISIONS_NEEDED.md, already measured and owned by a designer, and there is nothing else to reach for -- --color-text-warning and --color-icon-warning-strong do not exist. The glyph is decorative here (aria-hidden, severity in text) so 1.4.11 does not require 3:1 of it, and inventing a value would pre-empt the decision. The test asserts that one exclusion as an exact equality, so resolving the question makes it fail and get deleted rather than linger. Mutation-tested four ways: dropping the hidden severity label, making an error announce politely, collapsing two variants onto one surface, and defaulting the title to a heading all fail."
}
