Web InnoventixFreeCode

Logos Carousel

Original · free

logo carousel strip

byWeb InnoventixReact + Tailwind
carlogoscarousels
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/car-logos.json
car-logos.tsx
"use client";

import { useState, type ReactNode } from "react";
import { motion, useReducedMotion } from "motion/react";

type Logo = {
  name: string;
  sector: string;
  Icon: (props: { className?: string }) => ReactNode;
};

const LOGOS: Logo[] = [
  {
    name: "Northwind Freight",
    sector: "Logistics",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M3 8h11a2.5 2.5 0 1 0-2.5-2.5" />
        <path d="M3 12h15a2.5 2.5 0 1 1-2.5 2.5" />
        <path d="M3 16h8" />
      </svg>
    ),
  },
  {
    name: "Lumen Analytics",
    sector: "Data",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <circle cx="12" cy="12" r="3.5" />
        <path d="M12 2.5v2.5M12 19v2.5M2.5 12H5M19 12h2.5M5.2 5.2l1.8 1.8M17 17l1.8 1.8M18.8 5.2 17 7M7 17l-1.8 1.8" />
      </svg>
    ),
  },
  {
    name: "Cedarline",
    sector: "Sustainability",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M12 3 6.5 11h11z" />
        <path d="M12 8 5 17h14z" />
        <path d="M12 17v4" />
      </svg>
    ),
  },
  {
    name: "Quanta Robotics",
    sector: "Hardware",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M12 3 20 7.5v9L12 21 4 16.5v-9z" />
        <circle cx="12" cy="12" r="2.4" />
      </svg>
    ),
  },
  {
    name: "Meridian Health",
    sector: "Healthcare",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <circle cx="12" cy="12" r="8.5" />
        <path d="M12 8v8M8 12h8" />
      </svg>
    ),
  },
  {
    name: "Halcyon Studios",
    sector: "Media",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M3 9c3-3 6 3 9 0s6 3 9 0" />
        <path d="M3 15c3-3 6 3 9 0s6 3 9 0" />
      </svg>
    ),
  },
  {
    name: "Ironpeak Capital",
    sector: "Finance",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M3 19 10 8l3 4 3-5 5 12z" />
      </svg>
    ),
  },
  {
    name: "Sable Coffee",
    sector: "Retail",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M6 9h9v3.5a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4z" />
        <path d="M15 10h1.8a2 2 0 1 1 0 4H15" />
        <path d="M9 3.5v2M12 3.5v2" />
      </svg>
    ),
  },
  {
    name: "Vireo Cloud",
    sector: "Infrastructure",
    Icon: ({ className }) => (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
        <path d="M5 19C5 11 11 5 19 5c0 8-6 14-14 14z" />
        <path d="M9 15 15 9" />
      </svg>
    ),
  },
];

