All components infosuccesswarningerrorneutralsmmdlg
Alert
Status banners for info, success, warning, and error with title, description, and indicator slots.
Heads up
Blocks inherit your theme automatically.
12345678910111213141516171819202122import { Alert } from "#components/ui/ark-ui/alert.tsx";
export function Example() { return ( // status/variant/size resolve once on Root; every part inherits them // via context (status also lands as a data-status attribute) <Alert.Root role="alert" status="info" variant="subtle" size="md"> <Alert.Indicator> <InfoIcon /> </Alert.Indicator> <Alert.Content> <Alert.Title>Heads up</Alert.Title> <Alert.Description> Blocks inherit your theme automatically. </Alert.Description> </Alert.Content> </Alert.Root> );}
// composition works per part — e.g. render the title as a link:// <Alert.Title asChild><a href="/changelog">Heads up</a></Alert.Title>Variants
Heads up
Blocks inherit your theme automatically.
Heads up
Blocks inherit your theme automatically.
Heads up
Blocks inherit your theme automatically.
Heads up
Blocks inherit your theme automatically.
Heads up
Blocks inherit your theme automatically.
Sizes
Heads up
Blocks inherit your theme automatically.
Heads up
Blocks inherit your theme automatically.
Heads up
Blocks inherit your theme automatically.
Install
1npx stylexblocks add alertSource
components/ui/ark-ui/alert.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207import * as stylex from "@stylexjs/stylex";import { createSlotRecipeContext } from "#lib/ark-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe } from "#lib/recipe-props.ts";import { colorPalette } from '@stylexblocks/ui/color-palette.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const alertRoot = stylex.create({ base: { width: sizes.full, display: 'flex', alignItems: 'flex-start', position: 'relative', },})
const alertRootSizes = stylex.create({ sm: { gap: spacing._2, paddingInline: spacing._3, paddingBlock: spacing._3, fontSize: fontSizes.xs, lineHeight: lineHeights.xs, borderRadius: semanticRadii.panel, }, md: { gap: spacing._3, paddingInline: spacing._4, paddingBlock: spacing._4, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, borderRadius: semanticRadii.panel, }, lg: { gap: spacing._3, paddingInline: spacing._4, paddingBlock: spacing._4, fontSize: fontSizes.md, lineHeight: lineHeights.md, borderRadius: semanticRadii.panel, },})
const alertRootVariants = stylex.create({ subtle: { backgroundColor: colorPalette.subtle, color: colorPalette.fg, }, surface: { backgroundColor: colorPalette.subtle, color: colorPalette.fg, boxShadow: 'inset 0 0 0px 1px var(--shadow-color)', '--shadow-color': colorPalette.solid, }, outline: { color: colorPalette.fg, boxShadow: 'inset 0 0 0px 1px var(--shadow-color)', '--shadow-color': colorPalette.subtle, }, solid: { backgroundColor: colorPalette.solid, color: colorPalette.contrast, },})
const alertRootStatus = stylex.create({ info: {}, warning: {}, success: {}, error: {}, neutral: {},})
const alertTitle = stylex.create({ base: { fontWeight: fontWeights.medium, },})
const alertDescription = stylex.create({ base: { display: 'inline', },})
const alertIndicator = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, width: '1em', height: '1em', },})
const alertIndicatorSizes = stylex.create({ sm: { fontSize: fontSizes.lg, lineHeight: lineHeights.lg, }, md: { fontSize: fontSizes.xl, lineHeight: lineHeights.xl, }, lg: { fontSize: fontSizes._2xl, lineHeight: lineHeights._2xl, },})
const alertIndicatorVariants = stylex.create({ surface: { color: colorPalette.fg, }, outline: { color: colorPalette.fg, }, solid: { color: colorPalette.contrast, },})
const alertContent = stylex.create({ base: { display: 'flex', flex: 1, gap: spacing._1, },})
const alertContentInlines = stylex.create({ true: { display: 'inline-flex', flexDirection: 'row', alignItems: 'center', }, false: { display: 'flex', flexDirection: 'column', },})
export type AlertStatus = keyof typeof alertRootStatus
export type AlertInline = keyof typeof alertContentInlines
export type AlertVariant = keyof typeof alertRootVariants
export type AlertSize = keyof typeof alertRootSizes
// defineSlotRecipe validates the shape at compile time: default values must// exist in the axis maps, and slot keys must be `styles` or a declared axis.// Axis maps are looked up by pluralized axis name, so the generated `status`// and `inline` maps are registered as `statuss`/`inlines`.const alertSlotRecipe = defineSlotRecipe({ slots: { root: { styles: alertRoot, sizes: alertRootSizes, variants: alertRootVariants, statuss: alertRootStatus, }, title: { styles: alertTitle }, description: { styles: alertDescription }, indicator: { styles: alertIndicator, sizes: alertIndicatorSizes }, content: { styles: alertContent, inlines: alertContentInlines }, }, defaultVariants: { status: "info", variant: "subtle", size: "md", inline: "false", },});
/** * Alert — recipe and component colocated in one file. `<Alert.Root status * variant size>` resolves the recipe once and every part inherits it through * context (status doubles as a data-status hook — pair it with a palette * theme for color); each part composes via `asChild`. */const { withProvider, withContext } = createSlotRecipeContext(alertSlotRecipe);
export const Alert = { // slot names resolve mechanically from the recipe maps above Root: withProvider("div", "root", "AlertRoot"), Title: withContext("div", "title", "AlertTitle"), Description: withContext("div", "description", "AlertDescription"), Content: withContext("div", "content", "AlertContent"), // explicit resolver: the indicator's variant map is sparse (no `subtle`) — // TypeScript errors here the moment a styled variant leaves the map above Indicator: withContext( "span", (v) => [ alertIndicator.base, alertIndicatorSizes[v.size], v.variant !== "subtle" && alertIndicatorVariants[v.variant], ], "AlertIndicator", ),};