GrAlert

  • core
  • feedback
  • API · 10 props

Shows an important message, warning or action status.

import { GrAlert } from '@feugene/granularity/components/GrAlert'

Overview

When to take it

  • the message belongs to a place on the page — a warning above a form, an explanation inside a section: it lives where its cause lives;
  • the message has to stay — a toast leaves on its own, this one is read for as long as it takes;
  • there is an action inside — the actions slot holds “Retry”, “Open settings”;
  • the message appears asynchronously — live announces it to a screen reader instead of leaving it silent.

When to take something else

NeedTake
A short message about the result of an actionGrToaster
A server error with a status and detailsGrResponseErrorBanner
An error on a single fieldGrFormField
The screen is empty and that needs explainingGrEmptyState
An answer from the user is requiredGrConfirmDialog

Variants every tone and view, rendered by the real component

Variants · GrAlert
tone \ variantsoftoutline
primary
neutral
success
warning
danger
info
slate
azure

The grid is live: hover and press it. Hover, focus and active states are not drawn here — they belong to the browser, not to a prop.

Playground controls come from the API, not from hand

Stage

Loading…

0 changed· state is in the URL

Examples 6 each with a live demo and its code

  • A warning next to its cause

    The message stands in the billing card, not in a corner of the screen, and stays until it is dealt with — unlike a toast. The title is the fact, the text is the consequence, and the #actions slot puts the ways out under the text. «Remind me in a week» swaps the warning for an info note of the same shape.

    BillingBusiness plan · 18 seats
    Active
    Next invoice1 November · €522.00
    Payment methodVisa •••• 4242 · expires 10/26
  • A result the screen reader hears

    One alert whose words change with the outcome of the sync: a live region announces a change of its content, while a freshly inserted one may go unheard. With live="auto" the tone picks the role — warning and danger interrupt, success waits for a pause. Run the sync a few times: the service answers differently.

    Fabrikam Books ConnectedInvoices and payments sync every hour.
    Last sync 2 hours ago
    214 invoices and 61 payments are in Fabrikam Books.
  • Tone, weight and an icon that names the subject

    Tone and variant are separate axes: the incident is danger on a tinted background, planned work is outline and does not compete with it. #icon swaps the glyph for the subject — a card, a rocket, a clock. Notices that load with the page are content, not events, so the incident gets live="off".

    System statusUpdated 2 minutes ago
    Partial outage
    Card payments are delayed
    Charges go through, but receipts arrive up to 20 minutes late. We are working on it with the payment provider.
    Release 2026.10.6 is live
    Faster exports and the new invoice editor are on for every workspace.
    Maintenance on Saturday, 11 October
    02:00–03:00 UTC. Sign-in and the API stay up; exports pause for about ten minutes.
  • Two ways to close

    With v-model:visible the cross hides the message and nobody else has to know. With only @close it is a request: the alert stays, and the screen decides — here it asks first, because turning summaries off changes the workspace for everyone.

    New: export to Excel
    Number formats and formulas survive the export. Find it under Reports → Export.
    AI summaries are on
    Every invoice and report opens with a three-line summary.

API generated from the package · cannot drift

Props

PropTypedefaultDescription
tone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
undefinedThe meaning of the message: the colour of the background and the border, the icon glyph and — with `live="auto"` — the role for a screen reader. `warning` and `danger` interrupt speech, the others wait for a pause. `info` by default; globally — `componentDefaults`.
variant"soft" | "outline" | undefinedundefinedThe weight: `soft` is a tinted background, `outline` is only a border in the tone, for calm notes next to other messages. `soft` by default.
titlestring | undefinedundefinedA short fact on the first line: «Your card expires in 12 days». The details go into the text below it; without a title the message is one line of text.
closableboolean | undefinedundefinedA close button. It does not hide the message by itself: it emits `close` and `update:visible`, and the screen decides whether to hide it (see `visible`).
liveGrAlertLive | undefinedundefinedHow the message is announced to a screen reader: `auto` — by tone, `assertive` — `role="alert"`, `polite` — `role="status"`, `off` — no role, when the message is already announced another way. `auto` by default.
iconboolean | undefinedtrueWhether to show the tone icon. An icon of your own goes into the `#icon` slot; the prop is for the case where the icon gets in the way: a narrow column, a dense list, a message inside a form.
visiblev-modelboolean | undefinedtrueThe visibility of the message — **controlled only**: without the prop the alert is always visible, and closing stays the concern of the consumer (`@close`). With `v-model:visible` the component hides itself. The prop deliberately has no state of its own. It would turn the close button into an irreversible action for everyone who already lives on `@close` and, for instance, asks for confirmation there.
backgroundColorstring | undefinedundefinedA background instead of the tone — the colour of a brand or a partner. The layout, the icon and closing stay the same. The `--gr-alert-bg` variable does the same from CSS.
textColorstring | undefinedundefinedThe colour of the text, the title and the icon instead of the tone (`--gr-alert-*` in `tokens.md`).
borderColorstring | undefinedundefinedThe colour of the border instead of the tone; with `outline` it is the main colour of the message.

