import { cva, type VariantProps } from 'class-variance-authority'; import { AlertTriangle, CheckCircle2, Info, XCircle } from 'lucide-react'; import { cn } from '@/lib/utils'; /** * DESIGN.md §6.5. * * The tinted surfaces stay light in dark mode and pin their text to ink, rather * than inverting. Inverting would mean a second set of tints for every tone, and * a pale-on-pale failure the first time one was missed. */ const bannerClasses = cva( 'flex gap-3 rounded-card border p-5 text-ink-950', { variants: { tone: { info: 'border-brand-200 bg-brand-100', success: 'border-go-100 bg-go-50', warning: 'border-sun-100 bg-sun-50', error: 'border-stop-100 bg-stop-50', }, }, defaultVariants: { tone: 'info' }, }, ); const TONE_ICON = { info: Info, success: CheckCircle2, warning: AlertTriangle, error: XCircle, } as const; const TONE_ICON_COLOR = { info: 'text-brand-500', success: 'text-go-600', warning: 'text-sun-500', error: 'text-stop-500', } as const; type BannerProps = VariantProps & { title?: string; children: React.ReactNode; className?: string; /** `status` for outcomes the user caused, `alert` for errors. */ role?: 'status' | 'alert'; }; export function Banner({ tone = 'info', title, children, className, role }: BannerProps) { const key = tone ?? 'info'; const Icon = TONE_ICON[key]; return (
{title &&

{title}

}
{children}
); } /** Inline form error. §6.2 — always announced. */ export function FormError({ children }: { children: React.ReactNode }) { return (

{children}

); }