# Team Section 5

> Solace UI team section block variation 5.

A shadcn/ui block from the [@solaceui](https://www.solaceui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/solaceui/team-section-5).

- Page, with a live preview: https://shadcn.exchange/r/solaceui/team-section-5
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/solaceui/team-section-5-light-8b8c2c51ab706ff7.png
- Registry item: https://www.solaceui.com/r/team-section-5.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://www.solaceui.com/r/team-section-5.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`, `motion`

## Files

### `src/registry/blocks/team-section/five/index.tsx`

```tsx
"use client";

import { useState } from "react";
import Image from "next/image";
import { motion, AnimatePresence } from "motion/react";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";

interface TeamMember {
  id: string;
  name: string;
  role: string;
  image: string;
  xUrl?: string;
  linkedinUrl?: string;
}

const teamMembers: TeamMember[] = [
  {
    id: "member-1",
    name: "Sarah Chen",
    role: "Head of Design",
    image: "https://assets.solaceui.com/solaceui-member-five.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
  {
    id: "member-2",
    name: "Marcus Rodriguez",
    role: "VP of Engineering",
    image: "https://assets.solaceui.com/solaceui-member-one.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
  {
    id: "member-3",
    name: "James Mitchell",
    role: "Product Manager",
    image: "https://assets.solaceui.com/solaceui-member-four.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
  {
    id: "member-4",
    name: "David Kim",
    role: "Founder & CEO",
    image: "https://assets.solaceui.com/solaceui-member-two.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
  {
    id: "member-5",
    name: "Olivia Koe",
    role: "Lead Designer",
    image: "https://assets.solaceui.com/solaceui-member-six.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
  {
    id: "member-6",
    name: "Amara Okonkwo",
    role: "CTO",
    image: "https://assets.solaceui.com/solaceui-member-three.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
  {
    id: "member-7",
    name: "Sarah Chen",
    role: "Head of Design",
    image: "https://assets.solaceui.com/solaceui-member-five.png",
    xUrl: "https://x.com",
    linkedinUrl: "https://linkedin.com",
  },
];

// Layout coordinates normalized to percentages from design coordinates:
// Container aspect ratio: 100/76.1
// Card dimensions: width = 19.65cqw, height = 18.5cqw (Polaroid aspect ratio ~17/16)
const cardLayouts = [
  { left: "58.5cqw", top: "6cqw" },     // Card 1: Sarah Chen
  { left: "80.35cqw", top: "6cqw" },    // Card 2: Marcus Rodriguez
  { left: "0cqw", top: "31.8cqw" },     // Card 3: James Mitchell
  { left: "21.85cqw", top: "31.8cqw" }, // Card 4: David Kim
  { left: "80.35cqw", top: "31.8cqw" }, // Card 5: Olivia Koe
  { left: "58.5cqw", top: "57.6cqw" },    // Card 6: Amara Okonkwo
  { left: "80.35cqw", top: "57.6cqw" },   // Card 7: Sarah Chen (repeated)
];

const TwitterIcon = () => (
  <svg
    width="20"
    height="18"
    viewBox="0 0 25 22"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    className="w-5 h-auto text-white/50 hover:text-white transition-colors"
  >
    <path
      d="M10.558 14.078L16.313 21.75H24.771L15.276 9.089L23.176 0H19.974L13.791 7.112L8.458 0H0L9.075 12.101L0.689 21.75H3.891L10.558 14.078ZM17.521 19.333L4.833 2.417H7.25L19.938 19.333H17.521Z"
      fill="currentColor"
    />
  </svg>
);

const LinkedInIcon = () => (
  <svg
    width="18"
    height="18"
    viewBox="0 0 23 23"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    className="w-4.5 h-auto text-white/50 hover:text-white transition-colors"
  >
    <path
      d="M20 0C20.663 0 21.299 0.263 21.768 0.732C22.237 1.201 22.5 1.837 22.5 2.5V20C22.5 20.663 22.237 21.299 21.768 21.768C21.299 22.237 20.663 22.5 20 22.5H2.5C1.837 22.5 1.201 22.237 0.732 21.768C0.263 21.299 0 20.663 0 20V2.5C0 1.837 0.263 1.201 0.732 0.732C1.201 0.263 1.837 0 2.5 0H20ZM19.375 19.375V12.75C19.375 11.669 18.946 10.633 18.181 9.869C17.417 9.104 16.381 8.675 15.3 8.675C14.238 8.675 13 9.325 12.4 10.3V8.912H8.912V19.375H12.4V13.213C12.4 12.25 13.175 11.463 14.137 11.463C14.602 11.463 15.047 11.647 15.375 11.975C15.703 12.303 15.887 12.748 15.887 13.213V19.375H19.375ZM4.85 6.95C5.407 6.95 5.941 6.729 6.335 6.335C6.729 5.941 6.95 5.407 6.95 4.85C6.95 3.688 6.013 2.737 4.85 2.737C4.29 2.737 3.752 2.96 3.356 3.356C2.96 3.752 2.737 4.29 2.737 4.85C2.737 6.013 3.688 6.95 4.85 6.95ZM6.588 19.375V8.912H3.125V19.375H6.588Z"
      fill="currentColor"
    />
  </svg>
);

export default function Team5() {
  const [selectedMember, setSelectedMember] = useState<TeamMember | null>(null);

  return (
    <section className="relative w-full py-20 md:py-28 bg-white dark:bg-background overflow-hidden flex flex-col items-center justify-center">
      {/* Dashed background pattern spanning end-to-end (full viewport width) */}
      <div className="absolute inset-x-0 top-1/2 -translate-y-1/2 h-[82%] z-0 opacity-10 bg-[repeating-linear-gradient(315deg,currentColor_0,currentColor_1px,transparent_0,transparent_50%)] bg-[length:10px_10px] border-y border-black dark:border-white pointer-events-none"></div>

      <div className="w-full max-w-5xl px-4 md:px-8 z-10 flex flex-col items-center justify-center">

        {/* Main Grid Wrapper utilizing Container Queries for flawless scaling */}
        <div
          className={cn(
            "@container relative w-full aspect-[100/76.1] z-10 transition-all duration-500 ease-in-out",
            selectedMember ? "opacity-15 pointer-events-none blur-[1.5px]" : "opacity-100"
          )}
        >
          {/* Background Text "Meet" - Styled to sit at same top and height as Row 1 Cards */}
          <div
            className="absolute left-0 font-sans font-semibold text-black dark:text-white select-none tracking-tight flex items-center leading-none"
            style={{
              top: "6cqw",
              height: "18.5cqw",
              fontSize: "25cqw",
            }}
          >
            Meet
          </div>

          {/* Background Text "The" - Styled to sit at same top and height as Row 2 Cards */}
          <div
            className="absolute font-sans font-semibold text-black dark:text-white select-none tracking-tight flex items-center leading-none"
            style={{
              left: "43.7cqw",
              top: "31.8cqw",
              height: "18.5cqw",
              fontSize: "25cqw",
            }}
          >
            The
          </div>

          {/* Background Text "Team" - Styled to sit at same top and height as Row 3 Cards */}
          <div
            className="absolute left-0 font-sans font-semibold text-black dark:text-white select-none tracking-tight flex items-center leading-none"
            style={{
              top: "57.6cqw",
              height: "18.5cqw",
              fontSize: "25cqw",
            }}
          >
            Team
          </div>

          {/* Polaroid Cards - Positioned on the exact same levels and heights as the text boxes */}
          {teamMembers.map((member, index) => {
            const layout = cardLayouts[index];
            return (
              <motion.div
                key={`${member.id}-${index}`}
                layoutId={`card-${member.id}-${index}`}
                onClick={() => setSelectedMember(member)}
                className="absolute p-[1%] bg-white/95 dark:bg-zinc-900/95 border border-neutral-200/50 dark:border-zinc-800/50 shadow-md shadow-black/5 hover:shadow-xl dark:shadow-black/30 rounded-[4%] cursor-pointer flex flex-col justify-between"
                style={{
                  left: layout.left,
                  top: layout.top,
                  width: "19.65cqw",
                  height: "18.5cqw",
                }}
                whileHover={{
                  scale: 1.04,
                  rotate: index % 2 === 0 ? 1 : -1,
                }}
                transition={{
                  type: "spring",
                  stiffness: 300,
                  damping: 20,
                }}
              >
                <div className="relative w-full h-[85%] rounded-[3%] overflow-hidden bg-neutral-100 dark:bg-zinc-800">
                  <Image
                    src={member.image}
                    alt={member.name}
                    fill
                    className={cn(
                      "object-cover object-top transition-all duration-500",
                      "grayscale hover:grayscale-0"
                    )}
                    sizes="(max-width: 768px) 15vw, 10vw"
                    unoptimized
                  />
                </div>
                {/* Subtle placeholder line to balance the Polaroid look */}
                <div className="w-1/3 h-1 bg-neutral-200 dark:bg-zinc-700 rounded-full mx-auto mt-auto opacity-40" />
              </motion.div>
            );
          })}
        </div>
      </div>

      {/* Expanded Modal Card */}
      <AnimatePresence>
        {selectedMember && (
          <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
            {/* Backdrop */}
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              onClick={() => setSelectedMember(null)}
              className="absolute inset-0 bg-white/20 dark:bg-black/40 backdrop-blur-md cursor-pointer"
            />

            {/* Modal Detail Card */}
            {(() => {
              // Find the index of the selected member to get its original slot for layout animation
              const index = teamMembers.findIndex((m) => m.id === selectedMember.id);
              return (
                <motion.div
                  layoutId={`card-${selectedMember.id}-${index}`}
                  className="relative w-full max-w-[340px] sm:max-w-[400px] aspect-[134/193.75] bg-black text-white p-3.5 pb-6 rounded-xl border-4 border-black shadow-2xl z-50 flex flex-col justify-between"
                  transition={{
                    type: "spring",
                    stiffness: 260,
                    damping: 25,
                  }}
                >
                  {/* Close button on top-right of image */}
                  <button
                    onClick={() => setSelectedMember(null)}
                    className="absolute top-6 right-6 z-50 p-1.5 rounded-full bg-black/60 hover:bg-black/90 text-white/80 hover:text-white border border-white/10 transition-colors cursor-pointer"
                    aria-label="Close profile details"
                  >
                    <X className="w-4 h-4" />
                  </button>

                  {/* Polaroid Image Frame - Styled to be larger with thin padding around the edges */}
                  <div className="relative w-full h-[78%] rounded-lg overflow-hidden bg-zinc-900 border border-zinc-800/55">
                    <Image
                      src={selectedMember.image}
                      alt={selectedMember.name}
                      fill
                      className="object-cover object-top"
                      sizes="(max-width: 768px) 80vw, 40vw"
                      priority
                      unoptimized
                    />
                  </div>

                  {/* Member Info & Socials Section */}
                  <div className="mt-4 flex items-end justify-between px-2">
                    <div className="flex flex-col text-left">
                      <h3 className="font-sans font-semibold text-white text-xl sm:text-2xl tracking-tight leading-none">
                        {selectedMember.name}
                      </h3>
                      <p className="font-sans font-light text-neutral-400 text-sm sm:text-base mt-2.5 leading-none">
                        {selectedMember.role}
                      </p>
                    </div>
                    <div className="flex items-center gap-3 pb-0.5">
                      {selectedMember.xUrl && (
                        <a
                          href={selectedMember.xUrl}
                          target="_blank"
                          rel="noopener noreferrer"
                          className="hover:text-white transition-colors"
                          aria-label="Twitter profile"
                        >
                          <TwitterIcon />
                        </a>
                      )}
                      {selectedMember.linkedinUrl && (
                        <a
                          href={selectedMember.linkedinUrl}
                          target="_blank"
                          rel="noopener noreferrer"
                          className="hover:text-white transition-colors"
                          aria-label="LinkedIn profile"
                        >
                          <LinkedInIcon />
                        </a>
                      )}
                    </div>
                  </div>
                </motion.div>
              );
            })()}
          </div>
        )}
      </AnimatePresence>
    </section>
  );
}
```
