Displays an important message inline in the page. Reach for Alert, rather than the notifications service, when the message is part of the page and should stay there until the page or the consumer removes it — a notification is transient and dismisses itself.
import { Alert } from 'frontile';
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<Alert
@title='Update available'
@description='A new version is ready to install.'
/>
</div>
</template>
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<Alert
@title='Default'
@description='A neutral, general-purpose message.'
/>
<Alert
@title='Info'
@description='Something worth knowing about.'
@status='primary'
/>
<Alert
@title='Success'
@description='The operation completed.'
@status='success'
/>
<Alert
@title='Warning'
@description='Something needs attention.'
@status='warning'
/>
<Alert
@title='Danger'
@description='Something went wrong.'
@status='danger'
/>
</div>
</template>
@variant decides how much of the alert the status colors, from a neutral
surface with a colored icon and title through to a fully filled one. Set it
alongside @status — the three below are shown across all five intents.
A neutral surface; the status shows in the icon and title only. Quiet enough to sit in a page without competing with the content around it.
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<Alert @status='neutral' @title='Default' />
<Alert @status='primary' @title='Info' />
<Alert @status='success' @title='Success' />
<Alert @status='warning' @title='Warning' />
<Alert @status='danger' @title='Danger' />
</div>
</template>
A translucent tint of the status fills the alert, over an opaque surface. More
presence than default without the weight of solid.
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<Alert @variant='soft' @status='neutral' @title='Default' />
<Alert @variant='soft' @status='primary' @title='Info' />
<Alert @variant='soft' @status='success' @title='Success' />
<Alert @variant='soft' @status='warning' @title='Warning' />
<Alert @variant='soft' @status='danger' @title='Danger' />
</div>
</template>
The status fills the surface, with contrast ink on top. The loudest of the three — worth reserving for something the reader should not miss.
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<Alert @variant='solid' @status='neutral' @title='Default' />
<Alert @variant='solid' @status='primary' @title='Info' />
<Alert @variant='solid' @status='success' @title='Success' />
<Alert @variant='solid' @status='warning' @title='Warning' />
<Alert @variant='solid' @status='danger' @title='Danger' />
</div>
</template>
@layout='banner' drops the radius and border and centres the content, for an
announcement spanning the width of its container — a notice under a Drawer's
header, or across the top of a panel.
Width is not what the argument controls: an Alert is full-width in either layout. What changes is that a banner has no edges of its own, so it reads as part of the surface it sits on rather than as a card resting on it.
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<div
class='w-full overflow-hidden rounded-lg border border-surface-overlay-mild'
>
<div class='bg-surface-modal px-4 py-3 font-label text-label-xs'>
Panel header
</div>
<Alert
@layout='banner'
@variant='soft'
@status='warning'
@title='This is the banner text'
/>
<div class='bg-surface-modal px-4 py-6 text-body-2xs text-neutral-firm'>
Panel content
</div>
</div>
</div>
</template>
A banner's close button is pinned to the trailing edge instead of sitting in the row, so the centred text stays put whether or not the alert is dismissible — two banners, one dismissible and one not, still line up with each other.
import { Alert, Button } from 'frontile';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
export default class BannerCloseExample extends Component {
@tracked isVisible = true;
close = () => {
this.isVisible = false;
};
reset = () => {
this.isVisible = true;
};
<template>
<div class='demo-stack'>
<div
class='w-full overflow-hidden rounded-lg border border-surface-overlay-mild'
>
<Alert
@layout='banner'
@variant='soft'
@status='primary'
@title='Not dismissible'
/>
{{#if this.isVisible}}
<Alert
@layout='banner'
@variant='soft'
@status='primary'
@title='Dismissible'
@onClose={{this.close}}
@closeButtonTitle='Dismiss the banner'
/>
{{/if}}
</div>
{{#unless this.isVisible}}
<Button @size='xs' @onPress={{this.reset}}>Show the banner again</Button>
{{/unless}}
</div>
</template>
}
The icon block replaces the status glyph with anything you pass it — a Spinner is a
convenient way to build a loading alert, since there is no dedicated loading argument.
@hideIcon removes it entirely and wins over the block.
import { Alert, Spinner } from 'frontile';
<template>
<div class='demo-stack'>
<Alert @title='Syncing'>
<:icon><Spinner @size='sm' /></:icon>
</Alert>
<Alert @title='No icon' @hideIcon={{true}} />
</div>
</template>
The actions block renders buttons in a row between the content and the close button.
Alert follows the same styling convention as NotificationCard: @size='xs', the first
button's @status matching the alert's own, and any further button using
@variant='plain'.
import { Alert, Button } from 'frontile';
<template>
<div class='demo-stack'>
<Alert
@status='warning'
@title='Unsaved changes'
@description='Save before you leave?'
>
<:actions>
<Button @size='xs' @status='warning'>Save</Button>
<Button @size='xs' @variant='plain'>Discard</Button>
</:actions>
</Alert>
</div>
</template>
Passing @onClose reveals the close button. Alert does not hide itself when it is
pressed — the consumer removes the Alert from the DOM, so animating it out or persisting
the dismissal is the application's to decide.
import { Alert, Button } from 'frontile';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
export default class ClosableAlertExample extends Component {
@tracked isVisible = true;
close = () => {
this.isVisible = false;
};
reset = () => {
this.isVisible = true;
};
<template>
<div class='demo-stack'>
{{#if this.isVisible}}
<Alert
@status='success'
@title='Changes saved'
@onClose={{this.close}}
@closeButtonTitle='Dismiss saved message'
/>
{{else}}
<Button @size='xs' @onPress={{this.reset}}>Show alert again</Button>
{{/if}}
</div>
</template>
}
Set @closeButtonTitle when several alerts sit together, since every close button
otherwise announces as just "Close" without saying what is being dismissed.
The description block takes markup, such as a list, where the @description argument
only takes a string.
import { Alert } from 'frontile';
<template>
<div class='demo-stack'>
<Alert @status='primary' @title='Before you continue'>
<:description>
<ul class='list-disc pl-4'>
<li>Your session expires in 10 minutes.</li>
<li>Unsaved changes are not recovered.</li>
</ul>
</:description>
</Alert>
</div>
</template>
@status sets the ARIA role along with the color and icon: warning and danger render
role="alert"; every other status renders role="status". @role overrides this — use
'none' for an alert present in the DOM at first paint, where a live region announces
nothing useful and alert can interrupt a screen reader mid-page. Leave the default for an
alert inserted in response to an event, where the role is what gets it announced at all.
Colour alone should not carry the meaning of @status. A danger alert reads as a problem
to a sighted user and as an ordinary alert to everyone else, so put the state in the
@title or @description as well.
Element: HTMLDivElement
Displays an important message inline in the page.
The static counterpart to NotificationCard: same statuses and visual
recipes, but rendered as part of the page rather than pushed through the
notifications service.
| Name | Type | Default | Description |
|---|---|---|---|
class
|
string
|
- | Custom class name, it will override the default ones using Tailwind Merge library. |
classes
|
SlotsToClasses<'base' | 'title' | 'icon' | 'content' | 'description' | 'closeButton' | 'inner' | 'actions'>
|
- | Custom CSS classes for styling the individual slots. |
closeButtonTitle
|
string
|
'Close'
|
The accessible name of the close button. Worth setting when several alerts sit together, since every close button would otherwise be announced as just "Close" without saying what is being dismissed. |
description
|
string
|
- |
The supporting copy under the title. Ignored when a description
block is passed — use the block for anything that needs markup, such
as a list or a link.
|
hideIcon
|
boolean
|
false
|
Removes the icon. Wins over the icon block if both are supplied.
|
layout
|
enum
|
'inline'
|
Width is not what this controls: an Alert is |
onClose
|
function
|
- |
Called when the close button is pressed. Passing this argument is what reveals the close button. Alert does not hide itself — the consumer removes it from the DOM, so showing it again, animating it out, or persisting the dismissal are all the application's to decide. |
role
|
enum
|
- |
Overrides the ARIA role, which otherwise comes from That default suits an alert inserted in response to an event. Use
|
status
|
enum
|
'default'
|
The status of the alert, which drives its colour, its default icon, and its default ARIA role. |
title
|
string
|
- |
The alert's heading. Ignored when a title block is passed.
|
variant
|
enum
|
'surface'
|
The visual style of the alert. |
| Name | Type | Default | Description |
|---|---|---|---|
icon
*
|
Array
|
- |
Replaces the default status glyph. Ignored when @hideIcon is set.
|
title
*
|
Array
|
- |
Overrides @title.
|
description
*
|
Array
|
- |
Overrides @description. Takes markup.
|
actions
*
|
Array
|
- | Buttons, rendered in a row between the content and the close button. |