An ops assistant's integrations section: twelve tool tiles solve themselves into place round the headline, then drift. Each one is a link.
registry/demos/slide-puzzle-demo.tsx
import * as React from "react";
import { SlidePuzzle, type SlidePuzzleTile } from "@/components/ui/slide-puzzle";
function Glyph({ children }: { children: React.ReactNode }) {
return (
<svg viewBox="0 0 24 24" className="size-6" aria-hidden>
{children}
</svg>
);
}
// Okto, an ops assistant, and twelve of the tools it connects to. Every name and mark is made up.
const TILES: SlidePuzzleTile[] = [
{
id: "ember",
label: "Ember · Email",
href: "#ember",
icon: (
<Glyph>
<rect x="3" y="5" width="18" height="14" rx="3.2" fill="#ff5a1f" />
<path d="M6 8.6l6 4.4 6-4.4" fill="none" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
</Glyph>
),
},
{
id: "fathom",
label: "Fathom · Postgres",
href: "#fathom",
icon: (
<Glyph>
<path d="M5 6v12c0 1.6 3.1 2.9 7 2.9s7-1.3 7-2.9V6z" fill="#2563eb" />
<ellipse cx="12" cy="6" rx="7" ry="2.9" fill="#60a5fa" />
<path d="M5 12c0 1.6 3.1 2.9 7 2.9s7-1.3 7-2.9" fill="none" stroke="#bfdbfe" strokeWidth="1.3" />
</Glyph>
),
},
{
id: "relay",
label: "Relay · Team chat",
href: "#relay",
icon: (
<Glyph>
<path d="M4 7a4 4 0 0 1 4-4h8a4 4 0 0 1 4 4v5.5a4 4 0 0 1-4 4h-5L6.5 20.5v-4.2A4 4 0 0 1 4 12.5z" fill="#10b981" />
<circle cx="8.6" cy="9.8" r="1.2" fill="#fff" />
<circle cx="12" cy="9.8" r="1.2" fill="#fff" />
<circle cx="15.4" cy="9.8" r="1.2" fill="#fff" />
</Glyph>
),
},
{
id: "cobalt",
label: "Cobalt · Payments",
href: "#cobalt",
icon: (
<Glyph>
<rect x="2.5" y="5" width="19" height="14" rx="3" fill="#4f46e5" />
<rect x="2.5" y="8.4" width="19" height="2.6" fill="#312e81" />
<rect x="5.2" y="14.2" width="5.5" height="2" rx="1" fill="#c7d2fe" />
</Glyph>
),
},
{
id: "lumen",
label: "Lumen · Warehouse",
href: "#lumen",
icon: (
<Glyph>
<circle cx="12" cy="12" r="4.6" fill="#f59e0b" />
<g stroke="#fbbf24" strokeWidth="2" strokeLinecap="round">
<path d="M12 2.8v2.2M12 19v2.2M2.8 12H5M19 12h2.2M5.5 5.5l1.6 1.6M16.9 16.9l1.6 1.6M18.5 5.5l-1.6 1.6M7.1 16.9l-1.6 1.6" />
</g>
</Glyph>
),
},
{
id: "tessera",
label: "Tessera · Docs",
href: "#tessera",
icon: (
<Glyph>
<rect x="3" y="3" width="8" height="8" rx="2.4" fill="#7c3aed" />
<rect x="13" y="3" width="8" height="8" rx="4" fill="#c084fc" />
<rect x="3" y="13" width="8" height="8" rx="4" fill="#c084fc" />
<rect x="13" y="13" width="8" height="8" rx="2.4" fill="#7c3aed" />
</Glyph>
),
},
{
id: "kiln",
label: "Kiln · Deploys",
href: "#kiln",
icon: (
<Glyph>
<path d="M12 2.4c.9 3.3 5.8 5.7 5.8 10.8a5.8 5.8 0 0 1-11.6 0c0-2.5 1.4-4.2 2.7-5.4.1 1.8 1 3 2.3 3.5-.6-3.2-.1-6.2.8-8.9z" fill="#ef4444" />
<path d="M12 13.3c.5 1.4 2.4 2.2 2.4 4.1a2.4 2.4 0 0 1-4.8 0c0-1.5 1.4-2.5 2.4-4.1z" fill="#fdba74" />
</Glyph>
),
},
{
id: "mosaic",
label: "Mosaic · Analytics",
href: "#mosaic",
icon: (
<Glyph>
<rect x="3.5" y="12" width="4.6" height="8.5" rx="1.6" fill="#f43f5e" />
<rect x="9.7" y="7.5" width="4.6" height="13" rx="1.6" fill="#fb923c" />
<rect x="15.9" y="3.5" width="4.6" height="17" rx="1.6" fill="#facc15" />
</Glyph>
),
},
{
id: "orbit",
label: "Orbit · Calendar",
href: "#orbit",
icon: (
<Glyph>
<circle cx="12" cy="12" r="7.6" fill="none" stroke="#0ea5e9" strokeWidth="2.2" />
<circle cx="12" cy="12" r="3" fill="#0ea5e9" />
<circle cx="18.4" cy="7.4" r="2.6" fill="#38bdf8" stroke="var(--slide-puzzle-tile)" strokeWidth="1.6" />
</Glyph>
),
},
{
id: "tide",
label: "Tide · Support",
href: "#tide",
icon: (
<Glyph>
<g fill="none" stroke="#06b6d4" strokeWidth="2.2" strokeLinecap="round">
<path d="M3 8c2.2 0 2.2-2 4.5-2S9.8 8 12 8s2.2-2 4.5-2S18.8 8 21 8" />
<path d="M3 13c2.2 0 2.2-2 4.5-2s2.3 2 4.5 2 2.2-2 4.5-2 2.3 2 4.5 2" opacity="0.7" />
<path d="M3 18c2.2 0 2.2-2 4.5-2s2.3 2 4.5 2 2.2-2 4.5-2 2.3 2 4.5 2" opacity="0.4" />
</g>
</Glyph>
),
},
{
id: "sprout",
label: "Sprout · CRM",
href: "#sprout",
icon: (
<Glyph>
<path d="M4.5 19.5C4.5 10.4 10.2 4.5 19.5 4.5c0 9.3-5.9 15-15 15z" fill="#65a30d" />
<path d="M5 19 13.8 10.2" stroke="#ecfccb" strokeWidth="1.6" strokeLinecap="round" />
</Glyph>
),
},
{
id: "quill",
label: "Quill · Notes",
href: "#quill",
icon: (
<Glyph>
<path d="M12 2.5 18.2 9 15 18H9L5.8 9z" fill="currentColor" />
<circle cx="12" cy="10.6" r="1.7" fill="var(--slide-puzzle-tile)" />
<path d="M12 12.4v5.4" stroke="var(--slide-puzzle-tile)" strokeWidth="1.3" />
<rect x="8.4" y="19.2" width="7.2" height="2.3" rx="1.15" fill="currentColor" />
</Glyph>
),
},
];
export function SlidePuzzleDemo() {
return (
<section className="w-full max-w-5xl overflow-hidden rounded-3xl border border-zinc-950/[0.06] bg-white text-zinc-950 dark:border-white/10 dark:bg-zinc-950 dark:text-white">
<SlidePuzzle tiles={TILES}>
<div className="flex max-w-md flex-col items-center text-center">
<span className="inline-flex items-center gap-1.5 rounded-full border border-zinc-950/10 bg-white px-2.5 py-1 text-xs font-medium text-zinc-600 dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-300">
<svg viewBox="0 0 16 16" className="size-3.5" aria-hidden>
<rect x="2" y="2" width="5" height="5" rx="1.4" fill="#ff5a1f" />
<rect x="9" y="2" width="5" height="5" rx="1.4" fill="currentColor" opacity="0.35" />
<rect x="2" y="9" width="5" height="5" rx="1.4" fill="currentColor" opacity="0.35" />
<rect x="9" y="9" width="5" height="5" rx="1.4" fill="currentColor" opacity="0.35" />
</svg>
Integrations
</span>
<h2 className="mt-4 text-balance text-3xl font-medium tracking-[-0.03em] sm:text-4xl">
Plugs into the stack you already run.
</h2>
<p className="mt-3 text-pretty text-[15px] leading-relaxed text-zinc-500 dark:text-zinc-400">
Okto reads from and writes to forty-two tools your team already pays for, each one set up in a click.
</p>
<div className="mt-6 flex flex-wrap items-center justify-center gap-2">
<a
href="#integrations"
className="inline-flex h-9 items-center gap-1 rounded-full bg-[#ff5a1f] px-4 text-sm font-medium text-white shadow-[inset_0_1px_0_rgb(255_255_255/0.25)] transition-colors hover:bg-[#f04d12] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ff5a1f]"
>
Browse integrations
<svg viewBox="0 0 16 16" className="size-3.5" aria-hidden>
<path d="M6 4l4 4-4 4" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</a>
<a
href="#request"
className="inline-flex h-9 items-center rounded-full border border-zinc-950/10 bg-white px-4 text-sm font-medium transition-colors hover:bg-zinc-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ff5a1f] dark:border-white/10 dark:bg-zinc-900 dark:hover:bg-zinc-800"
>
Request one
</a>
</div>
</div>
</SlidePuzzle>
</section>
);
}
Live preview, examples and props: https://keyframeui.com/components/slide-puzzle#slide-puzzle-demo