CI / verify (push) Failing after 5m9s
Align the notebook browser with the physical LCD, refresh real project captures and navigation, simplify service content, and stabilize hero hydration.
344 lines
10 KiB
TypeScript
344 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
const METRICS = [
|
|
{
|
|
id: "performance",
|
|
label: "Performance",
|
|
code: "LCP / INP / CLS",
|
|
title: "O essencial chega primeiro.",
|
|
description:
|
|
"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.",
|
|
},
|
|
{
|
|
id: "accessibility",
|
|
label: "Acessibilidade",
|
|
code: "TECLADO / FOCO / SEMÂNTICA",
|
|
title: "A interface inclui mais pessoas.",
|
|
description:
|
|
"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.",
|
|
},
|
|
{
|
|
id: "practices",
|
|
label: "Boas práticas",
|
|
code: "HTTPS / PRIVACIDADE / DEPENDÊNCIAS",
|
|
title: "Confiança também se constrói.",
|
|
description:
|
|
"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.",
|
|
},
|
|
{
|
|
id: "seo",
|
|
label: "SEO",
|
|
code: "HTML / METADATA / INDEXAÇÃO",
|
|
title: "Conteúdo pronto para ser encontrado.",
|
|
description:
|
|
"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 [active, setActive] = useState(METRICS[0]);
|
|
|
|
return (
|
|
<section
|
|
id="lighthouse"
|
|
className="ds-section ds-section-dark quality-lab"
|
|
aria-labelledby="quality-title"
|
|
data-inspection={active.id}
|
|
>
|
|
<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>
|
|
|
|
<figure className="quality-visual bg-grid-light">
|
|
<div className="quality-visual-label">
|
|
<span>Laboratório de qualidade</span>
|
|
<span>Modelo interativo</span>
|
|
</div>
|
|
{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>
|
|
);
|
|
}
|