sdxc

Type to search, or start from one of these:

Alert

An inline status message rendered as a bordered, tinted panel, colored by one of five semantic tones and announced to assistive technology through role="alert" plus a configurable aria-live politeness.

View code
let dismissed = false;

function toggleDismissed() {
	dismissed = !dismissed;
	void handle.update();
}

<div mix={[vstack({ gap: 3, align: "stretch" }), is("100%"), maxIs("34rem")]}>
	<Alert color="danger" live="assertive">
		<Alert.Icon>
			<CircleAlertIcon />
		</Alert.Icon>
		<Alert.Content>
			<Alert.Title>We could not charge your card</Alert.Title>
			<Alert.Description>
				Visa ending 4242 was declined on 14 September. Invoices stay open for seven days
				before the workspace drops to the free plan.
			</Alert.Description>
		</Alert.Content>
		<Alert.Action>
			<Button type="button" color="danger" size="sm">
				Update card
			</Button>
		</Alert.Action>
	</Alert>

	{dismissed ? (
		<Button
			type="button"
			variant="outline"
			color="neutral"
			size="sm"
			mix={[self("start"), on<HTMLButtonElement, "click">("click", toggleDismissed)]}
		>
			Show the dismissible alert
		</Button>
	) : (
		<Alert
			id="preview-alert-quota"
			color="warning"
			// A page is read at its own pace, so the countdown stays off and dismissal
			// comes only from the button. A queued notice passes a duration instead.
			mix={[
				dismiss({ duration: null }),
				on<HTMLDivElement, "ui:dismiss">("ui:dismiss", toggleDismissed),
			]}
		>
			<Alert.Icon>
				<TriangleAlertIcon />
			</Alert.Icon>
			<Alert.Content>
				<Alert.Title>You are at 92% of your build minutes</Alert.Title>
				<Alert.Description>
					Builds keep running past the limit and bill at $0.008 per minute.
				</Alert.Description>
			</Alert.Content>
			<Alert.Action>
				<Button
					type="button"
					variant="ghost"
					color="warning"
					size="sm"
					commandfor="preview-alert-quota"
					command="--ui-dismiss"
				>
					Dismiss
				</Button>
			</Alert.Action>
		</Alert>
	)}

	<Alert color="success">
		<Alert.Icon>
			<CircleCheckIcon />
		</Alert.Icon>
		<Alert.Content>
			<Alert.Title>Domain verified</Alert.Title>
			<Alert.Description>acme.com now serves your workspace over HTTPS.</Alert.Description>
		</Alert.Content>
	</Alert>
</div>

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

npm add @sdxc/ui
import "@sdxc/ui/theme.css";

Usage

import { Alert } from "@sdxc/ui";

Renders an inline status panel tinted by Alert.Props.color, gaining inline-start padding through :has() when a direct Alert.Icon needs the room. role, aria-atomic, and aria-live stay overridable.

Composition

The parts the component publishes, each a static property of the host.

  • Alert
  • Alert.IconRenders Alert.IconProps.children as the alert's leading icon, absolutely positioned at the panel's inline/block-start corner with any direct SVG sized to 1rem.
  • Alert.ContentRenders Alert.ContentProps.children as the alert's body block: a column flex container that shrinks to fit alongside a leading icon or trailing action, holding Alert.Title and Alert.Description.
  • Alert.TitleRenders Alert.TitleProps.children as the alert's heading, inside the native heading element matching the nearest ancestor HeadingScope's depth — or <h1> where no scope wraps it — styled as a small, tight label.
  • Alert.DescriptionRenders Alert.DescriptionProps.children as the alert's supporting message, slightly translucent against the alert's foreground color so it reads as secondary to the title.
  • Alert.ActionRenders Alert.ActionProps.children as the alert's trailing control — a button, link, or dismiss affordance — pinned to the panel's block-start edge at its own natural size.

Examples

<Alert color="success" live="assertive">
  <Alert.Content>
    <Alert.Title>{t("alerts.saved.title")}</Alert.Title>
  </Alert.Content>
</Alert>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

PropTypeDescription
color?"brand" | "neutral" | "success" | "warning" | "danger"Semantic tone of the alert. Default: "neutral".
live?"polite" | "assertive" | "off"aria-live politeness for dynamic alerts. Default: "polite".
childrenRemixNodeThe alert's compound parts: Alert.Icon, Alert.Content, Alert.Action, or any other content.

Also accepts everything in TagProps<"div">.

Alert.Icon

PropTypeDescription
childrenRemixNodeThe icon graphic, typically a single SVG icon.

Also accepts everything in TagProps<"div">.

Alert.Content

PropTypeDescription
childrenRemixNodeThe alert's title, description, or other body content.

Also accepts everything in TagProps<"div">.

Alert.Title

PropTypeDescription
childrenRemixNodeThe alert's heading text.

Also accepts everything in TagProps<"h1">.

Alert.Description

PropTypeDescription
childrenRemixNodeThe alert's supporting message text.

Also accepts everything in TagProps<"p">.

Alert.Action

PropTypeDescription
childrenRemixNodeA button, link, or other control the alert offers, e.g. "Retry" or "Dismiss".

Also accepts everything in TagProps<"div">.

Types

NameValuesWhat it decides
Color"brand" | "neutral" | "success" | "warning" | "danger"Semantic tone driving the host element's border, tint, and foreground color through the --ui-* variables for that color.
Live"polite" | "assertive" | "off"aria-live politeness applied to the host element.