Componentfeedback1.11 KB gzipZero dependencies

Callout

Tactile admission callout note with dashed borders, status tones and dismiss action.

Preview

Live Preview
INFOADMISSION NOTICE
Gate 4 opens at 19:30. Please present your digital stub at the turnstile.

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add callout

Or install the full npm package:

npm install bigbullui
import { Callout } from "@/components/ui/callout";

<Callout title="ADMISSION NOTICE" variant="info">Gate 4 opens at 19:30.</Callout>

Usage guidance

Use Callout when a ticket stub surface needs Tactile admission callout note with dashed borders, status tones and dismiss action. Keep props minimal and prefer composition with sibling stubs.

Avoid Callout for long form flows or dense data grids where a dedicated layout block fits better.

Source code

callout.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface CalloutProps extends React.HTMLAttributes<HTMLDivElement> {
  title?: string;
  variant?: "info" | "success" | "warning" | "danger" | "neutral";
  icon?: React.ReactNode;
  dismissible?: boolean;
  onDismiss?: () => void;
  notched?: boolean;
  className?: string;
  children: React.ReactNode;
}

const VARIANT_STYLES = {
  info: "border-foreground bg-card text-foreground",
  success: "border-success bg-success/10 text-success",
  warning: "border-warning bg-warning/10 text-warning",
  danger: "border-destructive bg-destructive/10 text-destructive",
  neutral: "border-border bg-secondary/50 text-foreground",
};

const VARIANT_BADGES = {
  info: "bg-foreground text-background",
  success: "bg-success text-white",
  warning: "bg-warning text-black",
  danger: "bg-destructive text-white",
  neutral: "bg-secondary text-foreground",
};

export function Callout({
  title,
  variant = "info",
  icon,
  dismissible = false,
  onDismiss,
  notched = true,
  className,
  children,
  ...props
}: CalloutProps) {
  const [dismissed, setDismissed] = React.useState(false);

  if (dismissed) return null;

  const handleDismiss = () => {
    setDismissed(true);
    onDismiss?.();
  };

  return (
    <div
      role="note"
      className={cn(
        "relative flex flex-col gap-2 rounded-lg border-2 border-dashed p-4 font-mono select-none shadow-xs",
        VARIANT_STYLES[variant],
        className
      )}
      {...props}
    >
      {notched && (
        <>
          <div
            aria-hidden="true"
            className="absolute -left-1.5 top-1/2 -translate-y-1/2 size-3 rounded-full border border-foreground bg-background"
          />
          <div
            aria-hidden="true"
            className="absolute -right-1.5 top-1/2 -translate-y-1/2 size-3 rounded-full border border-foreground bg-background"
          />
        </>
      )}

      {/* Header */}
      <div className="flex items-center justify-between gap-2">
        <div className="flex items-center gap-2">
          <span
            className={cn(
              "rounded px-1.5 py-0.5 text-[9px] font-black uppercase tracking-wider",
              VARIANT_BADGES[variant]
            )}
          >
            {variant.toUpperCase()}
          </span>
          {title && (
            <span className="text-xs font-bold uppercase tracking-wide text-foreground">
              {title}
            </span>
          )}
        </div>

        {dismissible && (
          <button
            type="button"
            onClick={handleDismiss}
            aria-label="Dismiss callout"
            className="cursor-pointer rounded border border-current px-1.5 py-0.2 text-[10px] font-bold opacity-70 hover:opacity-100 transition-opacity"
          >
            ✕
          </button>
        )}
      </div>

      {/* Content */}
      <div className="text-xs font-sans leading-relaxed text-muted-foreground">
        {children}
      </div>
    </div>
  );
}

Props

PropTypeDescription
titlestringOptional callout title
variant"info" | "success" | "warning" | "danger" | "neutral"Callout tone
dismissiblebooleanWhether user can close the callout
onDismiss() => voidCallback on dismiss