Skip to content

Components

Alert

Alerts call out for user attention. Alerts should be part of the flow and used without interrupting the user's task.

Variants

Tonal (default) and outlined.

html
<article role="alert" class="alert">
  <div class="content">
		<h3>Note</h3>
		<p>This is a tonal Alert!</p>
	</div>
</article>

<article role="alert" class="alert outlined">
  <div class="content">
		<h3>Note</h3>
		<p>This is an outlined Alert!</p>
	</div>
</article>

Icon

Icon must be placed before the content.

html
<article role="alert" class="alert">
  <svg></svg>
  <div class="content">This is a tonal Alert with an icon.</div>
</article>

Severities

There are four different severities - neutral (default), ok, warning, error.

html
<article role="alert" class="alert neutral">
  <svg></svg>
  <div class="content">Warning</div>
</article>

Accessibility

  • Add role="alert" to the Alert container.
  • Use both color and icon to help distinguish between Alert severities.
  • Don't interrupt the user with an Alert. In that case, use Dialog or Snackbar.

Anatomy

  1. Container: must have role="alert"
  2. Content: text, or wrapper with .content class.
  3. Icon (optional): <svg> element
html
<article role="alert" class="alert outlined">
  <svg></svg>

  <div class="content">
    <h3>Another Alert</h3>
    <p>
      This is an outlined Alert. Why not use a
      <a class="link" href="#">Card</a> since they look very similar? For one,
      the Alert is a more focused component with different properties.
    </p>
  </div>
</article>

API

TypeModifiersDefaultDescription
Children& > .content, & > svg-Optional child content.
Severitiesneutral, .error, .ok, .warningneutralThe severity to use.
Variants.tonal, .outlined.tonalThe variant to use.

Browser compatibility

Installation

css
@layer components.has-deps {
  :where(.alert) {
    --_bg-color: var(--surface-tonal);
    --_border-color: var(--surface-tonal);
    --_color: var(--text-color-1);

    &.outlined {
      --_bg-color: var(--surface-default);
      --_border-color: var(--border-color);
      --_color: var(--text-color-1);
    }

    background-color: var(--_bg-color);
    border: 1px solid var(--_border-color);
    border-radius: var(--border-radius);
    color: var(--_color);
    padding: var(--size-3);

    & > .content {
      display: grid;
      gap: var(--size-2);
      font-size: var(--font-size-sm);

      h1,
      h2,
      h3,
      h4,
      h5,
      h6 {
        color: inherit;
        font-size: var(--font-size-md);
        font-weight: 600;

        * {
          font-size: inherit;
        }
      }
    }

    /* Colors */
    &.error,
    &.ok,
    &.warning {
      --_bg-color: var(--color-4);
      --_border-color: var(--color-9);
      --_color: var(--color-15);

      &.outlined {
        --_bg-color: var(--surface-default);
        --_border-color: var(--color-9);
        --_color: light-dark(var(--color-15), var(--color-1));
      }

      svg {
        margin-block-start: 0.15rem;
        stroke: var(--color-9);
      }

      /* Links
      * Can't make sure contrast will be acceptable (yet) so we use the current text color instead.
      */
      a[href] {
        color: inherit;

        &:hover {
          color: var(--primary);
        }
      }
    }

    /* Icon */
    &:has(svg) {
      display: grid;
      gap: var(--size-3);
      grid-template-columns: var(--size-4) 1fr;

      svg {
        margin-block-start: 0.15rem;
        stroke: currentColor;
      }
    }
  }
}
css
/* ... */
.error {
  --palette-hue: var(--oklch-red, 25);
  --palette-chroma: 1;
  --palette-hue-rotate-by: 1;
}
.ok {
  --palette-hue: var(--oklch-indigo, 310);
  --palette-chroma: 1;
  --palette-hue-rotate-by: 1;
}
.warning {
  --palette-hue: var(--oklch-warning, 75);
  --palette-chroma: 1;
  --palette-hue-rotate-by: 1;
}
/* ... */