Align the notebook browser with the physical LCD, refresh real project captures and navigation, simplify service content, and stabilize hero hydration.
This commit is contained in:
1 parent
e2b20bc699
commit
8b12e51bc5
35 files changed
+2359
-1299
No files matched your search
@@ -1,115 +1,343 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "motion/react";
|
||||
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
|
||||
import { Section } from "./Section";
|
||||
import { useState } from "react";
|
||||
|
||||
const METRICS = [
|
||||
{
|
||||
score: 100,
|
||||
id: "performance",
|
||||
label: "Performance",
|
||||
code: "LCP / INP / CLS",
|
||||
title: "O essencial chega primeiro.",
|
||||
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.",
|
||||
"O carregamento começa pelo que importa. Imagens, fontes e JavaScript precisam trabalhar a favor da experiência, não competir com ela.",
|
||||
checks: [
|
||||
"Conteúdo principal com prioridade de carregamento",
|
||||
"Dimensões reservadas para evitar saltos de layout",
|
||||
"JavaScript carregado onde faz diferença",
|
||||
],
|
||||
caption: "Em foco: o conteúdo principal e a estabilidade da página.",
|
||||
},
|
||||
{
|
||||
score: 100,
|
||||
id: "accessibility",
|
||||
label: "Acessibilidade",
|
||||
code: "TECLADO / FOCO / SEMÂNTICA",
|
||||
title: "A interface inclui mais pessoas.",
|
||||
description:
|
||||
"Acesso universal. Contraste perfeito, navegação por teclado e semântica adequada garantindo o uso por todos os perfis e mitigando riscos.",
|
||||
"Uma boa interface não exige um jeito único de navegar. Teclado, leitores de tela e preferências de movimento fazem parte do projeto.",
|
||||
checks: [
|
||||
"Foco visível e navegação por teclado",
|
||||
"Controles com nomes e estrutura semântica",
|
||||
"Contraste legível e movimento reduzido",
|
||||
],
|
||||
caption:
|
||||
"Em foco: controles reconhecíveis e um caminho claro pelo teclado.",
|
||||
},
|
||||
{
|
||||
score: 100,
|
||||
label: "Best Practices",
|
||||
id: "practices",
|
||||
label: "Boas práticas",
|
||||
code: "HTTPS / PRIVACIDADE / DEPENDÊNCIAS",
|
||||
title: "Confiança também se constrói.",
|
||||
description:
|
||||
"Código seguro e moderno. Uso das APIs mais recentes, prevenção inteligente contra vulnerabilidades comuns e front-end limpo.",
|
||||
"Cuidar da entrega também é cuidar dos dados, das dependências e dos limites do navegador. Segurança não se resume a uma nota.",
|
||||
checks: [
|
||||
"Conexões seguras e políticas do navegador",
|
||||
"Analytics condicionado ao consentimento",
|
||||
"Dependências verificadas e atualizadas",
|
||||
],
|
||||
caption: "Em foco: a conexão e os limites de acesso aos dados.",
|
||||
},
|
||||
{
|
||||
score: 100,
|
||||
id: "seo",
|
||||
label: "SEO",
|
||||
code: "HTML / METADATA / INDEXAÇÃO",
|
||||
title: "Conteúdo pronto para ser encontrado.",
|
||||
description:
|
||||
"Rankeamento orgânico superior. Estrutura impecável para indexação precisa pelos motores de busca e otimização para crawlers sociais.",
|
||||
"Estrutura antes de promessas. Conteúdo claro, títulos coerentes e dados estruturados ajudam pessoas e buscadores a entender cada página.",
|
||||
checks: [
|
||||
"Títulos, descrições e URLs canônicas",
|
||||
"HTML semântico e conteúdo rastreável",
|
||||
"Sitemap e dados estruturados factuais",
|
||||
],
|
||||
caption: "Em foco: a estrutura que dá contexto ao conteúdo.",
|
||||
},
|
||||
];
|
||||
|
||||
const BLUEPRINT = (
|
||||
<svg
|
||||
className="quality-blueprint"
|
||||
viewBox="0 0 640 440"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<g className="quality-dimensions">
|
||||
<path d="M64 68V48m512 20V48M64 58h512M44 86H24m20 284H24M34 86v284" />
|
||||
<path d="m60 54 8 8m504-8 8 8M30 82l8 8m-8 276 8 8" />
|
||||
<text x="304" y="47">
|
||||
RESPONSIVE
|
||||
</text>
|
||||
<path d="M64 390v18m512-18v18M64 400h180m152 0h180" />
|
||||
<text x="272" y="405">
|
||||
WEB / BLUEPRINT
|
||||
</text>
|
||||
</g>
|
||||
<rect
|
||||
className="quality-browser-frame"
|
||||
x="64"
|
||||
y="86"
|
||||
width="512"
|
||||
height="284"
|
||||
rx="8"
|
||||
/>
|
||||
<path className="quality-wire" d="M64 123h512" />
|
||||
<g className="quality-browser-dots">
|
||||
<circle cx="83" cy="105" r="3" />
|
||||
<circle cx="95" cy="105" r="3" />
|
||||
<circle cx="107" cy="105" r="3" />
|
||||
</g>
|
||||
<rect
|
||||
className="quality-wire"
|
||||
x="142"
|
||||
y="96"
|
||||
width="350"
|
||||
height="18"
|
||||
rx="3"
|
||||
/>
|
||||
<text className="quality-address" x="158" y="109">
|
||||
evandro.dev.br
|
||||
</text>
|
||||
<path className="quality-wire" d="M539 100h15m-15 5h15m-15 5h15" />
|
||||
<text className="quality-browser-heading" x="96" y="176">
|
||||
Engenharia
|
||||
</text>
|
||||
<text className="quality-browser-heading" x="96" y="203">
|
||||
que funciona.
|
||||
</text>
|
||||
<path className="quality-wire" d="M96 222h170M96 230h130" />
|
||||
<rect
|
||||
className="quality-browser-button"
|
||||
x="96"
|
||||
y="254"
|
||||
width="142"
|
||||
height="32"
|
||||
rx="2"
|
||||
/>
|
||||
<text className="quality-browser-button-label" x="112" y="274">
|
||||
EXPLORAR PROJETO
|
||||
</text>
|
||||
<rect
|
||||
className="quality-wire"
|
||||
x="330"
|
||||
y="144"
|
||||
width="216"
|
||||
height="142"
|
||||
rx="3"
|
||||
/>
|
||||
<g className="quality-wire">
|
||||
<path d="m438 164 58 33v55l-58 33-58-33v-55zM380 197l58 33 58-33m-58 33v55m0-121v66" />
|
||||
<path d="m409 181 58 33v55m-58 0v-55l58-33" />
|
||||
<path d="M96 306h450M96 324h113m-113 10h80m84-10h113m-113 10h80m84-10h122m-122 10h80" />
|
||||
</g>
|
||||
<g className="quality-layer" data-layer="performance">
|
||||
<rect
|
||||
className="quality-highlight"
|
||||
x="318"
|
||||
y="133"
|
||||
width="240"
|
||||
height="165"
|
||||
rx="4"
|
||||
/>
|
||||
<path className="quality-trace" pathLength="1" d="M558 216h43V59h-91" />
|
||||
<circle cx="558" cy="216" r="4" />
|
||||
<text x="393" y="63">
|
||||
LCP / CONTEÚDO
|
||||
</text>
|
||||
<path d="M330 299v12m216-12v12m-216-6h216" />
|
||||
<rect
|
||||
className="quality-annotation"
|
||||
x="368"
|
||||
y="311"
|
||||
width="154"
|
||||
height="18"
|
||||
/>
|
||||
<text x="376" y="323">
|
||||
ESPAÇO RESERVADO
|
||||
</text>
|
||||
</g>
|
||||
<g className="quality-layer" data-layer="accessibility">
|
||||
<rect
|
||||
className="quality-highlight quality-focus-frame"
|
||||
x="88"
|
||||
y="246"
|
||||
width="158"
|
||||
height="48"
|
||||
rx="5"
|
||||
/>
|
||||
<path className="quality-trace" pathLength="1" d="M88 270H50v149h20" />
|
||||
<circle cx="88" cy="270" r="4" />
|
||||
<text x="79" y="424">
|
||||
TAB / FOCO VISÍVEL
|
||||
</text>
|
||||
<rect x="260" y="259" width="45" height="24" rx="3" />
|
||||
<text x="270" y="275">
|
||||
TAB
|
||||
</text>
|
||||
</g>
|
||||
<g className="quality-layer" data-layer="practices">
|
||||
<rect
|
||||
className="quality-highlight"
|
||||
x="134"
|
||||
y="91"
|
||||
width="366"
|
||||
height="28"
|
||||
rx="4"
|
||||
/>
|
||||
<path className="quality-trace" pathLength="1" d="M500 105h101v314h-90" />
|
||||
<circle cx="500" cy="105" r="4" />
|
||||
<path
|
||||
className="quality-highlight"
|
||||
d="m499 220 36 13v32c0 25-36 41-36 41s-36-16-36-41v-32z"
|
||||
/>
|
||||
<rect x="487" y="255" width="24" height="20" rx="2" />
|
||||
<path d="M491 255v-7a8 8 0 0 1 16 0v7m-8 6v7" />
|
||||
<text x="290" y="424">
|
||||
HTTPS / CONSENTIMENTO
|
||||
</text>
|
||||
</g>
|
||||
<g className="quality-layer" data-layer="seo">
|
||||
<rect
|
||||
className="quality-highlight"
|
||||
x="85"
|
||||
y="144"
|
||||
width="224"
|
||||
height="95"
|
||||
rx="3"
|
||||
/>
|
||||
<path className="quality-trace" pathLength="1" d="M85 159H48V58h135" />
|
||||
<circle cx="85" cy="159" r="4" />
|
||||
<text x="74" y="49">
|
||||
HTML / SEMÂNTICA
|
||||
</text>
|
||||
<text x="99" y="140">
|
||||
<h1>
|
||||
</text>
|
||||
<path d="M331 317h213v29H331z" />
|
||||
<text x="350" y="337">
|
||||
<article> / <nav>
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default function LighthouseMetrics() {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const [active, setActive] = useState(METRICS[0]);
|
||||
|
||||
return (
|
||||
<Section
|
||||
<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}
|
||||
className="ds-section ds-section-dark quality-lab"
|
||||
aria-labelledby="quality-title"
|
||||
data-inspection={active.id}
|
||||
>
|
||||
<div
|
||||
className="mt-sp-4 grid grid-cols-1 gap-sp-4 md:grid-cols-2 lg:grid-cols-4"
|
||||
data-reel="fade"
|
||||
>
|
||||
{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>
|
||||
<div className="ds-section-shell">
|
||||
<div className="quality-intro">
|
||||
<header className="quality-copy">
|
||||
<p className="ds-eyebrow">Quality & Performance</p>
|
||||
<h2 id="quality-title" className="ds-section-title">
|
||||
Engenharia de ponta a ponta.
|
||||
<span>Qualidade em cada camada.</span>
|
||||
</h2>
|
||||
<p>
|
||||
Sistemas precisam mais do que uma boa aparência. Explore as
|
||||
decisões que tornam uma interface rápida, acessível, confiável e
|
||||
fácil de encontrar.
|
||||
</p>
|
||||
<a
|
||||
className="ds-link ds-link-accent"
|
||||
href="/servicos/consultoria-nextjs"
|
||||
>
|
||||
Conversar sobre a qualidade do seu projeto
|
||||
</a>
|
||||
</header>
|
||||
|
||||
{/* Content Details */}
|
||||
<div className="flex-1" data-reel-stagger="bottom">
|
||||
<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>
|
||||
<figure className="quality-visual bg-grid-light">
|
||||
<div className="quality-visual-label">
|
||||
<span>Laboratório de qualidade</span>
|
||||
<span>Modelo interativo</span>
|
||||
</div>
|
||||
</motion.article>
|
||||
))}
|
||||
{BLUEPRINT}
|
||||
<figcaption>
|
||||
{METRICS.map((metric) => (
|
||||
<span key={metric.id} aria-hidden={active.id !== metric.id}>
|
||||
{metric.caption}
|
||||
</span>
|
||||
))}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<fieldset className="quality-controls" aria-describedby="quality-hint">
|
||||
<legend className="sr-only">Escolha um aspecto da qualidade</legend>
|
||||
{METRICS.map((metric) => (
|
||||
<label key={metric.id} className="quality-control">
|
||||
<input
|
||||
type="radio"
|
||||
name="quality-pillar"
|
||||
value={metric.id}
|
||||
checked={active.id === metric.id}
|
||||
onChange={() => setActive(metric)}
|
||||
aria-controls="quality-report"
|
||||
/>
|
||||
<span className="quality-control-content">
|
||||
<span className="quality-control-name">{metric.label}</span>
|
||||
<span className="quality-control-target">
|
||||
Meta <strong>100</strong>
|
||||
<span>/100</span>
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</fieldset>
|
||||
|
||||
<p id="quality-hint" className="quality-hint">
|
||||
Selecione um pilar para explorar. No teclado, use as setas.
|
||||
</p>
|
||||
|
||||
<section
|
||||
id="quality-report"
|
||||
className="quality-report-stack"
|
||||
aria-label="Inspeção de qualidade"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
>
|
||||
{METRICS.map((metric) => (
|
||||
<div
|
||||
key={metric.id}
|
||||
className="quality-report"
|
||||
aria-hidden={active.id !== metric.id}
|
||||
>
|
||||
<div className="quality-report-copy">
|
||||
<p className="quality-code">{metric.code}</p>
|
||||
<h3>{metric.title}</h3>
|
||||
<p>{metric.description}</p>
|
||||
</div>
|
||||
<ul>
|
||||
{metric.checks.map((check) => (
|
||||
<li key={check}>
|
||||
<span aria-hidden="true">/</span>
|
||||
{check}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<p className="quality-disclaimer">
|
||||
Metas de qualidade, não uma auditoria em tempo real. Resultados
|
||||
Lighthouse variam conforme página, dispositivo e condições do teste.
|
||||
</p>
|
||||
</div>
|
||||
</Section>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user