Banner
Inline status message pairing a Lucide icon with tone (icon + colour, never colour alone).
import { Banner }from "@/components/ui"
Specification
Shows an inline, page-spanning status message pairing a Lucide icon with a tone and an optional action.
Props (5)
Every prop this component accepts, its type, default, and how to use it.
| Prop | Type | Default | Description |
|---|---|---|---|
| tone* | "amber" | "rose" | "neutral" | — | Sets the semantic color of the banner (never rely on color alone — always pair with an icon). |
| icon* | LucideIcon | — | Leading icon that reinforces the message's meaning. |
| children* | ReactNode | — | The banner message content. |
| action | string | — | Label for an optional inline action button. |
| onAction | () => void | — | Handler invoked when the action button is clicked. |
Tones with action
3 invoices need your approval.
Showing results from the last 30 days.
Full source
The complete component, read from
src/components/ui/banner.tsx.banner.tsx
"use client";
import { type ReactNode } from "react";
import type { LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
type BannerTone = "amber" | "rose" | "neutral";
interface BannerProps {
tone: BannerTone;
icon: LucideIcon;
children: ReactNode;
action?: string;
onAction?: () => void;
}
const TONE_CLASS: Record<BannerTone, string> = {
amber: "bg-warning-light text-warning",
rose: "bg-error-light text-error",
neutral: "bg-bg-secondary text-text-secondary",
};
export function Banner({ tone, icon: Icon, children, action, onAction }: BannerProps) {
return (
<div
role="status"
className={cn(
"flex flex-wrap items-center gap-2 border-b border-border px-4 py-2.5 text-sm",
TONE_CLASS[tone]
)}
>
<Icon size={15} className="shrink-0" aria-hidden />
<span className="flex-1">{children}</span>
{action && (
<button
onClick={onAction}
className="rounded px-2 py-0.5 text-sm font-medium underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current"
>
{action}
</button>
)}
</div>
);
}