Alert#

A message that announces itself, and can be dismissed. The root carries role="alert", which makes it a live region; the open state is one two-way model that defaults to true, because an alert is rendered when there is something to say. Closing it sets hidden on the root — the two states are told apart by presence, so a design system never paints closed.

Import#

TSX
import { Alert } from '@sigx/zero/alert';

Alert is a compound: Alert.Root, Alert.Icon, Alert.Title, Alert.Description, Alert.Close. It is also re-exported from the @sigx/zero root, together with alertAnatomy and useAlertContext.

Usage#

TSX
import { component } from 'sigx';
import { Alert } from '@sigx/zero/alert';

const QuotaWarning = component(({ signal }) => {
    const state = signal({ show: true });

    return () => (
        <Alert.Root model={() => state.show} color="warning">
            <Alert.Icon>!</Alert.Icon>
            <Alert.Title>Approaching your quota</Alert.Title>
            <Alert.Description>You have used 92% of this month's allowance.</Alert.Description>
            <Alert.Close label="Dismiss" />
        </Alert.Root>
    );
});

model={() => state.show} binds the alert's presence both ways — pressing Alert.Close writes false into state.show, and writing true back brings the alert back. See Models.

Uncontrolled#

TSX
<Alert.Root color="success">
    <Alert.Title>Saved</Alert.Title>
    <Alert.Close />
</Alert.Root>

Leave the model off and the state lives inside the component, seeded by defaultOpen (which defaults to true) — a bare Alert.Root with a Close button dismisses itself. openChange fires either way.

An alert that is only ever visible#

TSX
<Alert.Root color="info">
    <Alert.Icon>i</Alert.Icon>
    <Alert.Description>Prices are shown before tax.</Alert.Description>
</Alert.Root>

Without Alert.Close and without a model nothing ever closes the alert, and that is fine: the model exists so that dismissal has somewhere to live, not because every alert needs it.

Announcement#

role="alert" is the line between this component and a Card: an alert nobody is told about is a coloured box, and a coloured box is a card. The role costs nothing when the alert is server-rendered — a live region announces changes, so an alert present in the markup at load is silent, and one inserted or updated later is announced. Exactly the behaviour wanted in both cases, from one declaration.

Rendering the close button as your own element#

TSX
<Alert.Close asChild>
    {(p) => <MyIconButton {...p} icon="x" />}
</Alert.Close>

With asChild the default slot receives the part's attribute bag; spread it so the aria-label, the click handler and the press feedback land on your element.

Anatomy#

PartElementStatesFlagsNotes
rootdivopen | closedrole="alert". Carries the variant axes. Hidden in closed (hiddenIn).
iconspanaria-hidden="true". Inside root.
titledivInside root.
descriptiondivInside root.
closebuttondisabled, focus-visible, pressed, press-animatingaria-label (default Close). Publishes press feedback. asChild.

Every part carries data-scope="alert" and data-part="<part>". A closed root gets the hidden attribute from the runtime, which is what hiddenIn: ['closed'] declares: a [data-state="closed"] rule on the root can never paint, so a design system styles open and leaves the other alone. See The anatomy contract.

The icon is decorative. The severity a design system paints into it is already carried by the text, and a glyph that announced itself would say it twice.

Props#

Alert.Root#

PropTypeDefaultDescription
modelbooleanTwo-way binding of the open state.
defaultOpenbooleantrueInitial state when uncontrolled.
openChangeevent (open: boolean)Fires whenever the open state changes.
color / size / variant / axes / modsdesign-system vocabularyThe variant axes, rendered as data-* on root.
classstringExtra classes on the root element.

Alert.Icon, Alert.Title, Alert.Description#

Only class. Each renders its part around its children.

Alert.Close#

PropTypeDefaultDescription
labelstring'Close'The button's aria-label; its content is usually a glyph, so it needs a name of its own.
disabledbooleanfalseInert; renders data-disabled.
asChildbooleanfalseRender through the default slot, which receives the part bag.
classstringExtra classes.

Keyboard#

KeyAction
Enter / Space (on Alert.Close)Closes the alert — the native button activation; the press feedback tracks the key.

The alert itself is not focusable; only the close button is in the tab order.

In the shipped design systems#

Both @sigx/zero-basic and @sigx/zero-daisyui wire color (the eight recommended roles) and size (xsxl) on alert, so <Alert.Root color="error" size="lg"> is styled in both. Neither wires a variant or any mods on the scope; under a design system's /register import the variant prop is therefore absent. See Typed vocabulary.

The closed state needs no recipe — the runtime's hidden removes the root from layout — and any transition a design system wants belongs on open, where the element is actually present. A recipe that sets display on the root unconditionally would defeat hidden; if you need a display value, scope it with &:not([hidden]).

Card is the same box without the live region; Toast is the alert that arrives over a queue and goes away by itself.