Logo Marquee
betaA "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
each logo links to a case study: Tab to one and the row stops
Read how they use it
no pause button, with your own label
Used by teams like these
still: reduced motion or data-motion=off wraps the logos
Used by teams at
narrow container
Used by teams at
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the website component. It also adds every component it uses. Its own files go in your
componentsfolder, 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
| Prop | Type | Default | What it does |
|---|---|---|---|
| logos | { name: string; logo: ReactNode; href?: string }[] | - | 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. |
| label | ReactNode | "Used by teams at" | The line above the logos. |
| pauseButton | boolean | true | Shows a small button by the label that stops and starts the row. It hides when the row stands still. |
| renderLink | (props: LogoMarqueeLinkProps) => ReactNode | a plain <a> | Draws each logo's link. Spread the props onto your router's link: (props) => <Link {...props} />. |
| className | string | - | Extra classes on the root, which holds the container query. Set --logo-marquee-duration here to change the speed. |
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} />}
/>