# Announcement Bar Marquee With Linked Notices

A shadcn/ui block from the [@weinc](https://we.inc/components?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/weinc/announcement-bar-marquee-with-linked-notices).

- Page, with a live preview: https://shadcn.exchange/r/weinc/announcement-bar-marquee-with-linked-notices
- Screenshot: https://shadcn.exchange/shot/weinc/announcement-bar-marquee-with-linked-notices/light.png
- Registry item: https://we.inc/r/announcement-bar-marquee-with-linked-notices.json
- Listed: 2026-10-28, source last changed 2026-10-28

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/weinc/announcement-bar-marquee-with-linked-notices.json
```

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

## Files

### `components/we-inc/announcement-bar-marquee-with-linked-notices.tsx`

```tsx
import { useEffect, useRef, useState } from "react";

// Short notices for an invented boot maker. Each one is a real link.
const notices = [
  { label: "Resoling for winter: book a slot before 30 November", href: "#" },
  { label: "Read how a resole is done at our bench", href: "#" },
  { label: "New in: the waxed canvas field tote", href: "#" },
  { label: "Workshop open Tuesday to Saturday, 10 to 6", href: "#" },
];
const SPEED = 40; // seconds per loop

function Notices({ dup }: { dup?: boolean }) {
  return (
    <ul className="w3ann-group" aria-hidden={dup ? "true" : undefined}>
      {notices.map((n) => (
        <li key={n.label} className="flex items-center gap-10">
          <a
            href={n.href}
            tabIndex={dup ? -1 : undefined}
            className="rounded-sm underline decoration-[#8e8a80] underline-offset-4 hover:decoration-[#f4f1ea] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#e3b45a]"
          >
            {n.label}
          </a>
          <span aria-hidden="true" className="text-[#8e8a80]">/</span>
        </li>
      ))}
    </ul>
  );
}

export default function AnnouncementBarMarqueeWithLinkedNotices() {
  const root = useRef<HTMLElement>(null);
  const [paused, setPaused] = useState(false);
  const [off, setOff] = useState(false);

  useEffect(() => {
    const el = root.current;
    if (!el) return;
    const io = new IntersectionObserver(([e]) => setOff(!e.isIntersecting));
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return (
    <section
      ref={root}
      data-paused={paused ? "" : undefined}
      data-off={off ? "" : undefined}
      className="w3ann bg-[#f4f1ea] text-[#1d1b18]"
      style={{ ["--w3ann-speed" as string]: `${SPEED}s` }}
    >
      <style>{`
        .w3ann-view { overflow: hidden; flex: 1; }
        .w3ann-track { display: flex; width: max-content; animation: w3ann-run var(--w3ann-speed) linear infinite; }
        .w3ann-group { display: flex; align-items: center; gap: 40px; padding: 0 40px 0 0; margin: 0; list-style: none; height: 44px; white-space: nowrap; font-size: 14px; }
        .w3ann[data-paused] .w3ann-track, .w3ann[data-off] .w3ann-track, .w3ann-view:hover .w3ann-track, .w3ann-view:focus-within .w3ann-track { animation-play-state: paused; }
        @keyframes w3ann-run { to { transform: translateX(-50%); } }
        @media (prefers-reduced-motion: reduce) {
          .w3ann-view { overflow-x: auto; }
          .w3ann-track { animation: none; padding-left: 20px; }
          .w3ann-group[aria-hidden], .w3ann-toggle { display: none; }
        }
      `}</style>
      <div className="flex items-center bg-[#1d1b18] text-[#f4f1ea]">
        <div className="w3ann-view">
          <div className="w3ann-track">
            <Notices />
            <Notices dup />
          </div>
        </div>
        <button
          type="button"
          aria-pressed={paused}
          aria-label={paused ? "Play announcements" : "Pause announcements"}
          onClick={() => setPaused((p) => !p)}
          className="w3ann-toggle grid h-11 w-11 shrink-0 place-items-center border-l border-[#3a3732] hover:bg-[#2c2925] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[#e3b45a]"
        >
          <svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true" className="fill-current">
            {paused ? <path d="M4 2.5v11l9-5.5z" /> : <path d="M4 2.5h3v11H4zM9 2.5h3v11H9z" />}
          </svg>
        </button>
      </div>
      <header className="border-b border-[#ddd6c8]">
        <div className="mx-auto flex max-w-6xl items-center justify-between px-5 py-5">
          <a href="#" className="rounded font-serif text-xl font-semibold tracking-tight focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1d1b18]">Fenwick Boot Co.</a>
          <nav aria-label="Main">
            <ul className="hidden gap-8 text-sm font-medium text-[#3d3a34] sm:flex">
              {["Boots", "Repairs", "Care", "Our bench"].map((l) => (
                <li key={l}><a href="#" className="rounded hover:text-[#1d1b18] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1d1b18]">{l}</a></li>
              ))}
            </ul>
          </nav>
          <a href="#" className="inline-flex h-10 items-center rounded-full bg-[#1d1b18] px-4 text-sm font-medium text-[#f4f1ea] hover:bg-[#3a3732] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1d1b18] focus-visible:ring-offset-2">Basket (0)</a>
        </div>
      </header>
      <div className="mx-auto grid max-w-6xl gap-8 px-5 py-16 sm:py-20 md:grid-cols-2 md:items-end">
        <h1 className="font-serif text-4xl font-semibold leading-[1.1] tracking-tight sm:text-5xl">Goodyear welted boots, stitched and resoled in one workshop.</h1>
        <p className="max-w-md text-base leading-relaxed text-[#4a463e]">Leather from two tanneries we visit each spring, cut and lasted by hand, and resoled at the same bench when the soles wear through.</p>
      </div>
    </section>
  );
}
```
