# Alert

An inline message that stays on the page: an icon, a title, a description, and an optional action or dismiss control.

Block: `smbk-alert`. Status: candidate. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `tone` | `default`, `info`, `success`, `warning`, `danger` | `default` | Neutral, or one of the four feedback families on their subtle surface. Figma calls this "Variant". |

Parts: `smbk-alert__icon`, `smbk-alert__body`, `smbk-alert__title`, `smbk-alert__description`, `smbk-alert__action`, `smbk-alert__dismiss`

## Markup

```html
<div role="status" class="smbk-alert smbk-alert--tone-default">
  <i class="ph ph-bell smbk-icon smbk-icon--size-md smbk-alert__icon" aria-hidden="true"></i>
  <div class="smbk-alert__body">
    <p class="smbk-alert__title">New version available</p>
    <div class="smbk-alert__description">Reload the page to get the latest features.</div>
  </div>
  <div class="smbk-alert__action">
    <button type="button" class="smbk-btn smbk-btn--tone-secondary smbk-btn--size-sm">
      <span class="smbk-btn__label">Reload</span>
    </button>
  </div>
</div>
```

## React

```tsx
import { Alert } from './design-system/components/alert/alert.js';
import { Button } from './design-system/components/button/button.js';

<Alert tone="default" title="New version available" action={<Button tone="secondary" size="sm">Reload</Button>}>Reload the page to get the latest features.</Alert>
```

## Stylesheet

```css
/* smbk-alert — Alert. Figma "Alert" 4139:6031. */

.smbk-alert {
  /* The neutral tone; each feedback tone sets these again below (the tone recipe). */
  --smbk-tone-surface: var(--smbk-color-surface-default);
  --smbk-tone-border: var(--smbk-color-border-rim);
  --smbk-tone-text: var(--smbk-color-text-default);
  --smbk-tone-icon: var(--smbk-color-icon-subtle);
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  gap: var(--smbk-space-inline-lg);
  width: 100%;
  padding: var(--smbk-space-inset-lg) var(--smbk-space-inset-xl);
  border: var(--smbk-border-width-hairline) solid var(--smbk-tone-border);
  border-radius: var(--smbk-radius-container-sm);
  background: var(--smbk-tone-surface);
  color: var(--smbk-tone-text);
}

/* The icon's box is as tall as the title's line, so the icon sits on the first line's middle. */
.smbk-alert__icon {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  height: calc(var(--smbk-typography-label-lg-font-size) * var(--smbk-typography-label-lg-line-height));
  color: var(--smbk-tone-icon);
  font-size: var(--smbk-size-icon-md);
}

/* The title sits on its text with no gap: the line heights give the air. */
.smbk-alert__body {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  gap: var(--smbk-space-text-label);
  min-width: 0;
}

.smbk-alert__title {
  margin: 0;
  font-family: var(--smbk-typography-label-lg-font-family);
  font-size: var(--smbk-typography-label-lg-font-size);
  font-weight: var(--smbk-typography-label-lg-font-weight);
  line-height: var(--smbk-typography-label-lg-line-height);
  letter-spacing: var(--smbk-typography-label-lg-letter-spacing);
  text-transform: var(--smbk-typography-label-lg-text-transform);
}

.smbk-alert__description {
  margin: 0;
  color: var(--smbk-color-text-subtle);
  font-family: var(--smbk-typography-body-sm-font-family);
  font-size: var(--smbk-typography-body-sm-font-size);
  font-weight: var(--smbk-typography-body-sm-font-weight);
  line-height: var(--smbk-typography-body-sm-line-height);
  letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
}

/* The description holds a sentence, several paragraphs or a short bulleted list, in its own text
   style: paragraphs a paragraph apart, list items a line of list spacing apart, the bullets inside
   the alert's box. */
.smbk-alert__description > :where(p, ul, ol) {
  margin: 0;
}

.smbk-alert__description > * + * {
  margin-block-start: var(--smbk-space-text-paragraph);
}

.smbk-alert__description :where(ul, ol) {
  display: flex;
  flex-direction: column;
  gap: var(--smbk-space-text-list);
  padding-inline-start: var(--smbk-space-inset-xl);
}

/* One action or several; several wrap when the room runs out. */
.smbk-alert__action {
  display: flex;
  flex-shrink: 0;
  flex-wrap: wrap;
  gap: var(--smbk-space-inline-md);
}

/* .smbk-alert__dismiss: the cross is the size of the icon, in a box big enough to press, centred on the title's line;
   the box reaches past the glyph by the same amount on every side, so the glyph sits where Figma draws it. It is the
   small press box, with its focus ring and its press area on a touch screen, shared in press-box/press-box.css. */

/* ---- feedback tones: the tone recipe, each family's subtle surface, its border, its text and its icon ---- */

.smbk-alert--tone-info { --smbk-tone-surface: var(--smbk-color-feedback-info-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-info-border); --smbk-tone-text: var(--smbk-color-feedback-info-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-info-icon); }
.smbk-alert--tone-success { --smbk-tone-surface: var(--smbk-color-feedback-success-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-success-border); --smbk-tone-text: var(--smbk-color-feedback-success-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-success-icon); }
.smbk-alert--tone-warning { --smbk-tone-surface: var(--smbk-color-feedback-warning-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-warning-border); --smbk-tone-text: var(--smbk-color-feedback-warning-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-warning-icon); }
.smbk-alert--tone-danger { --smbk-tone-surface: var(--smbk-color-feedback-danger-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-danger-border); --smbk-tone-text: var(--smbk-color-feedback-danger-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-danger-icon); }

/* ---- the narrow form: the action goes under the title and description, lined up with them, so
   the words keep the alert's full width (Figma's Narrow layout). The cross stays on the first line. ---- */

@container style(--smbk-layout: narrow) {
  .smbk-alert {
    flex-wrap: wrap;
    row-gap: var(--smbk-space-stack-sm);
  }

  .smbk-alert__action {
    box-sizing: border-box;
    flex-basis: 100%;
    order: 1;
    padding-inline-start: calc(var(--smbk-size-icon-md) + var(--smbk-space-inline-lg));
  }
}
```

## Tokens it reads

```json
{
  "size": {
    "target": {
      "min": {
        "$type": "dimension",
        "$value": "{{size.24}}",
        "$description": "Smallest tap area allowed (24, WCAG 2.2)."
      },
      "comfortable": {
        "$type": "dimension",
        "$value": "{{size.44}}",
        "$description": "Comfortable touch target (44)."
      }
    },
    "icon": {
      "md": {
        "$type": "dimension",
        "$value": "{{size.20}}",
        "$description": "Icon box, medium."
      }
    }
  },
  "typography": {
    "label-lg": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "16px",
        "fontWeight": 500,
        "lineHeight": 1.4,
        "letterSpacing": "0px"
      },
      "$description": "Label LG: labels on controls and short UI text (buttons, chips, tabs, menu headings). Geist Medium, sentence case. Code and keys use Code SM and Code MD."
    },
    "body-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    }
  },
  "radius": {
    "full": {
      "$type": "dimension",
      "$value": "{{radius.9999}}",
      "$description": "Capsule: pills, chips, switches."
    },
    "container": {
      "sm": {
        "$type": "dimension",
        "$value": "{{radius.8}}",
        "$description": "Corners of cards, panels and dialogs, small."
      }
    }
  },
  "focus": {
    "ring": {
      "width": {
        "$type": "dimension",
        "$value": "{{border-width.2}}",
        "$description": "Thickness of the focus ring."
      },
      "offset": {
        "$type": "dimension",
        "$value": "{{space.2}}",
        "$description": "Gap between a control and its focus ring."
      }
    }
  },
  "color": {
    "border": {
      "focus": {
        "$type": "color",
        "$value": "{{color.red.600}}",
        "$description": "Keyboard focus ring on controls."
      },
      "rim": {
        "$type": "color",
        "$value": "{{color.alpha.white.90}}",
        "$description": "The light rim that makes a surface look lit: fields, chosen pills, cards."
      }
    },
    "surface": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.100}}",
        "$description": "Standard surface: cards, panels, the header."
      }
    },
    "text": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Main text."
      },
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      }
    },
    "icon": {
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Quiet icons."
      }
    },
    "feedback": {
      "info": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.blue.200}}",
          "$description": "Info messages: pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.blue.500}}",
          "$description": "Info messages: edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.blue.700}}",
          "$description": "Info messages: text on the pale fill."
        },
        "icon": {
          "$type": "color",
          "$value": "{{color.blue.600}}",
          "$description": "The info tone’s own icon (an info sign), so the tone is never shown by colour alone."
        }
      },
      "success": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.green.100}}",
          "$description": "Success messages: pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.green.500}}",
          "$description": "Success messages: edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.green.700}}",
          "$description": "Success messages: text on the pale fill."
        },
        "icon": {
          "$type": "color",
          "$value": "{{color.green.600}}",
          "$description": "The success tone’s own icon (a check), so the tone is never shown by colour alone."
        }
      },
      "warning": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.orange.200}}",
          "$description": "Warning messages: pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.orange.500}}",
          "$description": "Warning messages: edge."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.orange.700}}",
          "$description": "Warning messages: text on the pale fill."
        },
        "icon": {
          "$type": "color",
          "$value": "{{color.orange.600}}",
          "$description": "The warning tone’s own icon (a warning sign), so the tone is never shown by colour alone."
        }
      },
      "danger": {
        "surface-subtle": {
          "$type": "color",
          "$value": "{{color.red.200}}",
          "$description": "Danger messages: pale fill."
        },
        "border": {
          "$type": "color",
          "$value": "{{color.red.500}}",
          "$description": "Danger messages: edge; the edge of a field in error (3:1)."
        },
        "text-on-subtle": {
          "$type": "color",
          "$value": "{{color.red.700}}",
          "$description": "Danger messages: text on the pale fill."
        },
        "icon": {
          "$type": "color",
          "$value": "{{color.red.600}}",
          "$description": "The danger tone’s own icon (an alert sign), so the tone is never shown by colour alone."
        }
      }
    }
  },
  "space": {
    "inline": {
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Gap between things side by side, large."
      },
      "md": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things side by side, medium."
      }
    },
    "inset": {
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Padding inside a component, large."
      },
      "xl": {
        "$type": "dimension",
        "$value": "{{space.16}}",
        "$description": "Padding inside a component, extra large."
      }
    },
    "text": {
      "label": {
        "$type": "dimension",
        "$value": "{{space.0}}",
        "$description": "Between a label or title and the line of supporting text right under it, inside a component. None: untrimmed line height already spaces the two lines."
      },
      "paragraph": {
        "$type": "dimension",
        "$value": "{{space.stack.lg}}",
        "$description": "Between two paragraphs (16). Text is not trimmed, so each line’s own leading adds to it."
      },
      "list": {
        "$type": "dimension",
        "$value": "{{space.stack.sm}}",
        "$description": "Between the items of a list (8)."
      }
    },
    "stack": {
      "sm": {
        "$type": "dimension",
        "$value": "{{space.8}}",
        "$description": "Gap between things stacked one above another, small."
      }
    }
  },
  "border-width": {
    "hairline": {
      "$type": "dimension",
      "$value": "{{border-width.1}}",
      "$description": "Thin lines: dividers and almost every edge."
    }
  }
}
```

