Bento Grid
- Source
- Aceternity UI ↗
- Author
- Aceternity UI
- Licence
- Aceternity UI licence (free component)
Loading…
A bento layout whose small illustrations shift, fill and swing on hover.
Code
"use client";
import { cn } from "@/lib/utils";
import React from "react";
import { BentoGrid, BentoGridItem } from "@/lab/aceternity/ui/bento-grid";
import {
IconBoxAlignRightFilled,
IconClipboardCopy,
IconFileBroken,
IconSignature,
IconTableColumn,
} from "@tabler/icons-react";
import { motion } from "motion/react";
import { Fit } from "./_fit";
// Fixed widths (the original uses Math.random() per render).
const BAR_WIDTHS = ["86%", "62%", "94%", "48%", "77%", "55%"];
const AVATAR = "https://assets.aceternity.com/avatars/manu.webp";
export default function BentoGridThirdDemo() {
return (
<Fit width={940} height={700}>
<div className="flex h-full w-full items-center">
<BentoGrid className="max-w-4xl mx-auto auto-rows-[20rem]">
{items.map((item, i) => (
<BentoGridItem
key={i}
title={item.title}
description={item.description}
header={item.header}
className={cn("[&>p:text-lg]", item.className)}
icon={item.icon}
/>
))}
</BentoGrid>
</div>
</Fit>
);
}
const SkeletonOne = () => {
const variants = {
initial: {
x: 0,
},
animate: {
x: 10,
rotate: 5,
transition: {
duration: 0.2,
},
},
};
const variantsSecond = {
initial: {
x: 0,
},
animate: {
x: -10,
rotate: -5,
transition: {
duration: 0.2,
},
},
};
return (
<motion.div
initial="initial"
whileHover="animate"
className="flex flex-1 w-full h-full min-h-[6rem] bg-[radial-gradient(rgba(0,0,0,0.2)_1px,transparent_1px)] dark:bg-[radial-gradient(rgba(255,255,255,0.2)_1px,transparent_1px)] bg-[size:16px_16px] flex-col space-y-2"
>
<motion.div
variants={variants}
className="flex flex-row rounded-full border border-neutral-100 dark:border-white/[0.2] p-2 items-center space-x-2 bg-white dark:bg-black"
>
<div className="h-6 w-6 rounded-full bg-gradient-to-r from-pink-500 to-violet-500 shrink-0" />
<div className="w-full bg-gray-100 h-4 rounded-full dark:bg-neutral-900" />
</motion.div>
<motion.div
variants={variantsSecond}
className="flex flex-row rounded-full border border-neutral-100 dark:border-white/[0.2] p-2 items-center space-x-2 w-3/4 ml-auto bg-white dark:bg-black"
>
<div className="w-full bg-gray-100 h-4 rounded-full dark:bg-neutral-900" />
<div className="h-6 w-6 rounded-full bg-gradient-to-r from-pink-500 to-violet-500 shrink-0" />
</motion.div>
<motion.div
variants={variants}
className="flex flex-row rounded-full border border-neutral-100 dark:border-white/[0.2] p-2 items-center space-x-2 bg-white dark:bg-black"
>
<div className="h-6 w-6 rounded-full bg-gradient-to-r from-pink-500 to-violet-500 shrink-0" />
<div className="w-full bg-gray-100 h-4 rounded-full dark:bg-neutral-900" />
</motion.div>
</motion.div>
);
};
const SkeletonTwo = () => {
const variants = {
initial: {
width: 0,
},
animate: {
width: "100%",
transition: {
duration: 0.2,
},
},
hover: {
width: ["0%", "100%"],
transition: {
duration: 2,
},
},
};
const arr = new Array(6).fill(0);
return (
<motion.div
initial="initial"
animate="animate"
whileHover="hover"
className="flex flex-1 w-full h-full min-h-[6rem] bg-[radial-gradient(rgba(0,0,0,0.2)_1px,transparent_1px)] dark:bg-[radial-gradient(rgba(255,255,255,0.2)_1px,transparent_1px)] bg-[size:16px_16px] flex-col space-y-2"
>
{arr.map((_, i) => (
<motion.div
key={"skelenton-two" + i}
variants={variants}
style={{
maxWidth: BAR_WIDTHS[i],
}}
className="flex flex-row rounded-full border border-neutral-100 dark:border-white/[0.2] p-2 items-center space-x-2 bg-neutral-100 dark:bg-black w-full h-4"
></motion.div>
))}
</motion.div>
);
};
const SkeletonThree = () => {
const variants = {
initial: {
backgroundPosition: "0 50%",
},
animate: {
backgroundPosition: ["0, 50%", "100% 50%", "0 50%"],
},
};
return (
<motion.div
initial="initial"
animate="animate"
variants={variants}
transition={{
duration: 5,
repeat: Infinity,
repeatType: "reverse",
}}
className="flex flex-1 w-full h-full min-h-[6rem] rounded-lg flex-col space-y-2"
style={{
background:
"linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab)",
backgroundSize: "400% 400%",
}}
>
<motion.div className="h-full w-full rounded-lg"></motion.div>
</motion.div>
);
};
const SkeletonFour = () => {
const first = {
initial: {
x: 20,
rotate: -5,
},
hover: {
x: 0,
rotate: 0,
},
};
const second = {
initial: {
x: -20,
rotate: 5,
},
hover: {
x: 0,
rotate: 0,
},
};
return (
<motion.div
initial="initial"
animate="animate"
whileHover="hover"
className="flex flex-1 w-full h-full min-h-[6rem] bg-[radial-gradient(rgba(0,0,0,0.2)_1px,transparent_1px)] dark:bg-[radial-gradient(rgba(255,255,255,0.2)_1px,transparent_1px)] bg-[size:16px_16px] flex-row space-x-2"
>
<motion.div
variants={first}
className="h-full w-1/3 rounded-2xl bg-white p-4 dark:bg-black dark:border-white/[0.1] border border-neutral-200 flex flex-col items-center justify-center"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={AVATAR}
alt="avatar"
height="100"
width="100"
className="rounded-full h-10 w-10"
/>
<p className="sm:text-sm text-xs text-center font-semibold text-neutral-500 mt-4">
Just code in Vanilla Javascript
</p>
<p className="border border-red-500 bg-red-100 dark:bg-red-900/20 text-red-600 text-xs rounded-full px-2 py-0.5 mt-4">
Delusional
</p>
</motion.div>
<motion.div className="h-full relative z-20 w-1/3 rounded-2xl bg-white p-4 dark:bg-black dark:border-white/[0.1] border border-neutral-200 flex flex-col items-center justify-center">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={AVATAR}
alt="avatar"
height="100"
width="100"
className="rounded-full h-10 w-10"
/>
<p className="sm:text-sm text-xs text-center font-semibold text-neutral-500 mt-4">
Tailwind CSS is cool, you know
</p>
<p className="border border-green-500 bg-green-100 dark:bg-green-900/20 text-green-600 text-xs rounded-full px-2 py-0.5 mt-4">
Sensible
</p>
</motion.div>
<motion.div
variants={second}
className="h-full w-1/3 rounded-2xl bg-white p-4 dark:bg-black dark:border-white/[0.1] border border-neutral-200 flex flex-col items-center justify-center"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={AVATAR}
alt="avatar"
height="100"
width="100"
className="rounded-full h-10 w-10"
/>
<p className="sm:text-sm text-xs text-center font-semibold text-neutral-500 mt-4">
I love angular, RSC, and Redux.
</p>
<p className="border border-orange-500 bg-orange-100 dark:bg-orange-900/20 text-orange-600 text-xs rounded-full px-2 py-0.5 mt-4">
Helpless
</p>
</motion.div>
</motion.div>
);
};
const SkeletonFive = () => {
const variants = {
initial: {
x: 0,
},
animate: {
x: 10,
rotate: 5,
transition: {
duration: 0.2,
},
},
};
const variantsSecond = {
initial: {
x: 0,
},
animate: {
x: -10,
rotate: -5,
transition: {
duration: 0.2,
},
},
};
return (
<motion.div
initial="initial"
whileHover="animate"
className="flex flex-1 w-full h-full min-h-[6rem] bg-[radial-gradient(rgba(0,0,0,0.2)_1px,transparent_1px)] dark:bg-[radial-gradient(rgba(255,255,255,0.2)_1px,transparent_1px)] bg-[size:16px_16px] flex-col space-y-2"
>
<motion.div
variants={variants}
className="flex flex-row rounded-2xl border border-neutral-100 dark:border-white/[0.2] p-2 items-start space-x-2 bg-white dark:bg-black"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={AVATAR}
alt="avatar"
height="100"
width="100"
className="rounded-full h-10 w-10"
/>
<p className="text-xs text-neutral-500">
There are a lot of cool framerworks out there like React, Angular,
Vue, Svelte that can make your life ....
</p>
</motion.div>
<motion.div
variants={variantsSecond}
className="flex flex-row rounded-full border border-neutral-100 dark:border-white/[0.2] p-2 items-center justify-end space-x-2 w-3/4 ml-auto bg-white dark:bg-black"
>
<p className="text-xs text-neutral-500">Use PHP.</p>
<div className="h-6 w-6 rounded-full bg-gradient-to-r from-pink-500 to-violet-500 shrink-0" />
</motion.div>
</motion.div>
);
};
const items = [
{
title: "AI Content Generation",
description: (
<span className="text-sm">
Experience the power of AI in generating unique content.
</span>
),
header: <SkeletonOne />,
className: "col-span-1",
icon: <IconClipboardCopy className="h-4 w-4 text-neutral-500" />,
},
{
title: "Automated Proofreading",
description: (
<span className="text-sm">
Let AI handle the proofreading of your documents.
</span>
),
header: <SkeletonTwo />,
className: "col-span-1",
icon: <IconFileBroken className="h-4 w-4 text-neutral-500" />,
},
{
title: "Contextual Suggestions",
description: (
<span className="text-sm">
Get AI-powered suggestions based on your writing context.
</span>
),
header: <SkeletonThree />,
className: "col-span-1",
icon: <IconSignature className="h-4 w-4 text-neutral-500" />,
},
{
title: "Sentiment Analysis",
description: (
<span className="text-sm">
Understand the sentiment of your text with AI analysis.
</span>
),
header: <SkeletonFour />,
className: "col-span-2",
icon: <IconTableColumn className="h-4 w-4 text-neutral-500" />,
},
{
title: "Text Summarization",
description: (
<span className="text-sm">
Summarize your lengthy documents with AI technology.
</span>
),
header: <SkeletonFive />,
className: "col-span-1",
icon: <IconBoxAlignRightFilled className="h-4 w-4 text-neutral-500" />,
},
];
Prompt
Build "Bento Grid", a layout animation. What it does: A bento layout whose small illustrations shift, fill and swing on hover. Write it as a single self-contained React component in TypeScript that fills its parent (w-full h-full), styled with Tailwind CSS, using @tabler/icons-react, 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://ui.aceternity.com/components/bento-grid (Aceternity UI, Aceternity UI licence (free component)).