export default function CarLogos() {
  const reduce = useReducedMotion();
  const [playing, setPlaying] = useState<boolean>(true);
  const [reverse, setReverse] = useState<boolean>(false);
  const [speed, setSpeed] = useState<number>(3);
  const [hovered, setHovered] = useState<boolean>(false);

  const duration = 70 / speed;
  const paused = reduce || !playing || hovered;

  const pillBase =
    "inline-flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:focus-visible:ring-offset-slate-950 disabled:cursor-not-allowed disabled:opacity-50";
  const pillIdle =
    " border-slate-200 bg-white text-slate-700 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800";
  const pillActive =
    " border-indigo-600 bg-indigo-600 text-white hover:bg-indigo-500 dark:border-indigo-500 dark:bg-indigo-500 dark:text-white dark:hover:bg-indigo-400";

  const renderList = (hidden: boolean) => (
    <ul
      className="flex shrink-0 list-none items-center gap-x-12"
      aria-hidden={hidden || undefined}
      aria-label={hidden ? undefined : "Our partners and integrations"}
    >
      {LOGOS.map((logo) => (
        <li key={(hidden ? "dup-" : "") + logo.name} className="group flex shrink-0 items-center gap-3">
          <span className="text-slate-300 transition-colors duration-300 group-hover:text-indigo-500 dark:text-slate-600 dark:group-hover:text-indigo-400">
            {logo.Icon({ className: "h-7 w-7" })}
          </span>
          <span className="text-lg font-semibold tracking-tight text-slate-400 transition-colors duration-300 group-hover:text-slate-900 dark:text-slate-500 dark:group-hover:text-white">
            {logo.name}
          </span>
        </li>
      ))}
    </ul>
  );

  return (
    <section className="relative w-full overflow-hidden bg-slate-50 py-24 dark:bg-slate-950 sm:py-28">
      <style>{`
        @keyframes carlogos-marquee {
          from { transform: translateX(0); }
          to { transform: translateX(-50%); }
        }
        @keyframes carlogos-pulse {
          0%, 100% { transform: scale(1); opacity: 0.7; }
          50% { transform: scale(2.2); opacity: 0; }
        }
        @media (prefers-reduced-motion: reduce) {
          .carlogos-track { animation: none !important; transform: none !important; }
          .carlogos-pulse { animation: none !important; }
        }
      `}</style>

      <div className="mx-auto max-w-6xl px-6">
        <motion.div
          className="text-center"
          initial={reduce ? false : { opacity: 0, y: 16 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, margin: "-80px" }}
          transition={{ duration: 0.6, ease: "easeOut" }}
        >
          <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-1 text-xs font-medium uppercase tracking-widest text-slate-500 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400">
            <span className="relative flex h-2 w-2">
              <span
                className="carlogos-pulse absolute inline-flex h-full w-full rounded-full bg-emerald-400"
                style={reduce ? undefined : { animation: "carlogos-pulse 2s ease-in-out infinite" }}
              />
              <span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-500" />
            </span>
            Live ecosystem
          </span>

          <h2 className="mt-6 text-balance text-3xl font-bold tracking-tight text-slate-900 dark:text-white sm:text-4xl">
            The tools your team already trusts, working as one
          </h2>
          <p className="mx-auto mt-4 max-w-2xl text-pretty text-base text-slate-600 dark:text-slate-300 sm:text-lg">
            A live look at the partners, integrations, and clients moving through the platform every
            day. Pause it, slow it down, or send it the other way.
          </p>
        </motion.div>

        <div className="mt-10 flex flex-wrap items-center justify-center gap-3 sm:gap-4">
          <button
            type="button"
            onClick={() => setPlaying((p) => !p)}
            disabled={reduce ?? undefined}
            className={pillBase + (playing && !reduce ? pillActive : pillIdle)}
          >
            {playing ? (
              <svg viewBox="0 0 24 24" fill="currentColor" className="h-4 w-4" aria-hidden="true">
                <rect x="6" y="5" width="4" height="14" rx="1" />
                <rect x="14" y="5" width="4" height="14" rx="1" />
              </svg>
            ) : (
              <svg viewBox="0 0 24 24" fill="currentColor" className="h-4 w-4" aria-hidden="true">
                <path d="M8 5v14l11-7z" />
              </svg>
            )}
            {playing ? "Pause" : "Play"}
          </button>

          <button
            type="button"
            onClick={() => setReverse((r) => !r)}
            disabled={reduce ?? undefined}
            aria-pressed={reverse}
            className={pillBase + (reverse && !reduce ? pillActive : pillIdle)}
          >
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" strokeLinejoin="round" className="h-4 w-4" aria-hidden="true">
              <path d="M3 12h18" />
              <path d="m6 9-3 3 3 3" />
              <path d="m18 9 3 3-3 3" />
            </svg>
            Reverse
          </button>

          <div className="inline-flex items-center gap-3 rounded-full border border-slate-200 bg-white px-4 py-2 dark:border-slate-700 dark:bg-slate-900">
            <label
              htmlFor="carlogos-speed"
              className="text-sm font-medium text-slate-700 dark:text-slate-200"
            >
              Speed
            </label>
            <span className="text-xs text-slate-400 dark:text-slate-500" aria-hidden="true">Slow</span>
            <input
              id="carlogos-speed"
              type="range"
              min={1}
              max={5}
              step={1}
              value={speed}
              onChange={(e) => setSpeed(Number(e.target.value))}
              disabled={reduce ?? undefined}
              aria-label="Scrolling speed"
              aria-valuetext={`Speed ${speed} of 5`}
              className="h-2 w-28 cursor-pointer appearance-none rounded-full bg-slate-200 accent-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 dark:bg-slate-700 dark:accent-indigo-400 dark:focus-visible:ring-offset-slate-900"
            />
            <span className="text-xs text-slate-400 dark:text-slate-500" aria-hidden="true">Fast</span>
          </div>
        </div>

        {reduce ? (
          <p className="mt-4 text-center text-sm text-slate-500 dark:text-slate-400">
            Reduced-motion is on, so the strip is paused. Scroll it sideways to browse.
          </p>
        ) : null}

        <div
          className={
            "relative mt-12 rounded-3xl border border-slate-200 bg-white py-8 dark:border-slate-800 dark:bg-slate-900 " +
            (reduce ? "overflow-x-auto" : "overflow-hidden")
          }
          role="group"
          aria-label="Logo carousel"
          onMouseEnter={() => setHovered(true)}
          onMouseLeave={() => setHovered(false)}
        >
          {!reduce ? (
            <>
              <div className="pointer-events-none absolute inset-y-0 left-0 z-10 w-16 rounded-l-3xl bg-gradient-to-r from-white to-transparent dark:from-slate-900 sm:w-28" />
              <div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-16 rounded-r-3xl bg-gradient-to-l from-white to-transparent dark:from-slate-900 sm:w-28" />
            </>
          ) : null}

          <div
            className="carlogos-track flex w-max items-center gap-x-12 px-10"
            style={
              reduce
                ? undefined
                : {
                    animationName: "carlogos-marquee",
                    animationDuration: `${duration}s`,
                    animationTimingFunction: "linear",
                    animationIterationCount: "infinite",
                    animationDirection: reverse ? "reverse" : "normal",
                    animationPlayState: paused ? "paused" : "running",
                  }
            }
          >
            {renderList(false)}
            {!reduce ? renderList(true) : null}
          </div>
        </div>

        <p className="mt-6 text-center text-sm text-slate-500 dark:text-slate-400">
          Plus 40+ more in the integration marketplace, added every week.
        </p>
      </div>
    </section>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →