Componentfeedback1.1 KB gzipZero dependencies
Maintenance Banner
Maintenance notification bar with countdown chip and dismiss.
Preview
Live Preview
Scheduled maintenance in progress. Services may experience brief interruptions.
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add maintenance-bannerOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { MaintenanceBanner } from "@/components/ui/maintenance-banner";
<MaintenanceBanner />Usage guidance
Use Maintenance Banner when a ticket stub surface needs Maintenance notification bar with countdown chip and dismiss. Keep props minimal and prefer composition with sibling stubs.
Avoid Maintenance Banner for long form flows or dense data grids where a dedicated layout block fits better.
Source code
maintenance-banner.tsxZero 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 MaintenanceBannerProps extends React.HTMLAttributes<HTMLDivElement> {
message?: string;
/** Minutes remaining until maintenance window starts */
startsInMinutes?: number;
onDismiss?: () => void;
}
/** Scheduled maintenance banner: announcement + countdown + dismiss. */
export function MaintenanceBanner({ message = "Scheduled maintenance in progress. Services may experience brief interruptions.", startsInMinutes, onDismiss, className, ...props }: MaintenanceBannerProps) {
const [dismissed, setDismissed] = React.useState(false);
const [mins, setMins] = React.useState(startsInMinutes ?? 0);
const hasCountdown = startsInMinutes !== undefined && startsInMinutes > 0;
React.useEffect(() => {
if (!hasCountdown || mins <= 0) return;
const t = setInterval(() => setMins((m) => Math.max(0, m - 1)), 60000);
return () => clearInterval(t);
}, [hasCountdown, mins]);
if (dismissed) return null;
return (
<div className={cn("relative flex items-center gap-3 border-b border-warning/40 bg-warning/10 px-4 py-2", className)} role="status" {...props}>
<span className="flex size-5 shrink-0 items-center justify-center rounded-full bg-warning/20 text-warning animate-pulse motion-reduce:animate-none" aria-hidden="true">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 3" /></svg>
</span>
<p className="min-w-0 flex-1 truncate text-xs text-foreground">{message}</p>
{hasCountdown && mins > 0 && (
<span className="shrink-0 rounded-full border border-warning/50 bg-background px-2 py-0.5 font-mono text-[10px] tabular-nums text-warning">
~{mins} min
</span>
)}
<button
onClick={() => { setDismissed(true); onDismiss?.(); }}
className="shrink-0 rounded-sm p-0.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring motion-reduce:transition-none"
aria-label="Dismiss announcement"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12" /></svg>
</button>
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| message | string | |
| startsInMinutes | number | Minutes remaining until maintenance begins |
| onDismiss | () => void |