Baktiyar Ablaikhan

Animated List

Source
Magic UI ↗
Author
Magic UI
Licence
MIT

Loading…

Notifications spring in one by one at the top of a stack.

Code

"use client";

import { useEffect, useState } from "react";

import { cn } from "@/lib/utils";
import { AnimatedList } from "@/lab/magicui/ui/animated-list";

interface Item {
  name: string;
  description: string;
  icon: string;
  color: string;
  time: string;
}

const base: Item[] = [
  { name: "Payment received", description: "Magic UI", time: "15m ago", icon: "💸", color: "#00C9A7" },
  { name: "User signed up", description: "Magic UI", time: "10m ago", icon: "👤", color: "#FFB800" },
  { name: "New message", description: "Magic UI", time: "5m ago", icon: "💬", color: "#FF3D71" },
  { name: "New event", description: "Magic UI", time: "2m ago", icon: "🗞️", color: "#1E86FF" },
];

const notifications = Array.from({ length: 10 }, () => base).flat();

const Notification = ({ name, description, icon, color, time }: Item) => {
  return (
    <figure
      className={cn(
        "relative mx-auto min-h-fit w-full max-w-[400px] cursor-pointer overflow-hidden rounded-2xl p-3",
        "transition-all duration-200 ease-in-out hover:scale-[103%]",
        "bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
        "transform-gpu dark:bg-transparent dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] dark:backdrop-blur-md dark:[border:1px_solid_rgba(255,255,255,.1)]",
      )}
    >
      <div className="flex flex-row items-center gap-3">
        <div className="flex size-10 shrink-0 items-center justify-center rounded-2xl" style={{ backgroundColor: color }}>
          <span className="text-lg">{icon}</span>
        </div>
        <div className="flex flex-col overflow-hidden">
          <figcaption className="flex flex-row items-center text-lg font-medium whitespace-pre text-neutral-950 dark:text-white">
            <span className="text-sm sm:text-base">{name}</span>
            <span className="mx-1">·</span>
            <span className="text-xs text-gray-500">{time}</span>
          </figcaption>
          <p className="text-sm font-normal text-neutral-600 dark:text-white/60">{description}</p>
        </div>
      </div>
    </figure>
  );
};

export default function AnimatedListDemo() {
  // The list stops after the last item; start over so the card keeps moving.
  const [run, setRun] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setRun((r) => r + 1), notifications.length * 1000 + 2000);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="relative flex h-full w-full flex-col overflow-hidden p-3">
      <AnimatedList key={run}>
        {notifications.map((item, idx) => (
          <Notification {...item} key={idx} />
        ))}
      </AnimatedList>
      <div className="pointer-events-none absolute inset-x-0 bottom-0 h-1/4 bg-gradient-to-t from-[#fbfaf7]"></div>
    </div>
  );
}
Prompt

Build "Animated List", a layout animation. What it does: Notifications spring in one by one at the top of a stack. Write it as a single self-contained React component in TypeScript that fills its parent (w-full h-full), styled with Tailwind CSS, using motion. Technique: the Motion library. It sits on a light background, in a 16:10 frame. Respect prefers-reduced-motion with a still state, pause work when the element is off screen, and keep it accessible. Original: https://magicui.design/docs/components/animated-list (Magic UI, MIT).