# Banner-2

> A full-width dismissible announcement bar

A shadcn/ui block from the [@hexui](https://hexui.sh) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/hexui/banner-2).

- Page, with a live preview: https://shadcn.exchange/r/hexui/banner-2
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/hexui/banner-2-light-0da0b58910f07deb.png
- Registry item: https://hexui.sh/r/banner-2.json
- Categories: banner
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://hexui.sh/r/banner-2.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`

## Files

### `registry/blocks/banner/banner-2/source/app/banner/page.tsx`

```tsx
import { Banner } from "../../components/ui/banner"

export default function Page() {
  return (
    <div className="mx-auto flex min-h-screen w-full flex-col items-center">
      <Banner>
        {/* Copy swaps per breakpoint: short text on small screens, longer text on larger. */}
        <span className="font-semibold">Summer Sale is live</span>
        <span className="hidden sm:inline"> — up to 40% off on all plans.</span>
        <span className="sm:hidden"> · Up to 40% off </span>
        <span className="hidden md:inline"> No code needed.</span>
        <a href="#" className="ml-1 font-medium underline underline-offset-4">
          Shop now
        </a>
      </Banner>
    </div>
  )
}
```

### `registry/blocks/banner/banner-2/source/components/ui/banner.tsx`

```tsx
"use client"

import { type ReactNode, useState } from "react"
import { X } from "lucide-react"

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

export interface BannerProps {
  /** The content to display inside the banner. */
  children: ReactNode
  /** Additional CSS classes for the outer section. */
  className?: string
}

export function Banner({ children, className }: BannerProps) {
  // Local-only dismiss state: the banner stays hidden until the component remounts.
  const [dismissed, setDismissed] = useState(false)

  if (dismissed) return null

  return (
    <section
      aria-label="Promotional announcement"
      className={cn(
        "relative w-full bg-foreground px-4 py-3 text-primary-foreground",
        className
      )}
    >
      <div className="mx-auto flex max-w-7xl items-center justify-start text-sm font-medium sm:justify-center">
        <div className="text-balance text-center leading-snug">
          {children}
        </div>
      </div>
      <button
        type="button"
        onClick={() => setDismissed(true)}
        aria-label="Dismiss promotional banner"
        className="absolute right-2 top-1/2 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-full transition-colors hover:bg-current/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current/20"
      >
        <X className="size-4" aria-hidden="true" />
      </button>
    </section>
  )
}
```
