110 lines
4.2 KiB
TypeScript
110 lines
4.2 KiB
TypeScript
"use client";
|
|
|
|
import { motion, useReducedMotion } from "framer-motion";
|
|
import { Section } from "./Section";
|
|
|
|
const METRICS = [
|
|
{
|
|
score: 100,
|
|
label: "Performance",
|
|
description:
|
|
"Cada milissegundo conta. Tempos de carregamento instantâneos retêm mais usuários, aumentam a conversão e garantem que a interface pareça nativa.",
|
|
},
|
|
{
|
|
score: 100,
|
|
label: "Acessibilidade",
|
|
description:
|
|
"Acesso universal. Contraste perfeito, navegação por teclado e semântica adequada garantindo o uso por todos os perfis e mitigando riscos.",
|
|
},
|
|
{
|
|
score: 100,
|
|
label: "Best Practices",
|
|
description:
|
|
"Código seguro e moderno. Uso das APIs mais recentes, prevenção inteligente contra vulnerabilidades comuns e front-end limpo.",
|
|
},
|
|
{
|
|
score: 100,
|
|
label: "SEO",
|
|
description:
|
|
"Rankeamento orgânico superior. Estrutura impecável para indexação precisa pelos motores de busca e otimização para crawlers sociais.",
|
|
},
|
|
];
|
|
|
|
export default function LighthouseMetrics() {
|
|
const shouldReduceMotion = useReducedMotion();
|
|
|
|
return (
|
|
<Section
|
|
id="lighthouse"
|
|
eyebrow="Quality & Performance"
|
|
title="Engenharia de ponta a ponta: 100/100 não é vaidade."
|
|
description="Sistemas não podem apenas parecer bons. Eles precisam ser consistentes, rápidos, acessíveis, seguros e fáceis de processar. Entregar métricas máximas significa que a fundação técnica do projeto está preparada para escalar sem fricção."
|
|
dark
|
|
disableChildrenAnimation={false}
|
|
>
|
|
<div className="mt-sp-4 grid grid-cols-1 gap-sp-4 md:grid-cols-2 lg:grid-cols-4">
|
|
{METRICS.map((metric, i) => (
|
|
<motion.article
|
|
key={metric.label}
|
|
className="ds-card flex flex-col items-center gap-sp-6 text-center lg:items-start lg:text-left"
|
|
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 20 }}
|
|
whileInView={
|
|
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
|
|
}
|
|
viewport={{ once: true, amount: 0.2 }}
|
|
transition={{ duration: 0.6, delay: i * 0.1 }}
|
|
>
|
|
{/* The Ring Container */}
|
|
<div className="relative flex h-[100px] w-[100px] shrink-0 items-center justify-center rounded-full bg-[rgba(0,0,0,0.3)] shadow-inner">
|
|
<svg
|
|
className="absolute inset-0 h-full w-full rotate-[-90deg] drop-shadow-[0_0_12px_rgba(12,206,107,0.35)]"
|
|
viewBox="0 0 36 36"
|
|
aria-hidden="true"
|
|
>
|
|
{/* Background Track */}
|
|
<path
|
|
className="text-[rgba(255,255,255,0.1)]"
|
|
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2.5"
|
|
/>
|
|
{/* Animated Value Track */}
|
|
<motion.path
|
|
className="text-[#0cce6b]"
|
|
initial={{ strokeDasharray: "0, 100" }}
|
|
whileInView={{ strokeDasharray: `${metric.score}, 100` }}
|
|
viewport={{ once: true }}
|
|
transition={{
|
|
duration: 1.5,
|
|
ease: "easeOut",
|
|
delay: i * 0.15 + 0.2,
|
|
}}
|
|
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2.5"
|
|
strokeLinecap="round"
|
|
/>
|
|
</svg>
|
|
<div className="font-mono text-[24px] font-bold tracking-tight text-[#0cce6b]">
|
|
{metric.score}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Content Details */}
|
|
<div className="flex-1">
|
|
<h3 className="font-display text-[17px] font-bold text-[rgba(255,255,255,0.92)]">
|
|
{metric.label}
|
|
</h3>
|
|
<p className="mt-sp-3 text-[13px] leading-[1.8] text-[rgba(255,255,255,.75)]">
|
|
{metric.description}
|
|
</p>
|
|
</div>
|
|
</motion.article>
|
|
))}
|
|
</div>
|
|
</Section>
|
|
);
|
|
}
|