Full-width top bar alerts with gradient backgrounds — add to your site in seconds
Copy URL or Code
Paste to your AI coding assistant and customize:
Done. Your AI handles the rest.
Fully customizable. Features Slide Top (instant reveal) and Expanding Loader (progress bar animation) effects. Includes beautiful gradient backgrounds and type-specific icons.
/*
================================================================================
AI COMPONENT: TopBarNotification
================================================================================
SETUP:
1. Create file: components/TopBarNotification.tsx
2. Copy this entire code block into that file
3. Import: import TopBarNotification from "@/components/TopBarNotification"
4. No external dependencies required - pure React + CSS
================================================================================
QUICK CUSTOMIZATION (via props)
================================================================================
| Prop | Type | Default |
|------------|-------------------------------------------|-------------|
| message | string (supports HTML) | required |
| effect | "slidetop" | "exploader" | "slidetop" |
| type | "notice" | "warning" | "error" | "success"| "notice" |
| ttl | number (milliseconds, 0 = no auto-close) | 6000 |
| icon | React.ReactNode | type-based |
| isVisible | boolean | required |
| onDismiss | () => void | required |
================================================================================
ANIMATION EFFECTS
================================================================================
| Effect | Description |
|-----------|------------------------------------------------|
| slidetop | Slides down from top with smooth easing |
| exploader | Progress bar expands, then bar slides in |
================================================================================
GRADIENT BACKGROUNDS (by type)
================================================================================
| Type | Gradient |
|---------|-------------------------------------------------|
| notice | Purple (#667eea → #764ba2) |
| warning | Pink-Red (#f093fb → #f5576c) |
| error | Red-Orange (#ff416c → #ff4b2b) |
| success | Teal-Green (#11998e → #38ef7d) |
================================================================================
SOURCE CODE
================================================================================
*/
"use client";
import React, { useState, useEffect, useCallback, useRef } from "react";
export type BarEffect = "slidetop" | "exploader";
export type NotificationType = "notice" | "warning" | "error" | "success";
export interface TopBarNotificationProps {
message: string;
effect?: BarEffect;
type?: NotificationType;
ttl?: number;
onClose?: () => void;
isVisible: boolean;
onDismiss: () => void;
icon?: React.ReactNode;
}
const typeColors = {
notice: { bg: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)", text: "#fff", iconBg: "rgba(255,255,255,0.2)" },
warning: { bg: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)", text: "#fff", iconBg: "rgba(255,255,255,0.2)" },
error: { bg: "linear-gradient(135deg, #ff416c 0%, #ff4b2b 100%)", text: "#fff", iconBg: "rgba(255,255,255,0.2)" },
success: { bg: "linear-gradient(135deg, #11998e 0%, #38ef7d 100%)", text: "#fff", iconBg: "rgba(255,255,255,0.2)" },
};
const defaultIcons = {
notice: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>,
warning: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>,
error: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>,
success: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>,
};
export default function TopBarNotification({
message, effect = "slidetop", type = "notice", ttl = 6000, onClose, isVisible, onDismiss, icon,
}: TopBarNotificationProps) {
const [show, setShow] = useState(false);
const [hide, setHide] = useState(false);
const [loaderProgress, setLoaderProgress] = useState(0);
const timerRef = useRef(null);
const loaderRef = useRef(null);
const colors = typeColors[type];
const displayIcon = icon || defaultIcons[type];
useEffect(() => {
if (isVisible) {
setHide(false);
setLoaderProgress(0);
if (effect === "exploader") {
const startTime = Date.now();
const duration = 800;
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = 1 - Math.pow(1 - progress, 3);
setLoaderProgress(eased * 100);
if (progress < 1) loaderRef.current = setTimeout(animate, 16);
else setShow(true);
};
loaderRef.current = setTimeout(animate, 16);
} else {
requestAnimationFrame(() => setShow(true));
}
if (ttl > 0) timerRef.current = setTimeout(() => handleDismiss(), ttl + (effect === "exploader" ? 800 : 0));
}
return () => { if (timerRef.current) clearTimeout(timerRef.current); if (loaderRef.current) clearTimeout(loaderRef.current); };
}, [isVisible, ttl, effect]);
const handleDismiss = useCallback(() => {
if (timerRef.current) clearTimeout(timerRef.current);
if (loaderRef.current) clearTimeout(loaderRef.current);
setHide(true);
setTimeout(() => { setShow(false); setLoaderProgress(0); onDismiss(); onClose?.(); }, 400);
}, [onDismiss, onClose]);
if (!isVisible && !show && loaderProgress === 0) return null;
return (
<>
<style>{`
.top-bar {
position: fixed; top: 0; left: 0; width: 100%;
background: ${colors.bg};
padding: 16px 50px 16px 20px;
z-index: 1000; color: ${colors.text};
font-size: 14px; font-family: 'Helvetica Neue', sans-serif;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
display: flex; align-items: center; gap: 12px;
opacity: 0; transform: translateY(-100%);
}
.top-bar p { margin: 0; line-height: 1.3; flex: 1; }
.top-bar a { color: inherit; font-weight: 600; text-decoration: underline; }
.bar-icon { width: 36px; height: 36px; border-radius: 50%; background: ${colors.iconBg}; display: flex; align-items: center; justify-content: center; }
.bar-close { width: 24px; height: 24px; position: absolute; right: 16px; top: 50%; transform: translateY(-50%); cursor: pointer; opacity: 0.7; }
.bar-close:hover { opacity: 1; }
.bar-close::before, .bar-close::after { content: ''; position: absolute; width: 2px; height: 14px; top: 50%; left: 50%; background: ${colors.text}; }
.bar-close::before { transform: translate(-50%,-50%) rotate(45deg); }
.bar-close::after { transform: translate(-50%,-50%) rotate(-45deg); }
.ns-effect-slidetop { transition: transform 0.4s cubic-bezier(0.4,0,0.2,1), opacity 0.3s; }
.ns-effect-slidetop.ns-show { transform: translateY(0); opacity: 1; }
.ns-effect-slidetop.ns-hide { transform: translateY(-100%); opacity: 0; }
.loader-bar { position: fixed; top: 0; left: 0; height: 4px; background: ${colors.bg}; z-index: 1001; }
.ns-effect-exploader { transition: transform 0.4s cubic-bezier(0.4,0,0.2,1), opacity 0.3s; }
.ns-effect-exploader.ns-show { transform: translateY(0); opacity: 1; }
.ns-effect-exploader.ns-hide { transform: translateY(-100%); opacity: 0; }
`}</style>
{effect === "exploader" && loaderProgress > 0 && loaderProgress < 100 && (
<div className="loader-bar" style={{ width: `${loaderProgress}%` }} />
)}
<div className={`top-bar ns-effect-${effect} ${show ? "ns-show" : ""} ${hide ? "ns-hide" : ""}`}>
<div className="bar-icon">{displayIcon}</div>
<p dangerouslySetInnerHTML={{ __html: message }} />
<div className="bar-close" onClick={handleDismiss} />
</div>
</>
);
}import { useState, useCallback, useRef } from "react";
import TopBarNotification from "@/components/TopBarNotification";
export default function NotificationDemo() {
const [notification, setNotification] = useState(null);
const idRef = useRef(0);
const showNotification = useCallback((message, type = "notice", effect = "slidetop") => {
const newId = ++idRef.current;
setNotification({ id: newId, message, type, effect });
}, []);
const dismissNotification = useCallback(() => {
setNotification(null);
}, []);
return (
<div>
{/* Trigger buttons */}
<button onClick={() => showNotification("Settings saved!", "success", "slidetop")}>
Success (Slide)
</button>
<button onClick={() => showNotification("Loading complete!", "notice", "exploader")}>
Notice (Loader)
</button>
{/* Render notification */}
{notification && (
<TopBarNotification
message={notification.message}
effect={notification.effect}
type={notification.type}
ttl={6000}
isVisible={true}
onDismiss={dismissNotification}
/>
)}
</div>
);
}