feat: add LighthouseMetrics component to display quality and performance scores on the homepage.
This commit is contained in:
1 parent
d00258dcd1
commit
9d779390e5
2 files changed
+111
No files matched your search
@@ -0,0 +1,109 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
@@ -10,6 +10,7 @@ const Experience = dynamic(() => import("./components/Experience"));
|
||||
const WorkModalities = dynamic(() => import("./components/WorkModalities").then(mod => mod.WorkModalities));
|
||||
const Contact = dynamic(() => import("./components/Contact"));
|
||||
const Projects = dynamic(() => import("./components/Projects"));
|
||||
const LighthouseMetrics = dynamic(() => import("./components/LighthouseMetrics"));
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@@ -24,6 +25,7 @@ export default function Home() {
|
||||
>
|
||||
<Projects />
|
||||
</Suspense>
|
||||
<LighthouseMetrics />
|
||||
<WorkModalities />
|
||||
<Contact />
|
||||
</main>
|
||||
|
||||
Reference in new issue
Block a user