Feedback

Callout

An inline highlight box for docs, forms and explanatory copy — info, success, warning and danger variants with a tinted accent bar and default icon per variant.

Preview in your theme

Loading preview…

import * as React from "react"
import { CircleCheck, Info, OctagonAlert, TriangleAlert } from "lucide-react"
import { cn } from "@/lib/utils"

export type CalloutVariant = "info" | "success" | "warning" | "danger"

/**
 * variant → color, kept as one editable data table. Every value is a theme
 * CSS variable so the host palette (and dark mode) decides the actual color —
 * remap freely per project.
 */
const VARIANT_COLORS: Record<CalloutVariant, string> = {
  info: "var(--primary)",
  success: "var(--chart-2)",

Installation

npx shadcn@latest add https://ui.zyeon.ai/r/callout.json

Prompt

The prompt behind this component — paste it into your AI assistant to recreate or adapt it.

Build a React + TypeScript + Tailwind "Callout" component using lucide-react
icons (no other runtime dependencies).

Contract
- Export a forwardRef div extending React.HTMLAttributes<HTMLDivElement>;
  spread remaining props on the root and merge className via cn().
- Props: variant = "info" | "success" | "warning" | "danger" (default "info");
  title?: string; icon?: ReactNode (per-variant defaults: Info / CircleCheck /
  TriangleAlert / OctagonAlert; pass null to render no icon); children is the
  body copy.

Behavior
- Purely presentational — no state, no effects, no "use client" directive.
- role follows the variant: "danger" gets role="alert" (assertive, announced
  immediately); every other variant gets role="note" (ancillary content, no
  live-region behavior) since a callout is read in place, not announced as an
  interruption.
- The icon renders only when icon !== null: icon === undefined falls back to
  the variant's default glyph, any other value overrides it.

Rendering & styling
- Semantic tokens only, no hardcoded colors. A variant → color lookup maps to
  CSS variables: info = var(--primary), success = var(--chart-2),
  warning = var(--chart-4), danger = var(--destructive).
- The color is applied via inline style + color-mix (not a fixed Tailwind
  palette class) so the accent stays correct if a project remaps its tokens:
  a 3px border-inline-start in the variant color, a background of
  color-mix(in oklab, <color> 8%, transparent), and the icon/title tinted
  with the same color. Body copy stays text-foreground/90 for readable
  contrast against the tinted background in both themes.
- cn() merges the consumer className; no animation, so no reduced-motion
  branch is needed.

Customization levers
- Add a variant: one VARIANT_COLORS entry (any CSS color token) plus one
  DEFAULT_ICONS entry — layout and roles are untouched.
- Border style: swap the inline border-inline-start for a full border
  (border + border-color via the same color-mix value) for a boxed-admonition
  look instead of the accent-bar look.
- Density: px-4 py-3 text-sm is the only sizing; pass py-2 text-xs via
  className for a compact strip inside tight forms, or px-5 py-4 for a
  docs-page callout with more breathing room.
- Drop the icon with icon={null}, or pass any lucide-react (or custom) node
  to brand the callout.
- Omit title for a plain single-paragraph note; pass title for a labeled
  admonition (Note/Tip/Warning/Danger-style headings).

Concepts

  • Variant-driven accent color — a single CSS variable per variant feeds the border, background tint and icon/title color together, so remapping a project's theme tokens re-colors every callout for free.
  • Inline notice, not a page-level one — a callout lives inside the reading flow (a docs paragraph, a form's helper text) instead of pinning to the top of the page like a Banner.
  • Semantic live-region roledanger is role="alert" (interrupts screen readers immediately); the rest are role="note" (ancillary, read in place, not urgent) — urgency is an accessibility semantic, not just a color.
  • Default icon per variant — Info / CircleCheck / TriangleAlert / OctagonAlert ship out of the box; the icon prop overrides it and icon={null} removes it, keeping the common case zero-config.
  • Tinted background via color-mix — an 8% mix keeps the panel legible on both light and dark backgrounds without maintaining a second dark-mode color table.

On This Page