# Integrations 8

A shadcn/ui block from the [@diarmuradi](https://diarmuradi.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/diarmuradi/integrations-8).

- Page, with a live preview: https://shadcn.exchange/r/diarmuradi/integrations-8
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/diarmuradi/integrations-8-light-69c353c5d258a804.png
- Registry item: https://diarmuradi.com/r/integrations-8.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://diarmuradi.com/r/integrations-8.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: `motion`
- Registry items: `https://diarmuradi.com/r/styles/base-nova/icons.json`, `https://diarmuradi.com/r/styles/base-nova/use-reduced-motion.json`

## Files

### `integrations.tsx`

```tsx
"use client"

import { useEffect, useRef, useState } from "react"
import { motion } from "motion/react"

import { useReducedMotion } from "@/hooks/use-reduced-motion"
import { Icons } from "@/components/ui/icons"

type IntegrationItem = {
  label: string
  icon: React.ComponentType<React.HTMLAttributes<SVGElement>>
  description: string
}

const integrations: IntegrationItem[] = [
  {
    label: "Gemini",
    icon: Icons.Gemini,
    description: "Imagine",
  },
  {
    label: "GitHub",
    icon: Icons.GitHub,
    description: "Team up",
  },
  {
    label: "OpenAI",
    icon: Icons.OpenAI,
    description: "Explore",
  },
  {
    label: "Cursor",
    icon: Icons.Cursor,
    description: "Create",
  },
  {
    label: "Replit",
    icon: Icons.Replit,
    description: "Launch",
  },
]

export function Integration() {
  const [active, setActive] = useState<number | null>(null)
  const fanRef = useRef<HTMLUListElement>(null)
  const reduceMotion = useReducedMotion()

  useEffect(() => {
    if (active === null) return

    function deselect(event: PointerEvent) {
      if (
        event.target instanceof Element &&
        fanRef.current?.contains(
          event.target.closest('[data-slot="integration-card"]')
        )
      ) {
        return
      }
      setActive(null)
    }

    document.addEventListener("pointerdown", deselect)
    return () => document.removeEventListener("pointerdown", deselect)
  }, [active])

  return (
    <section
      aria-label="Integrations"
      className="@container mx-auto flex w-full max-w-6xl flex-col items-center gap-8 px-4 py-16 sm:px-6 lg:py-24"
    >
      <header className="order-2 flex max-w-xl flex-col items-center gap-5 text-center">
        <p className="text-xs font-medium tracking-widest text-muted-foreground uppercase">
          Integrations
        </p>
        <h1 className="text-3xl font-semibold tracking-tight text-balance sm:text-5xl">
          A stack that
          <br />
          feels like you.
        </h1>
        <p className="text-base text-pretty text-muted-foreground">
          The tools you reach for, brought together. Pick your favorites and
          make room for your next idea.
        </p>
      </header>
      <ul
        ref={fanRef}
        aria-label="Pick a tool"
        onKeyDown={(event) => {
          if (event.key === "Escape") setActive(null)
        }}
        className="relative aspect-7/4 w-full max-w-4xl"
      >
        {integrations.map(({ label, description, icon: Icon }, index) => (
          <li
            key={label}
            className="absolute top-[6%] aspect-4/5 w-[24%]"
            style={{
              left: `${6 + index * 16}%`,
              zIndex: active === index ? 10 : 5 - Math.abs(index - 2),
            }}
          >
            <motion.button
              type="button"
              data-slot="integration-card"
              aria-label={`Select ${label}`}
              aria-pressed={active === index}
              onClick={() =>
                setActive((selected) => (selected === index ? null : index))
              }
              initial={false}
              animate={{
                y: active === index ? "-8%" : `${Math.abs(index - 2) * 32}%`,
                rotate: active === index ? 0 : (index - 2) * 9,
                scale: active === index ? 1.04 : 1,
              }}
              transition={{
                duration: reduceMotion ? 0 : 0.25,
              }}
              className="flex size-full flex-col items-center justify-between gap-1 rounded-2xl bg-card p-1.5 shadow-elevated-md focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ring focus-visible:outline-solid @sm:gap-3 @sm:rounded-3xl @sm:p-4 @lg:rounded-4xl @lg:p-6"
            >
              <Icon
                className="my-auto h-auto w-2/5 max-w-16 shrink-0"
                aria-hidden="true"
              />
              <span className="flex flex-col gap-1">
                <span className="text-xs font-medium @sm:text-sm @lg:text-base">
                  {label}
                </span>
                <span className="text-xs text-muted-foreground @lg:text-sm">
                  {description}
                </span>
              </span>
            </motion.button>
          </li>
        ))}
      </ul>
    </section>
  )
}
```
