WebberUI

Broadcast Bar System

A top broadcast bar system that stacks multiple announcements, rotates them through a priority queue, supports countdowns, CTAs, and dismissal memory, and springs its height changes so the content below is pushed smoothly.

Loading preview…
npx shadcn@latest add https://webberui.com/r/broadcast-bar-system.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

5000
<BroadcastBarSystem />

Installation

npx shadcn@latest add https://webberui.com/r/broadcast-bar-system.json

Or, once registries are configured in components.json, install it as @webberui/broadcast-bar-system.

Usage

import { BroadcastBarSystem } from "@/components/ui/broadcast-bar-system";

<BroadcastBarSystem
  messages={[
    {
      id: "sale",
      variant: "promo",
      priority: 3,
      content: "Final hours of the summer sale",
      countdownTo: Date.now() + 1000 * 60 * 60 * 2,
      cta: { label: "Shop now", href: "/sale" },
    },
    {
      id: "maintenance",
      variant: "warning",
      priority: 1,
      content: "The system will be under maintenance this Sunday at 02:00",
    },
  ]}
/>

Announcements are queued from highest to lowest priority and rotate automatically; the ones a user dismisses are remembered in localStorage and do not reappear after a refresh. The broadcast bar itself sits in normal flow layout, and its height transitions with a spring, so the page content below is pushed naturally and smoothly.

Props

BroadcastBarSystem

PropTypeDefaultDescription
messagesBroadcastMessage[]The announcement array, rotated from highest to lowest priority
rotateIntervalnumber5000Automatic rotation interval (milliseconds); set to 0 to disable
storageKeystring | null"wb-broadcast-dismissed"localStorage key used to remember dismissed announcements; null disables persistence
stickybooleanfalseWhether it sticks to the top of the container
onDismiss(id: string) => voidFires when a single announcement is dismissed
onEmpty() => voidFires once when every announcement has been dismissed and the bar collapses to zero height

BroadcastMessage

FieldTypeDefaultDescription
idstringUnique identifier, used for rotation positioning and dismissal memory
contentReact.ReactNodeThe main text content
prioritynumber0Higher numbers rotate in earlier
variant"info" | "success" | "warning" | "critical" | "promo""info"Accent color and default icon
iconReact.ReactNodeCustom leading icon, overriding the variant default
ctaBroadcastCTACall to action; renders a link when it has an href, otherwise a button
countdownTonumber | DateCountdown target, showing a live HH:MM:SS countdown
onCountdownEnd() => voidFires once when the countdown reaches zero
dismissiblebooleantrueWhether to show a dismiss button

How it works

  • Priority queue: messages is stably sorted by priority and only announcements that have not been dismissed are shown; exactly one appears at a time, rotating at a fixed interval.
  • Countdown: countdownTo accepts a millisecond timestamp or a Date and updates every 250ms; past one day it shows D天 HH:MM:SS (a day count followed by HH:MM:SS). Reaching zero fires onCountdownEnd once.
  • Dismissal memory: the ids of dismissed announcements are written to localStorage (customize it with storageKey or turn it off with null); when localStorage is unavailable it silently degrades to remembering only the current session.
  • Smooth pushing: the bar measures its content height and transitions its own height with a spring, so switching between announcements of different heights or dismissing them all pushes the content below smoothly or lets it fill back in.

Accessibility

  • The broadcast bar is marked with role="region" plus an aria-label, which makes it easy for assistive technology to locate.
  • With multiple announcements it provides keyboard-operable navigation dots (role="tab"), each labeled "item N of M"; the dismiss button carries an aria-label.
  • Rotation pauses automatically on pointer hover or when keyboard focus enters, so the content does not jump while the user is reading.
  • It respects the system "reduce motion" setting: automatic rotation and displacement animations are disabled, height switches instantly, and announcements fade in and out instead.
  • The countdown only renders after mount, which avoids the hydration mismatch that depending on Date.now() would cause.

On this page