From 85dd326964068edb84ecd66e07db5b724e1c528d Mon Sep 17 00:00:00 2001 From: Evandro Fonseca Junior Date: Mon, 5 Oct 2026 00:02:35 -0300 Subject: [PATCH] feat(seo): og:locale pt_BR em todas as paginas, titulos e descricoes mais curtos, Person com cargo e areas; remove alegacao de LCP do SC Plus - openGraph.locale pt_BR nas 6 rotas que redefinem openGraph (so a home tinha) - titulos: cases usam 'Case de Engenharia'; /curriculo, Tauri e Engenharia de Software mais curtos; descricao da home (193 -> 146) e de AS Servicos (184 -> 136) - Person JSON-LD ganha jobTitle e knowsAbout (dados e texto ja visiveis no site) - SC Plus: remove 'otimizada para Core Web Vitals' / LCP < 2,5 s; Lighthouse mobile local mediu LCP 3,7 s. Texto passa a afirmar so metadados e dados estruturados (verificados no site) Co-Authored-By: Claude Sonnet 5.5 --- app/cases/[slug]/page.tsx | 3 ++- app/cases/page.tsx | 1 + app/curriculo/page.tsx | 3 ++- app/layout.tsx | 2 +- app/privacidade/page.tsx | 1 + app/servicos/[slug]/page.tsx | 1 + app/servicos/page.tsx | 1 + lib/portfolio-data.test.ts | 7 +++++++ lib/seo.test.ts | 13 ++++++++++++- lib/seo.ts | 12 ++++++++++++ lib/site-content.ts | 14 +++++++------- public/portfolio-data.json | 25 +++++++++---------------- 12 files changed, 56 insertions(+), 27 deletions(-) diff --git a/app/cases/[slug]/page.tsx b/app/cases/[slug]/page.tsx index 053fdb7..b1a1a72 100644 --- a/app/cases/[slug]/page.tsx +++ b/app/cases/[slug]/page.tsx @@ -35,10 +35,11 @@ export async function generateMetadata({ const url = `${siteUrl}/cases/${caseStudy.id}`; return { - title: `${caseStudy.title} | Case de Engenharia de Software`, + title: `${caseStudy.title} | Case de Engenharia`, description: caseStudy.summary, alternates: { canonical: url }, openGraph: { + locale: "pt_BR", title: caseStudy.title, description: caseStudy.summary, url, diff --git a/app/cases/page.tsx b/app/cases/page.tsx index 5948699..5fadb4a 100644 --- a/app/cases/page.tsx +++ b/app/cases/page.tsx @@ -18,6 +18,7 @@ export const metadata: Metadata = { canonical: pageUrl, }, openGraph: { + locale: "pt_BR", title: pageTitle, description: pageDescription, url: pageUrl, diff --git a/app/curriculo/page.tsx b/app/curriculo/page.tsx index 8837c8b..7ad4370 100644 --- a/app/curriculo/page.tsx +++ b/app/curriculo/page.tsx @@ -8,11 +8,12 @@ import { siteName, siteUrl } from "@/lib/site-content"; const pageUrl = `${siteUrl}/curriculo`; export const metadata: Metadata = { - title: "Currículo de Evandro Fonseca Junior para Contratação", + title: { absolute: `Currículo de Evandro Fonseca Junior | ${siteName}` }, description: "Currículo profissional de Evandro Fonseca Junior, engenheiro da computação e desenvolvedor de software, com experiência, formação e competências técnicas.", alternates: { canonical: pageUrl }, openGraph: { + locale: "pt_BR", title: `Currículo de Evandro Fonseca Junior | ${siteName}`, description: "Experiência profissional, formação e competências técnicas de Evandro Fonseca Junior para avaliação de contratação.", diff --git a/app/layout.tsx b/app/layout.tsx index 6ed082a..6fe63a7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -12,7 +12,7 @@ import "./globals.css"; const defaultTitle = "Sistemas sob medida para empresas | Evandro.dev.br"; const seoDescription = - "Desenvolvimento de sistemas web, aplicativos e integrações sob medida. Do entendimento do negócio à entrega em produção, com Evandro Fonseca Junior. Base em Santa Catarina e atendimento remoto."; + "Sistemas web, aplicativos e integrações sob medida, do negócio à produção, com Evandro Fonseca Junior. Base em Santa Catarina, atendimento remoto."; const spaceGrotesk = Space_Grotesk({ subsets: ["latin"], diff --git a/app/privacidade/page.tsx b/app/privacidade/page.tsx index cabcd7b..5eda1d2 100644 --- a/app/privacidade/page.tsx +++ b/app/privacidade/page.tsx @@ -12,6 +12,7 @@ export const metadata: Metadata = { "Como o portfólio trata dados de contato, consentimento de analytics, links externos e solicitações de privacidade.", alternates: { canonical: pageUrl }, openGraph: { + locale: "pt_BR", title: `Privacidade e consentimento | ${siteName}`, description: "Informações factuais sobre contato, analytics e preferências de privacidade no portfólio.", diff --git a/app/servicos/[slug]/page.tsx b/app/servicos/[slug]/page.tsx index 0789b35..7ce805a 100644 --- a/app/servicos/[slug]/page.tsx +++ b/app/servicos/[slug]/page.tsx @@ -45,6 +45,7 @@ export async function generateMetadata({ canonical: url, }, openGraph: { + locale: "pt_BR", title: service.seoTitle, description: service.metaDescription, url, diff --git a/app/servicos/page.tsx b/app/servicos/page.tsx index 420ee83..5e172a0 100644 --- a/app/servicos/page.tsx +++ b/app/servicos/page.tsx @@ -56,6 +56,7 @@ export const metadata: Metadata = { canonical: pageUrl, }, openGraph: { + locale: "pt_BR", title: pageTitle, description: pageDescription, url: pageUrl, diff --git a/lib/portfolio-data.test.ts b/lib/portfolio-data.test.ts index afbfeb1..bae3977 100644 --- a/lib/portfolio-data.test.ts +++ b/lib/portfolio-data.test.ts @@ -100,6 +100,13 @@ describe("factual portfolio records", () => { ); }); + it("does not claim measured performance for SC Plus without a stored measurement", () => { + // Lighthouse mobile local: LCP 3,7 s em 2026-10-05; a alegação anterior (LCP < 2,5 s) não se sustentava + expect(serializeProject("sc-plus")).not.toMatch( + /LCP|Core Web Vitals|otimizad|velocidade|rápid|Performance/i, + ); + }); + it("does not publish the unavailable Twins Lab demo", () => { expect( portfolio.projects.find((item) => item.id === "twins-lab")?.demoUrl, diff --git a/lib/seo.test.ts b/lib/seo.test.ts index b808f18..4f07b75 100644 --- a/lib/seo.test.ts +++ b/lib/seo.test.ts @@ -37,7 +37,17 @@ it("keeps the root graph limited to verified website and person fields", () => { ["@id", "@type", "inLanguage", "name", "publisher", "url"].sort(), ); expect(Object.keys(person).sort()).toEqual( - ["@id", "@type", "homeLocation", "image", "name", "sameAs", "url"].sort(), + [ + "@id", + "@type", + "homeLocation", + "image", + "jobTitle", + "knowsAbout", + "name", + "sameAs", + "url", + ].sort(), ); expect(website).toMatchObject({ "@type": "WebSite", @@ -49,6 +59,7 @@ it("keeps the root graph limited to verified website and person fields", () => { "@type": "Person", "@id": SEO_IDS.person, image: `${siteUrl}/edev.jpg`, + jobTitle: "Engenheiro da Computação e Desenvolvedor de Software", homeLocation: { "@type": "Place", name: "Garuva, Santa Catarina, Brasil" }, sameAs: [ "https://github.com/evandrodevbr", diff --git a/lib/seo.ts b/lib/seo.ts index 05a5c3e..2e232ef 100644 --- a/lib/seo.ts +++ b/lib/seo.ts @@ -24,6 +24,18 @@ export function buildPersonJsonLd() { name: "Evandro Fonseca Junior", url: siteUrl, image: `${siteUrl}/edev.jpg`, + // cargo e áreas vêm de public/portfolio-data.json e estão visíveis em /curriculo e na seção de skills + jobTitle: "Engenheiro da Computação e Desenvolvedor de Software", + knowsAbout: [ + "Next.js", + "React", + "TypeScript", + "Node.js", + "Python", + "IoT", + "Computação em nuvem", + "Inteligência artificial", + ], homeLocation: { "@type": "Place", name: "Garuva, Santa Catarina, Brasil", diff --git a/lib/site-content.ts b/lib/site-content.ts index ce49fd8..e678477 100644 --- a/lib/site-content.ts +++ b/lib/site-content.ts @@ -146,7 +146,7 @@ export const servicePages: ServicePageContent[] = [ slug: "engenharia-de-software", navLabel: "Sistemas web e aplicativos mobile", eyebrow: "Engenharia de software", - seoTitle: "Desenvolvimento de Sistemas Web e Aplicativos Mobile", + seoTitle: "Sistemas Web e Aplicativos Mobile sob Medida", metaDescription: "Sistemas web e aplicativos mobile sob medida, integração entre ferramentas e evolução de software existente. Escopo, plataformas e entregas definidos por projeto.", headline: "Sistemas e apps para o seu trabalho.", @@ -268,7 +268,7 @@ export const servicePages: ServicePageContent[] = [ slug: "tauri-rust", navLabel: "Aplicativos desktop para Windows e Linux", eyebrow: "Aplicativos desktop", - seoTitle: "Aplicativos Desktop para Windows e Linux com Tauri e Rust", + seoTitle: "Aplicativos Desktop com Tauri e Rust", metaDescription: "Desenvolvimento de aplicativos desktop para Windows e Linux com Tauri e Rust. Ferramentas locais, arquivos e integrações com compatibilidade definida por projeto.", headline: "Aplicativos para Windows e Linux.", @@ -692,11 +692,11 @@ export const caseStudies: CaseStudyContent[] = [ { id: "sc-plus", title: "SC Plus Aquicultura", - subtitle: "Site institucional em Next.js com foco em performance e SEO", + subtitle: "Landing page institucional em Next.js 15", summary: - "Landing page em Next.js 15 otimizada para Core Web Vitals, metadata e estrutura técnica de indexação.", + "Landing page institucional em Next.js 15, React 19 e Tailwind CSS 4, com metadados e dados estruturados para indexação.", impact: - "Exemplo de projeto em que clareza de mensagem, velocidade e SEO técnico trabalham juntos.", + "Dar à instituição uma página própria, com base técnica para ser encontrada em buscadores.", stack: ["Next.js 15", "React 19", "Tailwind CSS 4", "TypeScript"], demoUrl: "https://scplus.evandro.dev.br", githubUrl: "https://github.com/evandrodevbr/scplus", @@ -768,7 +768,7 @@ export const caseStudies: CaseStudyContent[] = [ title: "AS Serviços", subtitle: "Site institucional com cena 3D procedural", summary: - "Site institucional em Next.js 16 que representa quatro frentes de engenharia com uma cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.", + "Site institucional em Next.js 16 com cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.", impact: "Representar engenharia física e software na mesma narrativa visual sem depender de um modelo 3D pronto.", stack: ["Next.js 16", "Three.js", "TypeScript"], @@ -777,7 +777,7 @@ export const caseStudies: CaseStudyContent[] = [ role: "Desenvolvimento da primeira landing page do site, com animações 3D, e de um painel para controle de projetos.", detail: { answer: { - text: "Site institucional em Next.js 16 que representa quatro frentes de engenharia com uma cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.", + text: "Site institucional em Next.js 16 com cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.", sourceIds: ["as-readme", "as-package", "as-engine"], }, challenge: { diff --git a/public/portfolio-data.json b/public/portfolio-data.json index 20b962b..9117ab7 100644 --- a/public/portfolio-data.json +++ b/public/portfolio-data.json @@ -178,28 +178,21 @@ { "id": "sc-plus", "title": "SC Plus Aquicultura", - "subtitle": "Landing Page Institucional", - "description": "Landing page Next.js 15 com React 19 e Tailwind CSS 4. Arquitetura otimizada para Core Web Vitals com LCP < 2.5s através de code splitting e server-side rendering. SEO técnico implementado com meta tags dinâmicas e structured data.", - "longDescription": "Site institucional responsivo desenvolvido com as mais recentes tecnologias web, otimizado para performance e SEO, apresentando os serviços e valores da empresa de aquicultura sustentável.", - "technologies": [ - "Next.js 15", - "React 19", - "Tailwind CSS 4", - "TypeScript" - ], + "subtitle": "Landing page institucional em Next.js 15", + "description": "Landing page institucional em Next.js 15, React 19 e Tailwind CSS 4, com metadados e dados estruturados para indexação.", + "longDescription": "Site institucional em Next.js 15 com React 19 e Tailwind CSS 4. A página publica título, descrição, URL canônica, imagem social e dados estruturados.", + "technologies": ["Next.js 15", "React 19", "Tailwind CSS 4", "TypeScript"], "features": [ - "Design moderno e responsivo", - "Performance otimizada", - "SEO otimizado", - "Interface intuitiva", - "Carregamento rápido" + "Landing page institucional", + "Metadados e URL canônica", + "Dados estruturados" ], "category": "web", "level": "pleno", "status": "Concluído", "demoUrl": "https://scplus.evandro.dev.br", "githubUrl": "https://github.com/evandrodevbr/scplus", - "badges": ["Responsive", "SEO", "Modern Stack"], + "badges": ["Landing page", "Next.js 15"], "featured": true, "metrics": { "complexity": 2, @@ -236,7 +229,7 @@ "id": "as-servicos", "title": "AS Serviços", "subtitle": "Site Institucional com Cena 3D Procedural", - "description": "Site institucional em Next.js 16 que representa quatro frentes de engenharia com uma cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.", + "description": "Site institucional em Next.js 16 com cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.", "longDescription": "O motor monta casa, instalação elétrica e placa-mãe com geometrias e linhas do Three.js. O canvas converte a rolagem em progresso de 0 a 1, e a qualidade se adapta conforme WebGL, reduced motion, economia de dados, conexão e capacidade do dispositivo.", "technologies": ["Next.js 16", "TypeScript", "Three.js"], "features": [