Alert
Inline message in the page flow. Not dismissed by time; use for state the user should notice before they act.
import { Alert, AlertAction, AlertDescription, AlertTitle,} from "@/components/ui/alert";import { Button } from "@/components/ui/button";
const icon = (d: string) => function Icon() { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" > <path d={d} /> </svg> ); };const InfoIcon = icon( "M12 8h.01M11 12h1v4h1M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0",);const CircleCheckIcon = icon( "m8 12 3 3 5-6M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0",);const TriangleAlertIcon = icon( "M12 9v4M12 17h.01M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0",);
export default function Demo() { return ( <div className="grid w-full max-w-xl gap-3"> <Alert> <InfoIcon /> <AlertTitle>Devnet only</AlertTitle> <AlertDescription> Prices come from a mock oracle. Nothing here is real money. </AlertDescription> </Alert> <Alert variant="destructive"> <TriangleAlertIcon /> <AlertTitle>Transaction failed</AlertTitle> <AlertDescription> Slippage was above 1%. Try a smaller order. </AlertDescription> </Alert> <Alert variant="success"> <CircleCheckIcon /> <AlertTitle>Index launched</AlertTitle> <AlertDescription>MAG4 is live and accepting joins.</AlertDescription> <AlertAction> <Button size="xs" variant="outline"> View </Button> </AlertAction> </Alert> <Alert variant="brand"> <InfoIcon /> <AlertTitle>Autopilot is on</AlertTitle> <AlertDescription> Rebalances run automatically with the theme accent. </AlertDescription> </Alert> <Alert variant="info"> <InfoIcon /> <AlertTitle>Oracle updated</AlertTitle> <AlertDescription>Prices refresh every 15 seconds.</AlertDescription> </Alert> <Alert variant="warning"> <TriangleAlertIcon /> <AlertTitle>Drift is 6.2%</AlertTitle> <AlertDescription> Above the 5% limit. The keeper will rebalance at the next run. </AlertDescription> </Alert> </div> );}<script setup lang="ts">import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/ui/alert";import { Button } from "@/components/ui/button";
const info = "M12 8h.01M11 12h1v4h1M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0";const check = "m8 12 3 3 5-6M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0";const warn = "M12 9v4M12 17h.01M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0";</script>
<template> <div class="grid w-full max-w-xl gap-3"> <Alert> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path :d="info" /></svg> <AlertTitle>Devnet only</AlertTitle> <AlertDescription>Prices come from a mock oracle. Nothing here is real money.</AlertDescription> </Alert> <Alert variant="destructive"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path :d="warn" /></svg> <AlertTitle>Transaction failed</AlertTitle> <AlertDescription>Slippage was above 1%. Try a smaller order.</AlertDescription> </Alert> <Alert variant="success"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path :d="check" /></svg> <AlertTitle>Index launched</AlertTitle> <AlertDescription>MAG4 is live and accepting joins.</AlertDescription> <AlertAction> <Button size="xs" variant="outline">View</Button> </AlertAction> </Alert> <Alert variant="brand"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path :d="info" /></svg> <AlertTitle>Autopilot is on</AlertTitle> <AlertDescription>Rebalances run automatically with the theme accent.</AlertDescription> </Alert> <Alert variant="info"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path :d="info" /></svg> <AlertTitle>Oracle updated</AlertTitle> <AlertDescription>Prices refresh every 15 seconds.</AlertDescription> </Alert> <Alert variant="warning"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path :d="warn" /></svg> <AlertTitle>Drift is 6.2%</AlertTitle> <AlertDescription>Above the 5% limit. The keeper will rebalance at the next run.</AlertDescription> </Alert> </div></template><script lang="ts"> import * as Alert from "$lib/components/ui/alert"; import { Button } from "$lib/components/ui/button";
const info = "M12 8h.01M11 12h1v4h1M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0"; const check = "m8 12 3 3 5-6M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0"; const warn = "M12 9v4M12 17h.01M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0";</script>
{#snippet icon(d: string)} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path {d} /></svg>{/snippet}
<div class="grid w-full max-w-xl gap-3"> <Alert.Root> {@render icon(info)} <Alert.Title>Devnet only</Alert.Title> <Alert.Description>Prices come from a mock oracle. Nothing here is real money.</Alert.Description> </Alert.Root> <Alert.Root variant="destructive"> {@render icon(warn)} <Alert.Title>Transaction failed</Alert.Title> <Alert.Description>Slippage was above 1%. Try a smaller order.</Alert.Description> </Alert.Root> <Alert.Root variant="success"> {@render icon(check)} <Alert.Title>Index launched</Alert.Title> <Alert.Description>MAG4 is live and accepting joins.</Alert.Description> <Alert.Action> <Button size="xs" variant="outline">View</Button> </Alert.Action> </Alert.Root> <Alert.Root variant="brand"> {@render icon(info)} <Alert.Title>Autopilot is on</Alert.Title> <Alert.Description>Rebalances run automatically with the theme accent.</Alert.Description> </Alert.Root> <Alert.Root variant="info"> {@render icon(info)} <Alert.Title>Oracle updated</Alert.Title> <Alert.Description>Prices refresh every 15 seconds.</Alert.Description> </Alert.Root> <Alert.Root variant="warning"> {@render icon(warn)} <Alert.Title>Drift is 6.2%</Alert.Title> <Alert.Description>Above the 5% limit. The keeper will rebalance at the next run.</Alert.Description> </Alert.Root></div>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/alertpnpm dlx shadcn@latest add @edmi-ui/alertyarn dlx shadcn@latest add @edmi-ui/alertbunx shadcn@latest add @edmi-ui/alertPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install class-variance-authorityTerminal window pnpm add class-variance-authorityTerminal window yarn add class-variance-authorityTerminal window bun add class-variance-authorityCopy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/alertpnpm dlx shadcn-vue@latest add @edmi-ui/alertyarn dlx shadcn-vue@latest add @edmi-ui/alertbunx shadcn-vue@latest add @edmi-ui/alertPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install class-variance-authorityTerminal window pnpm add class-variance-authorityTerminal window yarn add class-variance-authorityTerminal window bun add class-variance-authorityCopy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/alert.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Copy the code from Component source into your project and update the import paths to match your aliases.
import { Alert, AlertAction, AlertDescription, AlertTitle,} from "@/components/ui/alert";import { Button } from "@/components/ui/button";
<div className="grid w-full max-w-xl gap-3"> <Alert> <InfoIcon /> <AlertTitle>Devnet only</AlertTitle> <AlertDescription> Prices come from a mock oracle. Nothing here is real money. </AlertDescription> </Alert> <Alert variant="destructive"> <TriangleAlertIcon /> <AlertTitle>Transaction failed</AlertTitle> <AlertDescription> Slippage was above 1%. Try a smaller order. </AlertDescription> </Alert> <Alert variant="success"> <CircleCheckIcon /> <AlertTitle>Index launched</AlertTitle> <AlertDescription>MAG4 is live and accepting joins.</AlertDescription> <AlertAction> <Button size="xs" variant="outline"> View </Button> </AlertAction> </Alert> {/* … */}</div><script setup lang="ts">import { Alert, AlertAction, AlertDescription, AlertTitle,} from "@/components/ui/alert";import { Button } from "@/components/ui/button";</script>
<template> <div class="grid w-full max-w-xl gap-3"> <Alert> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path /></svg> <AlertTitle>Devnet only</AlertTitle> <AlertDescription>Prices come from a mock oracle. Nothing here is real money.</AlertDescription> </Alert> <Alert variant="destructive"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path /></svg> <AlertTitle>Transaction failed</AlertTitle> <AlertDescription>Slippage was above 1%. Try a smaller order.</AlertDescription> </Alert> <Alert variant="success"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path /></svg> <AlertTitle>Index launched</AlertTitle> <AlertDescription>MAG4 is live and accepting joins.</AlertDescription> <AlertAction> <Button size="xs" variant="outline">View</Button> </AlertAction> </Alert> <Alert variant="brand"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path /></svg> <AlertTitle>Autopilot is on</AlertTitle> <AlertDescription>Rebalances run automatically with the theme accent.</AlertDescription> </Alert> <!-- … --> </div></template><script lang="ts"> import * as Alert from "$lib/components/ui/alert"; import { Button } from "$lib/components/ui/button";</script>
<div class="grid w-full max-w-xl gap-3"> <Alert.Root> <Alert.Title>Devnet only</Alert.Title> <Alert.Description>Prices come from a mock oracle. Nothing here is real money.</Alert.Description> </Alert.Root> <Alert.Root variant="destructive"> <Alert.Title>Transaction failed</Alert.Title> <Alert.Description>Slippage was above 1%. Try a smaller order.</Alert.Description> </Alert.Root> <Alert.Root variant="success"> <Alert.Title>Index launched</Alert.Title> <Alert.Description>MAG4 is live and accepting joins.</Alert.Description> <Alert.Action> <Button size="xs" variant="outline">View</Button> </Alert.Action> </Alert.Root> <Alert.Root variant="brand"> <Alert.Title>Autopilot is on</Alert.Title> <Alert.Description>Rebalances run automatically with the theme accent.</Alert.Description> </Alert.Root> <Alert.Root variant="info"> <Alert.Title>Oracle updated</Alert.Title> <Alert.Description>Prices refresh every 15 seconds.</Alert.Description> </Alert.Root> <!-- … --></div>Component source
Section titled “Component source”import { cva, type VariantProps } from "class-variance-authority";import { cn } from "cn";import type * as React from "react";
// Soft fill + tinted 30-40% border for coloured variants, never solid (DESIGN §4.12).const alertVariants = cva( "group/alert relative grid w-full grid-cols-[1fr_auto] items-start gap-x-3 gap-y-0.5 rounded-xl border px-4 py-3.5 text-left text-sm has-[>svg]:grid-cols-[20px_1fr_auto] *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", { variants: { variant: { default: "border-border bg-card text-card-foreground", destructive: "border-[color-mix(in_srgb,var(--destructive)_40%,var(--popover))] bg-destructive-soft text-destructive-text", brand: "border-[color-mix(in_srgb,var(--brand)_40%,var(--popover))] bg-brand-soft text-brand-text", // ✦ success: "border-[color-mix(in_srgb,var(--success)_40%,var(--popover))] bg-success-soft text-success-text", // ✦ warning: "border-[color-mix(in_srgb,var(--warning)_40%,var(--popover))] bg-warning-soft text-warning-text", // ✦ info: "border-[color-mix(in_srgb,var(--info)_40%,var(--popover))] bg-info-soft text-info-text", // ✦ }, }, defaultVariants: { variant: "default", }, },);
function Alert({ className, variant, ...props}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) { return ( <div data-slot="alert" data-variant={variant ?? "default"} role="alert" className={cn(alertVariants({ variant }), className)} {...props} /> );}
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="alert-title" className={cn( "col-start-1 font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3", className, )} {...props} /> );}
function AlertDescription({ className, ...props}: React.ComponentProps<"div">) { return ( <div data-slot="alert-description" className={cn( "col-start-1 text-[13px] text-balance text-muted-foreground group-has-[>svg]/alert:col-start-2 md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4", "group-data-[variant=destructive]/alert:text-destructive-text/80", className, )} {...props} /> );}
function AlertAction({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="alert-action" className={cn( "col-start-2 row-span-2 row-start-1 self-center group-has-[>svg]/alert:col-start-3", className, )} {...props} /> );}
export { Alert, AlertAction, AlertDescription, AlertTitle };<script setup lang="ts">import type { HTMLAttributes } from 'vue'import type { AlertVariants } from '.'import { cn } from '@/registry/edmi/lib/utils'import { alertVariants } from '.'
const props = defineProps<{ class?: HTMLAttributes['class'] variant?: AlertVariants['variant']}>()</script>
<template> <div data-slot="alert" :data-variant="variant ?? 'default'" role="alert" :class="cn(alertVariants({ variant }), props.class)" > <slot /> </div></template><script setup lang="ts">import type { HTMLAttributes } from 'vue'import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{ class?: HTMLAttributes['class']}>()</script>
<template> <div data-slot="alert-action" :class="cn('col-start-2 row-span-2 row-start-1 self-center group-has-[>svg]/alert:col-start-3', props.class)" > <slot /> </div></template><script setup lang="ts">import type { HTMLAttributes } from 'vue'import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{ class?: HTMLAttributes['class']}>()</script>
<template> <div data-slot="alert-description" :class="cn('col-start-1 text-[13px] text-balance text-muted-foreground group-has-[>svg]/alert:col-start-2 md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4 group-data-[variant=destructive]/alert:text-destructive-text/80', props.class)" > <slot /> </div></template><script setup lang="ts">import type { HTMLAttributes } from 'vue'import { cn } from '@/registry/edmi/lib/utils'
const props = defineProps<{ class?: HTMLAttributes['class']}>()</script>
<template> <div data-slot="alert-title" :class="cn('col-start-1 font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3', props.class)" > <slot /> </div></template>import type { VariantProps } from "class-variance-authority";import { cva } from "class-variance-authority";
export { default as Alert } from "./Alert.vue";export { default as AlertAction } from "./AlertAction.vue";export { default as AlertDescription } from "./AlertDescription.vue";export { default as AlertTitle } from "./AlertTitle.vue";
// Soft fill + tinted 30-40% border for coloured variants, never solid (DESIGN 4.12).export const alertVariants = cva( "group/alert relative grid w-full grid-cols-[1fr_auto] items-start gap-x-3 gap-y-0.5 rounded-xl border px-4 py-3.5 text-left text-sm has-[>svg]:grid-cols-[20px_1fr_auto] *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", { variants: { variant: { default: "border-border bg-card text-card-foreground", destructive: "border-[color-mix(in_srgb,var(--destructive)_40%,var(--popover))] bg-destructive-soft text-destructive-text", brand: "border-[color-mix(in_srgb,var(--brand)_40%,var(--popover))] bg-brand-soft text-brand-text", // ✦ success: "border-[color-mix(in_srgb,var(--success)_40%,var(--popover))] bg-success-soft text-success-text", // ✦ warning: "border-[color-mix(in_srgb,var(--warning)_40%,var(--popover))] bg-warning-soft text-warning-text", // ✦ info: "border-[color-mix(in_srgb,var(--info)_40%,var(--popover))] bg-info-soft text-info-text", // ✦ }, }, defaultVariants: { variant: "default", }, },);
export type AlertVariants = VariantProps<typeof alertVariants>;<script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();</script>
<div bind:this={ref} data-slot="alert-action" class={cn("col-start-2 row-span-2 row-start-1 self-center group-has-[>svg]/alert:col-start-3", className)} {...restProps}> {@render children?.()}</div><script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();</script>
<div bind:this={ref} data-slot="alert-description" class={cn("col-start-1 text-[13px] text-balance text-muted-foreground group-has-[>svg]/alert:col-start-2 md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4 group-data-[variant=destructive]/alert:text-destructive-text/80", className)} {...restProps}> {@render children?.()}</div><script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();</script>
<div bind:this={ref} data-slot="alert-title" class={cn("col-start-1 font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3", className)} {...restProps}> {@render children?.()}</div><script lang="ts" module> import { type VariantProps, tv } from "tailwind-variants";
// Soft fill + tinted 30-40% border for coloured variants, never solid (DESIGN 4.12). export const alertVariants = tv({ base: "group/alert relative grid w-full grid-cols-[1fr_auto] items-start gap-x-3 gap-y-0.5 rounded-xl border px-4 py-3.5 text-left text-sm has-[>svg]:grid-cols-[20px_1fr_auto] *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", variants: { variant: { default: "border-border bg-card text-card-foreground", destructive: "border-[color-mix(in_srgb,var(--destructive)_40%,var(--popover))] bg-destructive-soft text-destructive-text", brand: "border-[color-mix(in_srgb,var(--brand)_40%,var(--popover))] bg-brand-soft text-brand-text", // ✦ success: "border-[color-mix(in_srgb,var(--success)_40%,var(--popover))] bg-success-soft text-success-text", // ✦ warning: "border-[color-mix(in_srgb,var(--warning)_40%,var(--popover))] bg-warning-soft text-warning-text", // ✦ info: "border-[color-mix(in_srgb,var(--info)_40%,var(--popover))] bg-info-soft text-info-text", // ✦ }, }, defaultVariants: { variant: "default", }, });
export type AlertVariant = VariantProps<typeof alertVariants>["variant"];</script>
<script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { ref = $bindable(null), class: className, variant = "default", children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { variant?: AlertVariant; } = $props();</script>
<div bind:this={ref} data-slot="alert" data-variant={variant} role="alert" class={cn(alertVariants({ variant }), className)} {...restProps}> {@render children?.()}</div>import Root from "./alert.svelte";import Action from "./alert-action.svelte";import Description from "./alert-description.svelte";import Title from "./alert-title.svelte";
export { type AlertVariant, alertVariants } from "./alert.svelte";
export { Action, Action as AlertAction, Description, Description as AlertDescription, Root, // Root as Alert, Title, Title as AlertTitle,};| Prop | Type | Default |
|---|---|---|
Alert variant |
default | destructive | brand | success | warning | info |
default |
AlertAction |
slot, placed in the last column | none |
Parts: Alert, AlertTitle, AlertDescription, AlertAction. A direct svg child becomes the leading icon.
✦ Edmi additions
Section titled “✦ Edmi additions”brand,success,warning,infovariants: soft fill with a tinted 40% border (DESIGN §4.12), never solid.successis always green (--success-soft/--success-text);brandfollows the theme accent.