All components

Sticky Banner

Dismissible announcement bar pinned above the header.

0.8 KB gzippedZero dependenciesReduced-motion safeBase UI & Radix compatible
Velora 0.4 is out — 64 components

Installation

npx shadcn@latest add https://innsite.com.mx/r/sticky-banner.json

Source

"use client";

import { useState } from "react";

import { cn } from "@/lib/utils";

interface StickyBannerProps extends React.HTMLAttributes<HTMLDivElement> {
  /** Hide the dismiss button for banners that must stay put */
  dismissible?: boolean;
  /** Called after the banner is dismissed */
  onDismiss?: () => void;
  children: React.ReactNode;
}

/**
 * Announcement bar that pins to the top of the page and can be dismissed.
 * Sits above a sticky header when both are present.
 */
export function StickyBanner({
  dismissible = true,
  onDismiss,
  children,
  className,
  ...props
}: StickyBannerProps) {
  const [open, setOpen] = useState(true);

  if (!open) return null;

  return (
    <div
      {...props}
      data-slot="sticky-banner"
      className={cn(
        "sticky top-0 z-60 flex items-center justify-center gap-3 bg-gradient-to-r from-brand-from via-brand-via to-brand-to px-4 py-2.5 text-center text-sm text-brand-foreground",
        className
      )}
    >
      <div className="flex-1">{children}</div>
      {dismissible && (
        <button
          type="button"
          aria-label="Dismiss announcement"
          onClick={() => {
            setOpen(false);
            onDismiss?.();
          }}
          className="-mr-1 shrink-0 rounded-md p-1 opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
        >
          <svg
            width="14"
            height="14"
            viewBox="0 0 14 14"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.75"
            strokeLinecap="round"
            aria-hidden
          >
            <path d="M3 3l8 8M11 3l-8 8" />
          </svg>
        </button>
      )}
    </div>
  );
}