Frontile

Alert

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

import { Alert } from 'frontile';

Usage

Update available
A new version is ready to install.
import { Alert } from 'frontile';

<template>
  <div class='demo-stack'>
    <Alert
      @title='Update available'
      @description='A new version is ready to install.'
    />
  </div>
</template>

Statuses

Default
A neutral, general-purpose message.
Info
Something worth knowing about.
Success
The operation completed.
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>

Variants

@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.

Default

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.

Default
Info
Success
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>

Soft

A translucent tint of the status fills the alert, over an opaque surface. More presence than default without the weight of solid.

Default
Info
Success
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>

Solid

The status fills the surface, with contrast ink on top. The loudest of the three — worth reserving for something the reader should not miss.

Default
Info
Success
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.

Panel header
Panel content
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.

Not dismissible
Dismissible
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>
}

Icon

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.

Syncing
No icon
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>

Actions

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>

Closing

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.

Changes saved
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.

Rich content

The description block takes markup, such as a list, where the @description argument only takes a string.

Before you continue
  • Your session expires in 10 minutes.
  • Unsaved changes are not recovered.
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>

Accessibility

@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.

API

Alert

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.

Arguments

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'

banner drops the radius and border and centres the content, for a full-bleed announcement bar spanning its container — a notice under a Drawer's header, say.

Width is not what this controls: an Alert is w-full in either layout. A banner's close button is pinned to the trailing edge rather than sitting in the row, so the centred text does not shift when it is present.

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 @status: warning and danger render role="alert", every other status renders role="status".

That default suits an alert inserted in response to an event. Use 'none' for one present in the DOM at first paint, where a live region announces nothing useful and alert can interrupt a screen reader mid-page.

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.

Blocks

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.
Released under MIT License - Created by Josemar Luedke