Slots

SlotTypeDescription
defaultanyThe text of the message.
iconanyAn icon instead of the glyph chosen by tone. Decorative: it stays `aria-hidden`.
actionsanyActions on the message: "Retry", "Details". They go under the text.

Events

EventSignatureDescription
close(event: "close"): void—
update:visible(event: "update:visible", value: boolean): void—

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrAlertTone, GrAlertLive, GrAlertProps } from '@feugene/granularity/components/GrAlert'

Filedist/types/components/GrAlert/config.d.ts

grAlertConfigconst
const grAlertConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrAlert">;

Filedist/types/components/GrAlert/GrAlert.vue.d.ts

GrAlertTonetype
type GrAlertTone = GrTone;
GrAlertLivetype
type GrAlertLive = 'auto' | 'assertive' | 'polite' | 'off';
GrAlertPropsinterface
interface GrAlertProps {
    tone?: GrAlertTone;
    variant?: GrAlertVariant;
    title?: string;
    closable?: boolean;
    live?: GrAlertLive;
    icon?: boolean;
    visible?: boolean;
    backgroundColor?: string;
    textColor?: string;
    borderColor?: string;
}
GrAlertEmitsinterface
interface GrAlertEmits {
    (e: 'close'): void;
    (e: 'update:visible', value: boolean): void;
}

Filedist/types/components/GrAlert/defaults.d.ts

GrAlertConfigurablePropsinterface
interface GrAlertConfigurableProps {
    tone: GrTone;
    variant: GrAlertVariant;
    closable: boolean;
    live: GrAlertLive;
}

Filedist/types/components/GrAlert/grAlertStyles.d.ts

GrAlertVarianttype
type GrAlertVariant = typeof GR_ALERT_VARIANTS[number];

Usage from the library prose

Tone and variant are different axes

tone is responsible for colour and meaning (info, success, warning, danger, slate, azure), variant for weight: soft (a tinted background) or outline (a border only). Every combination makes sense.

The colours are expressed entirely in --gr-* tokens, so the message is equally correct in the light and the dark theme. For pointed adjustments there are the backgroundColor, textColor and borderColor props, or the --gr-alert-* variables (see tokens.md).

How the message is announced to a screen reader

The key role here belongs to live, and by default it is derived from the tone:

liveWhat the element getsWhen
auto (the default)alert for warning/danger, status for the restthe ordinary case
assertiverole="alert" — interrupts readingthe message demands an immediate reaction
politerole="status" — waits for a pausethe message can be finished later
offno role at allthe message has already been announced another way

The point of the default: role="alert" interrupts speech, and hanging it on every informational message turns a calm hint into an alarm.

The mode is configurable globally — an application whose alerts must not interrupt speech does not need the prop on every one of them:

vue
<GrConfigProvider :component-defaults="{ GrAlert: { live: 'polite' } }">

componentDefaults also sets tone, variant and closable.

The icon

By default the glyph is chosen by the tone. The #icon slot replaces it, the :icon="false" prop removes it:

vue
<GrAlert tone="success">
  <template #icon>
    <IconRocket />
  </template>
  Deploy finished.
</GrAlert>

<GrAlert tone="slate" :icon="false">
  Draft saved automatically.
</GrAlert>

The icon is decorative in both cases — it stays aria-hidden, because the meaning is carried by the text, not by the picture. A message without an icon reads more calmly and belongs where there are many alerts: a dense form, a list of settings.

Actions

“Retry” and “Details” buttons go into the #actions slot rather than into the text: there they get a place of their own below the message and do not break the sentence apart.

vue
<GrAlert tone="danger" title="Export failed" closable>
  The report service returned 502.

  <template #actions>
    <GrButton size="sm" tone="danger" @click="retry">Retry</GrButton>
    <GrButton size="sm" variant="outline" tone="danger">Open logs</GrButton>
  </template>
</GrAlert>
Dismissal: close versus v-model:visible

closable adds the button. What happens next is the consumer’s choice:

vue
<!-- the message stays until the screen decides otherwise -->
<GrAlert closable @close="askConfirmation" />

<!-- the message hides itself -->
<GrAlert v-model:visible="shown" closable />

visible has no state of its own, and that is deliberate: without the prop the alert does not disappear on click, it only reports the intent with a close event. Otherwise the dismiss button would become an irreversible action for everyone who asks for confirmation on close or writes a mark on the server. close is emitted in both modes.