Alert
A feedback banner for surface-level status messages — errors, warnings, successes, and informational notices. Supports an optional heading, icon, metadata, action buttons, and a dismiss button.
Features
- 🎨 3 Variants:
flat(default),solid,bordered - 🌈 6 Color Themes:
primary,secondary,info,success,warning,error - 📏 3 Sizes:
sm,md,lg - ✖️ Dismissible: animated close button — plays a smooth fade+collapse animation before hiding
- 🖼️ Icon Slot: prepend any SVG or icon font glyph
- 🏷️ Heading: optional bold heading above the message
- 🕐 Meta Slot: timestamp or secondary info alongside the heading
- 🔘 Actions Slot: call-to-action buttons below the message (or horizontal)
- 🎯 Accented: left accent border for flat and bordered variants
- ♿ Accessible:
role="alert"+ smartaria-live, labelled close button
Source Code
View Source Code
import { defineComponent, guard, html, onMount, onSlotChange, signal } from '@vielzeug/craftit';
import type { ComponentSize, RoundedSize, ThemeColor } from '../../types';
import { closeIcon } from '../../icons';
import { forcedColorsMixin, formFieldMixins, sizeVariantMixin } from '../../styles';
import { awaitExit } from '../../utils/animation';
import componentStyles from './alert.css?inline';
export type BitAlertEvents = {
dismiss: { originalEvent: MouseEvent };
};
export type BitAlertProps = {
/** Show a left accent border (for flat/bordered variants) */
accented?: boolean;
/** Theme color */
color?: ThemeColor;
/** Show a dismissable (×) button */
dismissible?: boolean;
/** Heading text shown above the content */
heading?: string;
/** Position action buttons to the right instead of below */
horizontal?: boolean;
/** Border radius */
rounded?: RoundedSize | '';
/** Alert size */
size?: ComponentSize;
/** Visual style variant */
variant?: 'solid' | 'flat' | 'bordered';
};
/**
* A status/feedback banner with optional heading, icon slot, and dismiss button.
*
* @element bit-alert
*
* @attr {string} color - Theme color (primary/success/warning/error…)
* @attr {string} variant - Visual style: 'flat' | 'solid' | 'bordered'
* @attr {string} size - Size: 'sm' | 'md' | 'lg'
* @attr {string} rounded - Border radius size
* @attr {string} heading - Bold heading text above the content
* @attr {boolean} dismissible - Show a close (×) button
* @attr {boolean} accented - Add a left accent border (flat/bordered only)
* @attr {boolean} horizontal - Position action buttons to the right instead of below
*
* @fires dismiss - Fired when the alert is dismissed
*
* @slot - Default slot for the alert message content
* @slot icon - Icon on the left side
* @slot meta - Metadata displayed alongside the heading (lighter, right-aligned)
* @slot actions - Action buttons shown below the message
*
* @cssprop --alert-bg - Background color
* @cssprop --alert-color - Text/icon color
* @cssprop --alert-border-color - Border color
* @cssprop --alert-radius - Border radius
* @cssprop --alert-padding - Padding
* @cssprop --alert-gap - Gap between icon, body, and close button
* @cssprop --alert-font-size - Font size
*
* @example
* ```html
* <bit-alert color="success">Your changes have been saved.</bit-alert>
* <bit-alert color="error" variant="solid" dismissible heading="Something went wrong">
* Please try again later.
* </bit-alert>
* ```
*/
export const ALERT_TAG = defineComponent<BitAlertProps, BitAlertEvents>({
props: {
accented: { default: false },
color: { default: undefined },
dismissible: { default: false },
heading: { default: '' },
horizontal: { default: false },
rounded: { default: undefined },
size: { default: undefined },
variant: { default: undefined },
},
setup({ emit, host, props }) {
const handleDismiss = guard(
() => props.dismissible.value,
(e: MouseEvent) => {
host.setAttribute('dismissing', '');
awaitExit(host, () => {
host.removeAttribute('dismissing');
host.setAttribute('dismissed', '');
emit('dismiss', { originalEvent: e });
});
},
);
const hasIcon = signal(false);
const hasActions = signal(false);
onMount(() => {
onSlotChange('icon', (els) => {
hasIcon.value = els.length > 0;
});
onSlotChange('actions', (els) => {
hasActions.value = els.length > 0;
});
});
return html`
<div class="alert" :role="${() => (props.color.value === 'error' ? 'alert' : 'status')}" part="alert">
<span class="icon" part="icon" aria-hidden="true" ?hidden=${() => !hasIcon.value}>
<slot name="icon"></slot>
</span>
<div class="header" part="header" ?hidden=${() => !props.heading.value}>
<span class="heading" part="heading">${() => props.heading.value}</span>
<span class="meta" part="meta">
<slot name="meta"></slot>
</span>
</div>
<div class="body" part="body">
<div class="content" part="content">
<slot></slot>
</div>
</div>
<div class="actions" part="actions" ?hidden=${() => !hasActions.value}>
<slot name="actions"></slot>
</div>
<button
class="close"
part="close"
type="button"
aria-label="Dismiss alert"
?hidden=${() => !props.dismissible.value}
@click=${handleDismiss}>
${closeIcon}
</button>
</div>
`;
},
styles: [
...formFieldMixins,
forcedColorsMixin,
sizeVariantMixin({
lg: { '--_padding': 'var(--size-4) var(--size-5)', fontSize: 'var(--text-base)', gap: 'var(--size-3-5)' },
sm: { '--_padding': 'var(--size-2) var(--size-3)', fontSize: 'var(--text-xs)', gap: 'var(--size-2)' },
}),
componentStyles,
],
tag: 'bit-alert',
});Basic Usage
<bit-alert color="success">Your changes have been saved.</bit-alert>
<script type="module">
import '@vielzeug/buildit/alert';
</script>Variants
Three visual styles are available via the variant attribute. flat is the default.
Colors
Sizes
Heading
Use heading to add a bold heading above the message body.
Dismissible
Add dismissible to show a close (×) button. When clicked, the component plays a bit-alert-exit animation (opacity fade + height collapse) before applying [dismissed] which sets display: none. Listen to the dismiss event to remove it from the DOM or restore it later.
document.querySelector('bit-alert').addEventListener('dismiss', (e) => {
e.target.remove(); // optionally remove from DOM entirely
});Icon
Use the icon slot to add a leading icon. The icon wrapper is hidden entirely when the slot is empty — no reserved space.
Meta
Use the meta slot to add secondary information (e.g. a timestamp) displayed alongside the heading in a lighter, smaller style. Pair it with a heading for best results.
Actions
Use the actions slot to add call-to-action buttons below the message.
Horizontal Actions
Add horizontal to move the actions to the right side of the content instead of below it. Best suited for short, single-line messages without a heading.
TIP
Avoid combining horizontal with heading — it makes the layout feel cramped.
Accented
Add accented to add a thick left border for extra visual emphasis. Only applies to flat and bordered variants.
API Reference
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
color | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error' | — | Theme color |
variant | 'flat' | 'solid' | 'bordered' | 'flat' | Visual style variant |
size | 'sm' | 'md' | 'lg' | 'md' | Component size |
rounded | 'none' | 'sm' | 'md' | 'lg' | 'full' | — | Border radius override |
heading | string | '' | Bold heading above the message body |
dismissible | boolean | false | Show a close (×) button |
accented | boolean | false | Left accent border (flat/bordered variants only) |
horizontal | boolean | false | Place action buttons beside the content |
Slots
| Slot | Description |
|---|---|
| (default) | Alert message content |
icon | Icon on the leading edge. Hidden when empty — no reserved space. |
meta | Secondary info alongside the heading (lighter, right-aligned, smaller) |
actions | Action buttons below the message, or beside it when horizontal is set |
Events
| Event | Detail | Description |
|---|---|---|
dismiss | { originalEvent: MouseEvent } | Fired when the close button is clicked |
CSS Parts
| Part | Description |
|---|---|
alert | Root container element |
icon | Icon wrapper |
body | Body flex container |
header | Heading + meta row |
heading | Heading text span |
meta | Meta slot wrapper |
content | Default slot wrapper |
actions | Actions slot wrapper |
close | Dismiss button |
CSS Custom Properties
| Property | Description | Default |
|---|---|---|
--alert-bg | Background color | Theme-dependent |
--alert-color | Text and icon color | Theme-dependent |
--alert-border-color | Border color | Theme-dependent |
--alert-radius | Border radius | var(--rounded-lg) |
--alert-padding | Internal padding | var(--size-3) var(--size-4) |
--alert-gap | Gap between icon, body, and close | var(--size-3) |
--alert-font-size | Font size | var(--text-sm) |
Accessibility
The alert component follows WAI-ARIA best practices.
bit-alert
✅ Screen Readers
- Uses
role="alert"witharia-live="polite"— screen readers announce the alert on insertion. aria-liveisassertivewhencolor="error"so urgent errors interrupt immediately; all other severities usepolite.[dismissed]usesdisplay: none, removing the element from the accessibility tree entirely.
✅ Keyboard Navigation
- The close button is keyboard-reachable and has
aria-label="Dismiss alert". [dismissing]disablespointer-eventsduring the exit animation to prevent double-activation.
Dynamic insertion
Alerts injected into the DOM are announced automatically via role="alert". Avoid toggling color after insertion as it may trigger an unwanted re-announcement.
Best Practices
Do:
- Always supply a
colorto convey semantic intent (color="error"for failures,color="success"for confirmations, etc.). - Use
headingwhen you need to distinguish a short title from a longer explanation. - Use
accentedto draw extra attention to a critical flat or bordered alert. - Listen to
dismissand calle.target.remove()when you want the alert gone from the DOM, not just hidden.
Don't:
- Use
bit-alertfor short, transient notifications — use a toast/notification system instead. - Combine
horizontalwithheading— the layout becomes cramped. - Nest complex interactive controls in the default slot; keep alerts primarily informational.