Alert & banner

Three message components, three different subjects: Alert is about the thing you are looking at, Toast is about what just happened, Banner is about the whole application. Keeping them apart is the entire reason there are three - the third case used to be a full-width Alert, which gets the corners, the edges and the stacking wrong in exactly the way a component pressed into the wrong job does.

Reference

sk-alert

Every alert carries a status icon, so it still reads in greyscale and for colour vision deficiency - the amber and green wells are close to indistinguishable for a meaningful share of readers. The icon takes the status colour; the body text stays ink, because coloured body copy is harder to read and the well already sets the tone.

<div class="sk-alert" data-status="danger" role="alert">
  <span class="sk-alert-icon"><svg class="sk-icon">...</svg></span>
  <div class="sk-alert-content">
    <p class="sk-alert-title">Title</p>
    Message
  </div>
  <button class="sk-alert-dismiss" aria-label="Dismiss">...</button>
</div>
AttributeTypeDefaultNotes
data-status"info" | "success" | "warning" | "danger""info"
data-variant"bare"filled wellDrops the fill for a coloured rule down the leading edge, for alerts inside an already-busy surface.
role"alert" | "status"-Only for alerts that arrive. alert interrupts (danger, warning); status waits for a pause (info, success). Omit entirely for an alert present at first render - announcing something the user did not cause is noise.
sk-alert-dismisschild button-The design system ships no JavaScript and does not remove the node. Wire it yourself: hx-target="closest .sk-alert" hx-swap="outerHTML".

sk-banner

A band across the top of the page, above the navbar - under a sticky header it would scroll away and take the message with it, which for "you are in read-only mode" is precisely wrong. Edge to edge and square, because this is a band and not a card that happens to be wide. It is not live by default: a banner is normally rendered with the page, and a live region firing on first paint interrupts the reader before they have heard what page they are on.

<div class="sk-banner" data-status="warning">
  <svg class="sk-icon">…</svg>
  <p class="sk-banner-text">Your trial ends in 3 days.</p>
  <div class="sk-banner-actions">…</div>
</div>
AttributeTypeDefaultNotes
data-status"info" | "success" | "warning" | "danger""info"
data-variant"accent"-The product's own colour rather than a status - a launch, a new feature. News, not a warning.
role"status" | "alert"-Only when the banner is swapped in after load - a websocket telling everyone the service just went read-only.
sk-banner-dismisschild button-Rendered and named; removing the node needs script, so the application says what it does. Same honesty as the alert.

Examples

Statuses

Scheduled maintenance

The service will be read-only on Sunday between 02:00 and 04:00 UTC.

Settings saved

Your changes are live.

Approaching your quota

You have used 92% of the storage on this plan.

Could not save

Two fields need attention before this can be submitted.
Show markup
<div class="sk-flex sk-flex-col sk-gap-3">
  <div class="sk-alert" data-status="info">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="M12 11v5">
        </path>
        <path d="M12 8h.01">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      <p class="sk-alert-title">
        Scheduled maintenance
      </p>
      The service will be read-only on Sunday between 02:00 and 04:00 UTC.
    </div>
  </div>
  <div class="sk-alert" data-status="success">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="m8.5 12.5 2.5 2.5 4.5-5">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      <p class="sk-alert-title">
        Settings saved
      </p>
      Your changes are live.
    </div>
  </div>
  <div class="sk-alert" data-status="warning">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <path d="M10.3 4.3 2.6 17.4a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0Z">
        </path>
        <path d="M12 10v4">
        </path>
        <path d="M12 17h.01">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      <p class="sk-alert-title">
        Approaching your quota
      </p>
      You have used 92% of the storage on this plan.
    </div>
  </div>
  <div class="sk-alert" data-status="danger">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="m15 9-6 6">
        </path>
        <path d="m9 9 6 6">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      <p class="sk-alert-title">
        Could not save
      </p>
      Two fields need attention before this can be submitted.
    </div>
  </div>
</div>

Without a title

A one-line alert does not need one.

Drafts are kept for thirty days.
Copied to the clipboard.
Show markup
<div class="sk-flex sk-flex-col sk-gap-3">
  <div class="sk-alert" data-status="info">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="M12 11v5">
        </path>
        <path d="M12 8h.01">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      Drafts are kept for thirty days.
    </div>
  </div>
  <div class="sk-alert" data-status="success">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="m8.5 12.5 2.5 2.5 4.5-5">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      Copied to the clipboard.
    </div>
  </div>
</div>

Banner

Shown in flow here. In an application these sit above the navbar, full width, and stay there.

Your trial ends in 3 days.

The service is in read-only mode while we finish a migration.

Kiwi Kart 1.4 is out - eight new tracks.

Show markup
<div class="sk-flex sk-flex-col sk-gap-3">
  <div class="sk-banner" data-status="warning">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <path d="M10.3 4.3 2.6 17.4a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0Z">
      </path>
      <path d="M12 10v4">
      </path>
      <path d="M12 17h.01">
      </path>
    </svg>
    <p class="sk-banner-text">
      Your trial ends in 3 days.
      <div class="sk-banner-actions">
        <button class="sk-button" type="button" data-variant="solid" data-size="sm">
          Upgrade
        </button>
      </div>
    </p>
    <button type="button" class="sk-banner-dismiss" aria-label="Dismiss">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <path d="M18 6 6 18">
        </path>
        <path d="m6 6 12 12">
        </path>
      </svg>
    </button>
  </div>
  <div class="sk-banner" data-status="danger">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <circle cx="12" cy="12" r="9">
      </circle>
      <path d="m15 9-6 6">
      </path>
      <path d="m9 9 6 6">
      </path>
    </svg>
    <p class="sk-banner-text">
      The service is in read-only mode while we finish a migration.
    </p>
  </div>
  <div class="sk-banner" data-variant="accent">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <path d="M12 5v14">
      </path>
      <path d="M5 12h14">
      </path>
    </svg>
    <p class="sk-banner-text">
      Kiwi Kart 1.4 is out - eight new tracks.
      <div class="sk-banner-actions">
        <button class="sk-button" type="button" data-variant="quiet" data-size="sm">
          Read more
        </button>
      </div>
    </p>
    <button type="button" class="sk-banner-dismiss" aria-label="Dismiss">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <path d="M18 6 6 18">
        </path>
        <path d="m6 6 12 12">
        </path>
      </svg>
    </button>
  </div>
