Skip to content
txbrendan/ui

Logo Marquee

beta

A "Used by teams at" row of logos that scrolls slowly in a loop and stands still for reduced motion.

Built from Button.

Preview

Used by teams at

  • Studio
  • Clinic
  • Lab
  • Library
  • Bakery
  • Academy
  • Agency
  • Workshop

each logo links to a case study: Tab to one and the row stops

no pause button, with your own label

Used by teams like these

  • Lab
  • Library
  • Bakery
  • Academy
  • Agency
  • Workshop

still: reduced motion or data-motion=off wraps the logos

Used by teams at

  • Studio
  • Clinic
  • Lab
  • Library
  • Bakery
  • Academy
  • Agency
  • Workshop

narrow container

Used by teams at

  • Studio
  • Clinic
  • Lab
  • Library
  • Bakery
  • Academy
  • Agency
  • Workshop

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the website component. It also adds every component it uses. Its own files go in your components folder, and they are yours to edit.

    npx shadcn@latest add @txbrendan/logo-marquee

Use it for

  • Under a Hero, to show who already uses the product.
  • Six to twelve logos. Fewer than six leaves the loop looking thin.
  • Draw each logo in currentColor, so they all take the same muted gray and follow the theme.
  • Give each logo the company's `name`. Screen readers say it, and it is the key.
  • Give a logo an `href` when you have a case study. Pass your router's link through `renderLink`.
  • Slow it down or speed it up with className="[--logo-marquee-duration:60s]".

Avoid it for

  • Logos you have no right to show. Ask each customer first.
  • Two marquees stacked on one page. One moving row is enough.
  • Content people must read or press, like plans or news. Use a static grid, or Bento Grid.
  • Fewer than four logos. Put them in a plain row instead.

Props

  • logos

    The logos, in order. Each name must be unique, since it is the key. An SVG or <img> as the direct child of `logo` fills its height: 28px, or 32px from 48rem.

    { name: string; logo: ReactNode; href?: string }[]

  • label

    The line above the logos.

    ReactNode · default "Used by teams at"

  • pauseButton

    Shows a small button by the label that stops and starts the row. It hides when the row stands still.

    boolean · default true

  • renderLink

    Draws each logo's link. Spread the props onto your router's link: (props) => <Link {...props} />.

    (props: LogoMarqueeLinkProps) => ReactNode · default a plain <a>

  • className

    Extra classes on the root, which holds the container query. Set --logo-marquee-duration here to change the speed.

    string

Accessibility

  • The logos are a list. Each one is named by its `name`, and the drawn logo is hidden from screen readers.
  • The row repeats once to fill the loop. The copy is aria-hidden and inert, so screen readers and Tab meet each logo once.
  • The row stops while the pointer is over it, or while focus is inside it. Tab to a linked logo and the row goes back to its start with no fade, then scrolls to show that logo whole.
  • A pause button by the label stops it for good, on touch screens too. It says it is pressed with aria-pressed.
  • With prefers-reduced-motion, or data-motion="off", the row stands still and wraps, so every logo shows. The pause button and the edge fade go away then.
  • In forced colors the edge fade goes away, so a logo at either edge is cut off instead of faded.

Examples

Under a hero

<LogoMarquee
  logos={[
    { name: "Studio", logo: <img src="/logos/studio.svg" alt="" /> },
    { name: "Clinic", logo: <img src="/logos/clinic.svg" alt="" /> },
    { name: "Library", logo: <img src="/logos/library.svg" alt="" /> },
    { name: "Bakery", logo: <img src="/logos/bakery.svg" alt="" /> },
    { name: "Agency", logo: <img src="/logos/agency.svg" alt="" /> },
    { name: "Workshop", logo: <img src="/logos/workshop.svg" alt="" /> },
  ]}
/>

Links to case studies, slower

<LogoMarquee
  label="Read how they use it"
  className="[--logo-marquee-duration:60s]"
  logos={[
    { name: "Studio", logo: <img src="/logos/studio.svg" alt="" />, href: "/customers/studio" },
    { name: "Clinic", logo: <img src="/logos/clinic.svg" alt="" />, href: "/customers/clinic" },
  ]}
  renderLink={(props) => <Link {...props} />}
/>