Command Search
Search for a page
/ Components / Team Photo Grid

Team Photo Grid

Team

Responsive photo grid of team members with a name/role caption overlay that appears on hover, plus a muted call-to-action banner for open roles.

teamgridhovercareers
Open preview
Live Preview
AI Generation Prompt

Create a Svelte 5 (runes) team photo grid. Centered header (shimmering-text heading + subtext). Below, a responsive grid (1 col mobile, 2 col sm, 3 col md, 4 col xl) of overlapping photo tiles: each tile is a `relative overflow-hidden rounded-lg group`, with the photo zooming in slightly on hover (`scale-125` with `ease-in duration-300`), and a permanent bottom gradient overlay (`from-transparent to-black/60`) with the member's name (bold, white) and role (gray) captioned at the bottom. Stagger each tile's scroll-reveal entrance with a 'scale' animation, 60ms × index. Below the grid, a muted rounded banner with a small people icon + short prompt text on the left and a 'View Open Roles' link with an arrow icon on the right (stacked on mobile, row on desktop). Data-driven via `content: { heading, subtext, members: {name, role, image}[], cta: {text, label, href} }`.

<script lang="ts">
	import ShimmeringText from '$lib/components/animate-ui/ShimmeringText.svelte';
	import { reveal } from '$lib/actions/reveal';

	type TeamMember = { name: string; role: string; image: string };
	type Props = {
		content: {
			heading: string;
			subtext: string;
			members: TeamMember[];
			cta: { text: string; label: string; href: string };
		};
	};
	let { content }: Props = $props();
</script>

<section class="bg-background antialiased">
	<div class="max-w-screen-xl px-4 py-8 mx-auto lg:px-6 sm:py-16 lg:py-24">
		<div class="max-w-3xl mx-auto text-center" use:reveal>
			<h2 class="text-3xl font-extrabold leading-tight tracking-tight text-foreground sm:text-4xl">
				<ShimmeringText text={content.heading} />
			</h2>
			<p class="mt-4 text-base font-normal text-muted-foreground sm:text-xl">{content.subtext}</p>
		</div>

		<div class="grid grid-cols-1 gap-4 mt-8 lg:mt-16 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4">
			{#each content.members as member, i}
				<div class="relative overflow-hidden rounded-lg group" use:reveal={{ animation: 'scale', delay: i * 60 }}>
					<img
						class="object-cover w-full h-[320px] lg:h-auto scale-100 ease-in duration-300 group-hover:scale-125 transition-transform"
						src={member.image}
						alt={member.name}
						loading="lazy"
						decoding="async"
					/>
					<div class="absolute inset-0 grid items-end justify-center p-4 bg-linear-to-b from-transparent to-black/60">
						<div class="text-center">
							<p class="text-xl font-bold text-white">{member.name}</p>
							<p class="text-base font-medium text-gray-300">{member.role}</p>
						</div>
					</div>
				</div>
			{/each}
		</div>

		<div class="max-w-3xl p-4 mx-auto mt-8 rounded-md lg:mt-16 bg-muted" use:reveal={{ animation: 'up', delay: 200 }}>
			<div class="flex flex-col justify-between gap-3 md:gap-6 md:items-center md:flex-row">
				<div class="flex items-center gap-1.5">
					<svg aria-hidden="true" class="hidden w-5 h-5 text-muted-foreground md:block shrink-0" viewBox="0 0 20 20" fill="currentColor">
						<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z" />
					</svg>
					<span class="text-base font-normal text-muted-foreground">{content.cta.text}</span>
				</div>
				<a href={content.cta.href} class="inline-flex items-center text-base font-medium text-primary hover:underline shrink-0">
					{content.cta.label}
					<svg aria-hidden="true" class="w-5 h-5 ml-1.5" viewBox="0 0 20 20" fill="currentColor">
						<path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd" />
					</svg>
				</a>
			</div>
		</div>
	</div>
</section>