<script>
import LogosCarousel from '$registry/spelte/logos-carousel.svelte';
const logoItems = [
{ src: '/logos/vercel.svg', alt: 'Vercel logo' },
{ src: '/logos/google.svg', alt: 'Google logo' },
{ src: '/logos/framer.svg', alt: 'Framer logo' },
{ src: '/logos/discord.svg', alt: 'Discord logo' },
{ src: '/logos/openai.svg', alt: 'OpenAI logo' },
{ src: '/logos/phantom.svg', alt: 'Phantom logo' },
{ src: '/logos/descript.svg', alt: 'Descript logo' },
{ src: '/logos/netflix.svg', alt: 'Netflix logo' },
{ src: '/logos/linear.svg', alt: 'Linear logo' },
{ src: '/logos/notion.svg', alt: 'Notion logo' },
{ src: '/logos/shopify.svg', alt: 'Shopify logo' },
{ src: '/logos/duolingo.svg', alt: 'Duolingo logo' },
{ src: '/logos/ramp.svg', alt: 'Ramp logo' },
{ src: '/logos/tesla.svg', alt: 'Tesla logo' },
{ src: '/logos/opensea.svg', alt: 'OpenSea logo' },
{ src: '/logos/cursor.svg', alt: 'Cursor logo' }
];
</script>
<LogosCarousel logos={logoItems} count={4} class="gap-6 sm:gap-10" />Installation
pnpm dlx shadcn-svelte@latest add https://spelte.dev/r/logos-carousel.json<script lang="ts">
import { cn } from '$lib/utils';
type Logo = {
src: string;
alt: string;
};
interface Props {
logos: Logo[];
stagger?: number;
count?: number;
class?: string;
duration?: number;
interval?: number;
initialDelay?: number;
}
let {
logos,
stagger = 0.14,
count,
class: className,
duration = 600,
interval = 2500,
initialDelay = 500
}: Props = $props();
const logosPerGroup = $derived(count || logos.length);
const groups = $derived.by(() => {
const result: Logo[][] = [];
for (let i = 0; i < logos.length; i += logosPerGroup) {
result.push(logos.slice(i, i + logosPerGroup));
}
return result;
});
let currentIndex = $state(0);
let nextIndex = $state(1);
let animate = $state(false);
$effect(() => {
const id = setTimeout(() => (animate = true), initialDelay);
return () => clearTimeout(id);
});
$effect(() => {
if (!animate || groups.length === 0) return;
const id = setInterval(() => {
const newIndex = (currentIndex + 1) % groups.length;
currentIndex = newIndex;
nextIndex = (newIndex + 1) % groups.length;
}, interval);
return () => clearInterval(id);
});
</script>
<div class="max-w-[720px] grid place-items-center w-full">
{#each groups as group, groupIndex (groupIndex)}
{@const isCurrent = groupIndex === currentIndex}
{@const isNext = groupIndex === nextIndex && animate}
{@const isVisible = isCurrent || isNext}
<div
class={cn('flex w-full justify-center gap-10', className)}
aria-hidden={!isVisible}
style="grid-area: 1 / 1; pointer-events: {isVisible ? 'auto' : 'none'}; visibility: {isVisible ? 'visible' : 'hidden'};"
>
{#each group as logo, logoIndex (logo.src)}
{@const d = logoIndex * stagger}
{@const state = isCurrent ? 'exit' : 'enter'}
{@const animName = state === 'enter' ? 'logos-enter' : 'logos-exit'}
<div
style="
animation-delay: {d}s;
animation-duration: {duration}ms;
animation-fill-mode: both;
{animate && isVisible ? `animation-name: ${animName}; animation-timing-function: ease;` : ''}
opacity: {!animate ? (state === 'exit' ? 1 : 0) : !isVisible ? 0 : undefined};
"
>
<img
src={logo.src}
alt={logo.alt}
width="96"
height="96"
class="h-24 w-24 object-contain opacity-70 not-dark:invert-100 pointer-events-none select-none"
/>
</div>
{/each}
</div>
{/each}
</div>
<style>
@keyframes logos-enter {
0% {
transform: translateY(40px);
filter: blur(4px);
opacity: 0;
}
100% {
transform: translateY(0);
filter: blur(0px);
opacity: 1;
}
}
@keyframes logos-exit {
0% {
transform: translateY(0);
filter: blur(0px);
opacity: 1;
}
100% {
transform: translateY(-40px);
filter: blur(4px);
opacity: 0;
}
}
</style>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
logos | { src: string; alt: string }[] | — | Logo images to display |
count | number | — | Logos per group (defaults to all) |
stagger | number | 0.14 | Delay between logo animations (seconds) |
duration | number | 600 | Animation duration (ms) |
interval | number | 2500 | Time between transitions (ms) |
initialDelay | number | 500 | Initial delay before first transition (ms) |
class | string | — | Additional CSS classes for the flex container |