## Purpose

A message that belongs to the page and stays until the situation changes: a warning above a form, a notice that something is unavailable, confirmation that an action went through. It is inline, which is what separates it from a toast, which arrives and leaves on its own.

## Anatomy

- The block, a bordered row on the default or a subtle feedback surface.
- The icon part, a glyph at the start, centered on the title's line. Each tone has its own: a bell for the neutral tone, an info circle, a tick, a warning sign and a crossed circle for info, success, warning and danger, in the tone's icon color. Any Phosphor icon can replace it.
- The body part, holding the title part and the description part, the title sitting straight on its text. The description holds a sentence, several paragraphs or a short bulleted list, in the description's text style.
- The action part, an optional small secondary button (Figma's Action boolean), or several.
- The dismiss part, an optional cross button, the size of the icon in a box big enough to press.

## When to use

- Explaining a state the person needs to know about while they work.
- Confirming a completed action where the result stays visible.
- A note set into the page's own text, such as a warning at the step of a tutorial where things break. It is static, not live.

## When not to use

- Short-lived confirmations. Use a [toast](toast.md).
- Field-level errors. Those sit under the field.

## Behavior

None of its own. The dismiss control calls back and the page removes the alert; the action is whatever the page gives it.

In a narrow space, such as a phone, the action moves under the title and description, lined up with them, so the words keep the alert's full width; several buttons wrap onto more lines when they need to. The cross stays at the end of the first line. The form follows the layout switch, so an element around the alert can force either.

## Accessibility

### What it does for you

- An alert is live by default: a message that arrives while the reader is on the page. Danger and warning alerts carry the alert role and are announced at once; the others carry the status role and are announced politely.
- The wrapper sets the role from `live` and the tone, and a role given to it is ignored: choose the announcement with `live`.
- The dismiss control is a real button with an accessible name.
- The tone is shown by the icon's shape and the text as well as the color.

### What you must do

- Give an alert that is part of the page as it loads `live` as false. A note inside an article or a standing notice under a heading is not a message and must not be announced. It carries the note role and is read in place with the text around it. Its tone still shows through the icon and the words, and it looks the same either way.
- Choose by how the alert gets there, not by its tone. A warning written into the page is static; a success that appears after the reader saves is live.
- Keep a list in the description short, since it is read as part of the one message. Put nothing that can be pressed in it: the action and the dismiss control are the alert's only controls.
- Give a replacement icon that carries the tone as clearly as the one it replaces.
- In plain markup, give it the role the wrapper would: note for a static alert, alert for a live danger or warning alert, status for any other live alert.
- Give the dismiss cross's name, and every other word the alert says by itself, in the page's own language. They keep their English unless the page gives its own.

### How to test

- Tab through it: the action and the dismiss control take focus in turn, with the focus ring showing in both themes and in a forced-color mode.
- Press Enter, then Space on the dismiss control: each dismisses the alert.
- With a screen reader, a live alert is announced when it appears, a danger or warning one at once. A static one is read in place and not announced.
- With text at 200% and the page 320 pixels wide, the title and description wrap and the controls stay reachable.

### Keyboard

| Key | What it does |
|---|---|
| Tab | Moves focus to the action, then to the dismiss control. |
| Enter or Space | Presses the focused action or dismiss control. |

## Usage rules

### Do

- Write the title as the situation and the description as what to do about it.

### Don't

- Show more than one alert of the same tone in one place. Combine them.
