Features 2
Features 2 é um componente de layout para React e Tailwind CSS, da biblioteca SmoothUI, com licença MIT. Copia e cola no teu projeto.
SmoothUI
MIT
layout
O que é
Features 2 é um componente de layout para React e Tailwind CSS, da biblioteca SmoothUI, com licença MIT. Copia e cola no teu projeto.
Como usar
Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.
Cinco cópias grátis por mês. Sem cartão.
Precisa de
npm i motion
Licença
Este componente vem de SmoothUI e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.
Features 2
"use client";
import { cn } from "@/lib/utils";
import { motion, useReducedMotion } from "motion/react";
const SPRING = {
bounce: 0.1,
duration: 0.25,
type: "spring" as const,
};
const features = [
{
accent: true,
description:
"Real-time analytics dashboard with customizable metrics and beautiful visualizations.",
span: "col-span-2 row-span-2",
title: "Smart Analytics",
// The lead cell of a bento is two columns by two rows. Without something to
// look at, that is a quarter of the section spent on two lines of text.
visual: "analytics" as const,
},
{
description:
"Work together in real-time with built-in commenting and sharing.",
span: "col-span-1",
title: "Team Collaboration",
},
{
description: "RESTful API with comprehensive documentation and SDKs.",
span: "col-span-1",
title: "API First",
},
{
description: "Lightning-fast delivery from edge locations worldwide.",
span: "col-span-1",
title: "Global CDN",
},
{
description:
"Enterprise-grade security with SOC 2 compliance and encryption.",
span: "col-span-1",
title: "Security",
},
];
/** Bars of a plausible-looking week. Fixed, so the panel never reflows. */
const SERIES = [
{ id: "w1", value: 38 },
{ id: "w2", value: 52 },
{ id: "w3", value: 44 },
{ id: "w4", value: 66 },
{ id: "w5", value: 58 },
{ id: "w6", value: 74 },
{ id: "w7", value: 62 },
{ id: "w8", value: 88 },
{ id: "w9", value: 71 },
{ id: "w10", value: 96 },
{ id: "w11", value: 84 },
{ id: "w12", value: 100 },
];
const PEAK_ID = "w12";
const BAR_STAGGER = 0.03;
/**
* A small analytics panel for the bento's lead cell.
*
* Every element is real markup on the page's own tokens, so it follows the
* theme into dark mode and stays sharp at any pixel density — and the bars can
* grow on entry, which is the point of the library it ships with.
*/
const AnalyticsPanel = () => {
const shouldReduceMotion = useReducedMotion();
return (
<div className="flex h-full flex-col gap-4 rounded-xl border bg-background p-5">
<div className="flex items-baseline justify-between gap-3">
<div>
<p className="text-foreground/60 text-xs">Revenue this month</p>
<p className="font-semibold text-2xl text-foreground tabular-nums">
$48,219
</p>
</div>
<span className="rounded-full bg-foreground/5 px-2 py-0.5 font-medium text-foreground/70 text-xs tabular-nums">
+12.4%
</span>
</div>
<div
aria-hidden="true"
className="flex min-h-0 flex-1 items-end gap-1.5 sm:gap-2"
>
{SERIES.map(({ id, value }, index) => (
<motion.span
className={cn(
"flex-1 origin-bottom rounded-t-sm",
id === PEAK_ID ? "bg-foreground" : "bg-foreground/15"
)}
initial={shouldReduceMotion ? { scaleY: 1 } : { scaleY: 0 }}
key={id}
style={{ height: `${value}%` }}
transition={
shouldReduceMotion
? { duration: 0 }
: { ...SPRING, delay: index * BAR_STAGGER }
}
viewport={{ margin: "-80px", once: true }}
whileInView={{ scaleY: 1 }}
/>
))}
</div>
</div>
);
};
export function FeaturesBento() {
const shouldReduceMotion = useReducedMotion();
return (
<section aria-labelledby="features-bento-heading">
<div className="py-24 md:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="mx-auto mb-16 max-w-2xl text-center">
<h2
className="text-balance font-bold text-3xl tracking-tight md:text-4xl"
id="features-bento-heading"
>
Built for modern teams
</h2>
<p className="mt-4 text-foreground/70 text-lg">
A complete platform with everything you need to ship faster.
</p>
</div>
<div className="grid auto-rows-[180px] grid-cols-2 gap-4 md:grid-cols-4">
{features.map((feature, index) => (
<motion.div
className={cn(
"flex flex-col justify-end overflow-hidden rounded-xl border p-6",
feature.accent
? "bg-foreground/5 ring-1 ring-foreground/10"
: "bg-background",
feature.span
)}
initial={
shouldReduceMotion
? { opacity: 1 }
: { opacity: 0, scale: 0.95 }
}
key={feature.title}
transition={
shouldReduceMotion
? { duration: 0 }
: { ...SPRING, delay: index * 0.05 }
}
viewport={{ margin: "-100px", once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, scale: 1 }
}
>
{feature.visual === "analytics" && (
// Drawn, not photographed. A screenshot would be fixed to one
// theme, blur on a retina screen and weigh more than the whole
// block; markup inherits the page's colours and stays sharp.
<div className="-mx-6 -mt-6 mb-4 min-h-0 flex-1 overflow-hidden">
<AnalyticsPanel />
</div>
)}
<h3 className="mb-1 font-semibold text-foreground">
{feature.title}
</h3>
<p className="text-foreground/70 text-sm leading-relaxed">
{feature.description}
</p>
</motion.div>
))}
</div>
</div>
</div>
</section>
);
}
export default FeaturesBento;