Alert

A contextual feedback strip.

Tones

A tone is required. Pair .alert with .alert--neutral for the plain surface look, or with an intent like .alert--success for a tinted variant. A bare .alert renders transparent, the same contract as .btn.

Some neutral message here.
Heads up, your trial ends in 3 days.
Your changes have been saved successfully.
Some features may not work.
Payment failed. Check your card details.
Some useful information here.
<div class="demo-stack">
  <div class="alert alert--neutral">
    <i data-lucide="info"></i>
    <div>Some neutral message here.</div>
  </div>
  <div class="alert alert--primary">
    <i data-lucide="info"></i>
    <div>Heads up, your trial ends in 3 days.</div>
  </div>
  <div class="alert alert--success">
    <i data-lucide="check-circle-2"></i>
    <div>Your changes have been saved successfully.</div>
  </div>
  <div class="alert alert--warning">
    <i data-lucide="triangle-alert"></i>
    <div>Some features may not work.</div>
  </div>
  <div class="alert alert--danger">
    <i data-lucide="x-circle"></i>
    <div>Payment failed. Check your card details.</div>
  </div>
  <div class="alert alert--info">
    <i data-lucide="info"></i>
    <div>Some useful information here.</div>
  </div>
</div>

Without icon

There's no icon wrapper class. The leading icon is any direct <svg> or <i> child. Skip it and the row reflows around the text.

Heads up, your trial ends in 3 days.
Your changes have been saved successfully.
<div class="demo-stack">
  <div class="alert alert--neutral">Heads up, your trial ends in 3 days.</div>
  <div class="alert alert--success">Your changes have been saved successfully.</div>
</div>

With heading and description

Add .alert__description for a stacked layout. The heading sits above the description, the icon aligns with the heading, and the row gains a bit of breathing room.

Alert Title
Alert Description
<div class="alert alert--neutral">
  <i data-lucide="info"></i>
  <div class="alert__heading">Alert Title</div>
  <div class="alert__description">Alert Description</div>
</div>

Same layout, with an intent.

Heads up
A new version is available. Refresh to load it.
Unsaved changes
Leaving this page will discard your edits.
Payment failed
We couldn't charge your card. Update billing details and retry.
<div class="demo-stack">
  <div class="alert alert--info">
    <i data-lucide="info"></i>
    <div class="alert__heading">Heads up</div>
    <div class="alert__description">A new version is available. Refresh to load it.</div>
  </div>
  <div class="alert alert--warning">
    <i data-lucide="triangle-alert"></i>
    <div class="alert__heading">Unsaved changes</div>
    <div class="alert__description">Leaving this page will discard your edits.</div>
  </div>
  <div class="alert alert--danger">
    <i data-lucide="x-circle"></i>
    <div class="alert__heading">Payment failed</div>
    <div class="alert__description">We couldn't charge your card. Update billing details and retry.</div>
  </div>
</div>

Action slot

.alert__action is the trailing slot. It pushes to the right on single-line alerts and centers vertically when a description is present. Drop in a button, link, or any control.

Message deleted successfully.
<div class="alert alert--neutral">
  <i data-lucide="info"></i>
  <div>Message deleted successfully.</div>
  <div class="alert__action">
    <button type="button" class="btn btn--neutral btn--sm">Undo</button>
  </div>
</div>

Multiple actions and intent variants.

Your session is about to expire.
New version available
Reload to pick up the latest changes.
<div class="demo-stack">
  <div class="alert alert--warning">
    <i data-lucide="triangle-alert"></i>
    <div>Your session is about to expire.</div>
    <div class="alert__action">
      <button type="button" class="btn btn--ghost btn--neutral btn--sm">Stay</button>
      <button type="button" class="btn btn--neutral btn--sm">Extend</button>
    </div>
  </div>
  <div class="alert alert--info">
    <i data-lucide="info"></i>
    <div class="alert__heading">New version available</div>
    <div class="alert__description">Reload to pick up the latest changes.</div>
    <div class="alert__action">
      <button type="button" class="btn btn--primary btn--sm">Reload</button>
    </div>
  </div>
</div>

Dismissible

Alert ships no special close control. Drop a .btn--ghost.btn--neutral.btn--icon-only.btn--sm with an X icon inside .alert__action. JS to close the alert is wired in Step 4; today the button is markup-only.

Your changes have been saved.
Couldn't connect
We lost contact with the server. Try again in a moment.
<div class="demo-stack">
  <div class="alert alert--success">
    <i data-lucide="check-circle-2"></i>
    <div>Your changes have been saved.</div>
    <div class="alert__action">
      <button type="button" class="btn btn--ghost btn--neutral btn--icon-only btn--sm" aria-label="Dismiss">
        <i data-lucide="x"></i>
      </button>
    </div>
  </div>
  <div class="alert alert--danger">
    <i data-lucide="x-circle"></i>
    <div class="alert__heading">Couldn't connect</div>
    <div class="alert__description">We lost contact with the server. Try again in a moment.</div>
    <div class="alert__action">
      <button type="button" class="btn btn--ghost btn--neutral btn--icon-only btn--sm" aria-label="Dismiss">
        <i data-lucide="x"></i>
      </button>
    </div>
  </div>
</div>

.alert__link reads --alert-link-color. That resolves to primary on a neutral alert and to the intent color on tinted alerts, so the link affordance stays readable regardless of tone.

A neutral alert with a primary link.
An info alert with an info-colored link.
<div class="demo-stack">
  <div class="alert alert--neutral">
    <i data-lucide="info"></i>
    <div>A neutral alert with <a href="#" class="alert__link">a primary link</a>.</div>
  </div>
  <div class="alert alert--info">
    <i data-lucide="info"></i>
    <div>An info alert with <a href="#" class="alert__link">an info-colored link</a>.</div>
  </div>
</div>

Customization

Eight variables retune .alert without touching component CSS. Override on .alert itself, on a parent scope, or on :root. The cascade scopes the change.

Variable Default Use
--alert-radius var(--st-radius) Corner radius (per-component override via --st-alert-radius)
--alert-padding-y calc(0.625rem * var(--st-density)) Vertical padding; doubles when .alert__description is present
--alert-padding-x calc(1rem * var(--st-density)) Horizontal padding
--alert-bg transparent Background; --neutral sets --st-surface, intents set a 7% tint of the intent color
--alert-border-color transparent Border color; --neutral sets --st-border, intents set a 40% tint of the intent color
--alert-color var(--st-foreground) Body text color
--alert-icon-color var(--st-muted-foreground) Leading icon color; intents flip this to the intent color
--alert-link-color var(--st-primary) .alert__link color; intents flip this to the intent color so the link stays readable

A bare .alert stays transparent because --alert-bg and --alert-border-color default to transparent. The tone modifier provides the visible chrome, so an alert without a tone is invisible by design.