Loading...
import { Attraction } from "@/registry/aliimam/components/attraction"
export function AttractionDemo() {
const colors = [
"bg-red-500",
"bg-blue-500",
"bg-green-500",
"bg-yellow-500",
"bg-purple-500",
"bg-pink-500",
"bg-indigo-500",
"bg-teal-500",
"bg-orange-500",
"bg-lime-500",
"bg-emerald-500",
"bg-cyan-500",
"bg-sky-500",
"bg-violet-500",
"bg-fuchsia-500",
"bg-rose-500",
"bg-amber-500",
"bg-slate-500",
"bg-gray-500",
"bg-zinc-500",
]
return (
<div className="mx-auto flex h-full w-full max-w-xl items-center justify-center overflow-hidden">
<Attraction>
{colors.map((color, index) => (
<div key={index} className={`h-16 w-16 ${color}`} />
))}
</Attraction>
</div>
)
}
Loading...
"use client"
import { useRef } from "react"
import {
Attraction,
AttractionRef,
} from "@/registry/aliimam/components/attraction"
import { Button } from "@/components/ui/button"
export function AttractionDemo() {
const skills = [
{ label: "REACT", color: "from-cyan-400 to-blue-500" },
{ label: "NEXT.JS", color: "from-neutral-700 to-black" },
{ label: "NODE", color: "from-green-400 to-emerald-600" },
{ label: "TYPESCRIPT", color: "from-blue-500 to-indigo-600" },
{ label: "JAVASCRIPT", color: "from-yellow-400 to-amber-500" },
{ label: "THREE.JS", color: "from-purple-500 to-violet-600" },
{ label: "RUST", color: "from-orange-500 to-red-500" },
{ label: "CSS", color: "from-sky-400 to-blue-600" },
{ label: "TAILWIND", color: "from-teal-400 to-cyan-500" },
{ label: "FRAMER", color: "from-pink-500 to-rose-500" },
{ label: "MOTION", color: "from-fuchsia-500 to-purple-600" },
{ label: "UX", color: "from-indigo-400 to-blue-500" },
{ label: "UI", color: "from-violet-400 to-purple-500" },
{ label: "API", color: "from-lime-400 to-green-500" },
{ label: "GRAPHQL", color: "from-pink-400 to-red-500" },
{ label: "DESIGN", color: "from-rose-400 to-pink-600" },
{ label: "GIT", color: "from-orange-400 to-red-600" },
{ label: "DOCKER", color: "from-blue-400 to-sky-600" },
]
const physicsRef = useRef<AttractionRef>(null)
return (
<div className="relative mx-auto flex h-full w-full max-w-4xl items-center justify-center overflow-hidden border">
<Button
size="lg"
className="absolute top-6 z-20"
onClick={() => physicsRef.current?.shake(0.9)}
>
Shake Skills 🚀
</Button>
<Attraction
ref={physicsRef}
gravity={{ x: 0, y: 0.05 }}
restitution={0.1}
friction={0.8}
>
{skills.map(({ label, color }) => (
<div
key={label}
className={`bg-linear-to-r ${color} px-6 py-2 text-sm font-semibold tracking-wide text-white`}
>
{label}
</div>
))}
</Attraction>
</div>
)
}
Loading...
import { Attraction } from "@/registry/aliimam/components/attraction"
export function AttractionDemo() {
const usernames = [
"shadcn",
"vercel",
"nextjs",
"tailwindlabs",
"typescript-lang",
"eslint",
"prettier",
"babel",
"webpack",
"rollup",
"vite",
"react",
]
return (
<div className="mx-auto flex h-full w-full max-w-xl items-center justify-center overflow-hidden">
<Attraction
gravity={{ x: 0, y: 0.6 }}
restitution={0.7}
frictionAir={0.02}
dragStiffness={0.3}
>
{usernames.map((username) => (
<div
key={username}
className="h-18 w-18 rounded-none bg-transparent shadow-none select-none"
>
<img
src={`https://github.com/${username}.png`}
alt={username}
className="pointer-events-none select-none"
draggable={false}
/>
</div>
))}
</Attraction>
</div>
)
}
Loading...
import { Attraction } from "@/registry/aliimam/components/attraction"
export function AttractionDemo() {
const ingredients = [
{ name: "🧅 Onion", weight: "200g" },
{ name: "🧄 Garlic", weight: "4 cloves" },
{ name: "🍅 Tomato", weight: "3 pcs" },
{ name: "🌿 Basil", weight: "handful" },
{ name: "🫒 Olive oil", weight: "3 tbsp" },
{ name: "🧂 Salt", weight: "to taste" },
{ name: "🌶 Chilli", weight: "1 dried" },
{ name: "🍝 Pasta", weight: "320g" },
{ name: "🧀 Pecorino", weight: "60g" },
]
return (
<div className="mx-auto flex h-full w-full max-w-xl items-center justify-center overflow-hidden">
<Attraction
gravity={{ x: 0, y: 1.2 }}
restitution={0.25}
friction={0.9}
frictionAir={0.02}
dragStiffness={0.4}
>
{ingredients.map((ing, i) => (
<div
key={ing.name}
data-x={`${10 + (i % 3) * 30}%`}
data-y={`${8 + Math.floor(i / 3) * 14}%`}
data-angle={String(Math.round(Math.random() * 20 - 10))}
data-friction="0.85"
data-restitution="0.2"
className="cursor-grab rounded-sm border border-[#d4b896] bg-[#fff8f0] px-4 py-2 whitespace-nowrap shadow-sm select-none"
>
<p className="font-serif text-base text-[#3d2b1f]">{ing.name}</p>
<p className="mt-0.5 font-mono text-[10px] text-[#b8977e]">
{ing.weight}
</p>
</div>
))}
</Attraction>
</div>
)
}
Loading...
"use client"
import {
ClaudeAI,
Cursor,
Gemini,
Github,
Google,
Grok,
OpenAI,
PerplexityAI,
Replicate,
Resend,
Suno,
YouTube,
} from "@aliimam/logos"
import { Attraction } from "@/registry/aliimam/components/attraction"
export function LogosAttraction() {
const logos = [
OpenAI,
ClaudeAI,
Replicate,
Cursor,
Gemini,
Github,
Grok,
Google,
Suno,
Resend,
YouTube,
PerplexityAI,
]
return (
<div className="mx-auto flex h-full w-full max-w-xl items-center justify-center overflow-hidden">
<Attraction gravity={{ x: 0, y: 0 }} gravityScale={0.1}>
{logos.map((Logo, i) => (
<div
key={i}
data-x={`${10 + (i % 4) * 22}%`}
data-y={`${10 + Math.floor(i / 4) * 28}%`}
data-shape="circle"
data-friction="0"
data-restitution="0.9"
data-friction-air="0.01"
className="flex h-20 w-20 items-center justify-center border bg-white select-none"
>
<Logo size={28} />
</div>
))}
</Attraction>
</div>
)
}
Installation
pnpm dlx shadcn@latest add https://aliimam.in/r/attraction.json
Usage
import { Attraction } from "@/components/ui/attraction"<Attraction>
<div className="h-16 w-16 bg-red-500" />
<div className="h-16 w-16 bg-blue-500" />
<div className="h-16 w-16 bg-green-500" />
</Attraction>Props
| Prop | Type | Default | Description |
|---|---|---|---|
gravity | {x: number, y: number} | {x:0,y:1} | Physics world gravity |
className | string | "" | Custom class for container |
paused | boolean | false | Pause physics without clearing |
walls | boolean | true | Toggle world walls |
friction | number | 0.2 | Default friction for bodies |
restitution | number | 0.5 | Default bounciness |
stiffness | number | 0.2 | Mouse drag stiffness |
background | string | "transparent" | Canvas background |
width | number | container width | Force canvas width |
height | number | container height | Force canvas height |
savd