</div>

Bare

No fill, just a rule. For alerts sitting inside a card or another tinted surface.

A info message with no fill behind it.
A success message with no fill behind it.
A warning message with no fill behind it.
A danger message with no fill behind it.
Show markup
<div class="sk-flex sk-flex-col sk-gap-3">
  <div class="sk-alert" data-status="info" data-variant="bare">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="M12 11v5">
        </path>
        <path d="M12 8h.01">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      A info message with no fill behind it.
    </div>
  </div>
  <div class="sk-alert" data-status="success" data-variant="bare">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="m8.5 12.5 2.5 2.5 4.5-5">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      A success message with no fill behind it.
    </div>
  </div>
  <div class="sk-alert" data-status="warning" data-variant="bare">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <path d="M10.3 4.3 2.6 17.4a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0Z">
        </path>
        <path d="M12 10v4">
        </path>
        <path d="M12 17h.01">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      A warning message with no fill behind it.
    </div>
  </div>
  <div class="sk-alert" data-status="danger" data-variant="bare">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="m15 9-6 6">
        </path>
        <path d="m9 9 6 6">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      A danger message with no fill behind it.
    </div>
  </div>
</div>

With actions

Unsaved changes

Leaving this page will discard them.
Show markup
<div class="sk-alert" data-status="warning">
  <span class="sk-alert-icon">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <path d="M10.3 4.3 2.6 17.4a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0Z">
      </path>
      <path d="M12 10v4">
      </path>
      <path d="M12 17h.01">
      </path>
    </svg>
  </span>
  <div class="sk-alert-content">
    <p class="sk-alert-title">
      Unsaved changes
    </p>
    Leaving this page will discard them.
    <div class="sk-alert-actions">
      <button class="sk-button" type="button" data-variant="solid" data-size="sm">
        Save now
      </button>
      <button class="sk-button" type="button" data-variant="quiet" data-size="sm">
        Discard
      </button>
    </div>
  </div>
</div>

Dismissible

The close button is real and focusable, but removing the node is the application's job - the design system serves no JavaScript.

New in this release

Button groups and inline alerts.
Dismissible with no wiring - the button is present and focusable but does nothing.
Show markup
<div class="sk-flex sk-flex-col sk-gap-3">
  <div class="sk-alert" data-status="info">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="M12 11v5">
        </path>
        <path d="M12 8h.01">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      <p class="sk-alert-title">
        New in this release
      </p>
      Button groups and inline alerts.
    </div>
    <button type="button" class="sk-alert-dismiss" aria-label="Dismiss" hx-delete="/notices/release" hx-swap="outerHTML" hx-target="closest .sk-alert">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <path d="M18 6 6 18">
        </path>
        <path d="m6 6 12 12">
        </path>
      </svg>
    </button>
  </div>
  <div class="sk-alert" data-status="success">
    <span class="sk-alert-icon">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <circle cx="12" cy="12" r="9">
        </circle>
        <path d="m8.5 12.5 2.5 2.5 4.5-5">
        </path>
      </svg>
    </span>
    <div class="sk-alert-content">
      Dismissible with no wiring - the button is present and focusable but does nothing.
    </div>
    <button type="button" class="sk-alert-dismiss" aria-label="Dismiss">
      <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
        <path d="M18 6 6 18">
        </path>
        <path d="m6 6 12 12">
        </path>
      </svg>
    </button>
  </div>
</div>

As a form error

The pattern for a failed submit: an alert above the form, plus per-field errors below each control.

Show markup
<form>
  <div class="sk-flex sk-flex-col sk-gap-4">
    <div class="sk-alert" data-status="danger" role="alert">
      <span class="sk-alert-icon">
        <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
          <circle cx="12" cy="12" r="9">
          </circle>
          <path d="m15 9-6 6">
          </path>
          <path d="m9 9 6 6">
          </path>
        </svg>
      </span>
      <div class="sk-alert-content">
        <p class="sk-alert-title">
          Could not save
        </p>
        Fix the two fields below and try again.
      </div>
    </div>
    <div class="sk-field">
      <label class="sk-label" for="alert-email">
        Email
      </label>
      <input class="sk-input" id="alert-email" name="alert-email" type="email" value="not-an-email" data-size="md" aria-invalid="true" aria-describedby="alert-email-error">
      <p class="sk-error" id="alert-email-error" role="alert">
        <span aria-hidden="true">
          !
        </span>
        Enter a valid email address.
      </p>
    </div>
    <div class="sk-field">
      <label class="sk-label" for="alert-handle">
        Handle
      </label>
      <input class="sk-input" id="alert-handle" name="alert-handle" type="text" value="with spaces" data-size="md" aria-invalid="true" aria-describedby="alert-handle-error">
      <p class="sk-error" id="alert-handle-error" role="alert">
        <span aria-hidden="true">
          !
        </span>
        Handles cannot contain spaces.
      </p>
    </div>
    <button class="sk-button" type="submit" data-variant="solid" data-size="md">
      Save
    </button>
  </div>
</form>