Wuko
Components

Alert

An inline message block with info, success, warning, and danger variants. Each variant ships its own icon, role-token color, and ARIA role appropriate to the urgency. Optional title and close button.

Installation

terminal
npx shadcn@latest add @wuko/alert

Variants

Four variants cover the typical message hierarchy. info and success render with role="status" (polite, queued by the screen reader). warning and danger render with role="alert" (assertive, interrupts the current announcement).

A new version of Wuko is available.
Your changes have been saved.
components/alert-variants.tsx
import { Alert } from "@/components/ui/alert";

export function AlertVariants() {
  return (
    <div className="space-y-3">
      <Alert variant="info">A new version of Wuko is available.</Alert>
      <Alert variant="success">Your changes have been saved.</Alert>
      <Alert variant="warning">Your session expires in 2 minutes.</Alert>
      <Alert variant="danger">Could not connect to the server.</Alert>
    </div>
  );
}

With title

Pass titleto render a bolded heading above the body. The title takes the variant's foreground color so it pops against the tinted background.

Heads up
We just shipped two new components: Alert and Tabs.
tsx
<Alert variant="info" title="Heads up">
  We just shipped two new components — Alert and Tabs.
</Alert>
<Alert variant="danger" title="Build failed">
  3 of 12 tests failed. Check the logs for details.
</Alert>

Dismissible

Pass an onClose callback to render a close button at the top-right corner. The Alert does not own the dismissed state. Wire onClose to your own useState so the consumer controls visibility.

components/dismissible-alert.tsx
"use client";

import { useState } from "react";

import { Alert } from "@/components/ui/alert";

export function DismissibleAlert() {
  const [open, setOpen] = useState(true);
  if (!open) return null;
  return (
    <Alert
      variant="warning"
      title="Unsaved changes"
      onClose={() => setOpen(false)}
    >
      You have edits that haven't been saved. Save before navigating away.
    </Alert>
  );
}

Props

PropTypeDefaultDescription
variant"info" | "success" | "warning" | "danger""info"Selects the icon, color, and ARIA role. info and success render with role="status" (polite); warning and danger render with role="alert" (assertive).
titleReactNodeOptional bolded heading rendered above the body. Tinted with the variant's foreground color.
onClose() => voidWhen provided, renders a close button at the top-right corner with aria-label="Close". The Alert does not own the dismissed state. Wire onClose to your own useState so the consumer controls visibility.
childrenReactNodeThe body content of the alert.
classNamestringTailwind classes merged onto the Alert root div.
...restHTMLAttributes<HTMLDivElement>Forwarded to the underlying div. Excludes title (redefined as ReactNode) and role (controlled internally by variant).

Accessibility

  • ARIA role is variant-driven, not consumer-overridable. info and success use role="status" (polite, appended to the screen-reader queue without interrupting). warning and danger use role="alert" (assertive, interrupts the current announcement).
  • Variant icons are aria-hidden="true". They are decorative duplicates of the role-driven semantic. Screen readers announce the role; sighted users see the icon.
  • The close button gets aria-label="Close"and a focus ring matching Button's convention (focus-visible:outline). It uses type="button" so it does not submit forms.
  • All variants pass WCAG AA for both title and body text on the tinted surface in dark and light themes: lowest contrast 4.57:1 for info-light and success-light titles.