Files
ESPDocs/app/frameworks/[key]/page.jsx
T
Lucas Rayan GuerraandClaude Sonnet 5.5 bc9ae5c036 Redesign das páginas internas no novo design system (AAA) (#2)
* feat(design): redesign das páginas internas no novo design system

Padroniza todas as páginas e componentes internos nos tokens e primitivos
de app/globals.css, com contraste AAA (7:1) nos dois temas e alvos de toque
de 44px.

- Páginas reescritas: glossário, séries, série, seletor, comparador,
  diagnóstico, catálogo, component registry, frameworks, framework, sobre
  e a nova 404.
- Componentes: PageHero e SectionHead novos; BoardCard, CodeExample,
  PinRestrictions, SeriesTabMenu e ConnectionsDiagram refeitos, com abas
  e diálogo acessíveis.
- Remove emojis, cores avulsas por série, caixa alta com tracking, texto
  menor que 12px e os pares slate-*/dark:*.
- Comparador: "mostrar só as diferenças" e leitura de ?chips=.
- Corrige overflow em 320/390px e ajusta accent/copper do tema claro
  para fechar 7:1.
- scripts/auditar-temas.mjs passa a travar paleta crua, dark:, texto
  pequeno, emoji e contraste dos tokens.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016W1xmizFcmXLrmabSvfSuc

* ci: roda lint, auditorias de design e build em cada PR

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016W1xmizFcmXLrmabSvfSuc

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 16:10:15 -03:00

180 lines
6.5 KiB
React

import { paginaMeta, jsonLd } from "@/lib/seo";
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import PageHero from "@/components/PageHero";
import { notFound } from "next/navigation";
import CodeExample from "@/components/CodeExample";
import frameworksData from "@/public/frameworks.json";
import { ArrowRight, ArrowUpRight, BookOpen, Box, Check, Code, Layers } from "lucide-react";
export default async function FrameworkDetail({ params }) {
const { key } = await params;
const framework = frameworksData[key];
if (!framework) {
notFound();
}
const features = framework.caracteristicas || [];
const useCases = framework.casos_uso || [];
const isEspIdf = key === "ESP-IDF";
return (
<div className="site-canvas min-h-screen">
{/* Ficha técnica do framework e trilha de navegação para o Google */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd.framework(key, framework)) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd.trilha([
{ nome: "Início", caminho: "/" },
{ nome: "Frameworks", caminho: "/frameworks" },
{ nome: framework.nome, caminho: `/frameworks/${encodeURIComponent(key)}` },
])),
}}
/>
<Header />
<main id="conteudo">
<PageHero
trail={[{ label: "Frameworks e SDKs", href: "/frameworks" }, { label: framework.nome }]}
icon={Layers}
label={framework.tipo}
title={framework.nome}
lede={framework.descricao}
>
{framework.documentacao && (
<a href={framework.documentacao} target="_blank" rel="noopener noreferrer" className="btn btn-primary">
<BookOpen className="h-4 w-4" aria-hidden="true" />
Documentação oficial
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
</a>
)}
{framework.repositorio && (
<a href={framework.repositorio} target="_blank" rel="noopener noreferrer" className="btn btn-ghost">
<Code className="h-4 w-4" aria-hidden="true" />
Repositório no GitHub
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
</a>
)}
</PageHero>
<section className="shell mt-10" aria-label="Resumo">
<dl className="grid gap-3 md:grid-cols-3">
<div className="tile">
<dt className="text-xs font-medium text-muted">Linguagem</dt>
<dd className="mt-1 font-display text-2xl font-semibold tracking-[-0.03em] text-ink">{framework.linguagem}</dd>
<dd className="mt-0.5 text-xs text-muted">{framework.nome_completo}</dd>
</div>
<div className="tile">
<dt className="text-xs font-medium text-muted">Categoria</dt>
<dd className="mt-1 font-display text-2xl font-semibold tracking-[-0.03em] text-ink">{framework.tipo}</dd>
</div>
<div className="tile">
<dt className="text-xs font-medium text-muted">Objetivo principal</dt>
<dd className="mt-1 text-sm font-medium leading-relaxed text-ink">{framework.funcao_principal}</dd>
</div>
</dl>
</section>
<section className="shell mt-12 grid gap-5 lg:grid-cols-2" aria-label="Recursos e aplicações">
<div className="card p-5 sm:p-7">
<h2 className="font-display text-xl font-semibold tracking-[-0.03em] text-ink">Recursos de destaque</h2>
<ul className="mt-5 space-y-3">
{features.map((feature) => (
<li key={feature} className="flex items-start gap-3 text-sm leading-relaxed text-ink-soft">
<Check className="mt-0.5 h-4 w-4 shrink-0 text-ok" aria-hidden="true" />
{feature}
</li>
))}
</ul>
</div>
<div className="card p-5 sm:p-7">
<h2 className="font-display text-xl font-semibold tracking-[-0.03em] text-ink">Quando usar</h2>
<ul className="mt-5 space-y-2.5">
{useCases.map((useCase) => (
<li key={useCase} className="tile text-sm font-medium text-ink-soft">
{useCase}
</li>
))}
</ul>
</div>
</section>
{isEspIdf && (
<section className="shell mt-8">
<div className="panel flex flex-col items-start justify-between gap-6 p-7 sm:p-10 md:flex-row md:items-center">
<div className="max-w-2xl">
<span className="chip chip-accent">
<Box className="h-3.5 w-3.5" aria-hidden="true" />
ESP-IDF 4.4 ou superior
</span>
<h2 className="section-title mt-4">Módulos com o ESP Component Registry</h2>
<p className="mt-3 text-base leading-relaxed text-muted">
Nos projetos ESP-IDF v4.4+ e v5.x, as bibliotecas não precisam mais entrar como submódulos Git. Use o{" "}
<code className="rounded bg-surface-2 px-1.5 py-0.5 font-mono text-sm">idf_component.yml</code> e o comando{" "}
<code className="rounded bg-surface-2 px-1.5 py-0.5 font-mono text-sm">idf.py add-dependency</code> para baixar drivers de display (LVGL), pilhas industriais (Modbus), RainMaker e mais.
</p>
</div>
<Link href="/componentes" className="btn btn-primary shrink-0">
Explorar componentes
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</section>
)}
{framework.exemplo && (
<section className="shell mt-8">
<CodeExample framework={framework} />
</section>
)}
<div className="shell section pt-10">
<Link href="/frameworks" className="inline-flex min-h-11 items-center gap-2 text-sm font-medium text-accent hover:text-accent-strong">
<ArrowRight className="h-4 w-4 rotate-180" aria-hidden="true" />
Ver todos os frameworks
</Link>
</div>
</main>
<Footer />
</div>
);
}
export async function generateMetadata({ params }) {
const { key } = await params;
const framework = frameworksData[key];
if (!framework) {
return { title: "Framework não encontrado", robots: { index: false, follow: false } };
}
return {
...paginaMeta({
titulo: `${framework.nome} para ESP32 · Guia Técnico`,
descricao: `${framework.nome_completo}: ${framework.descricao} Guia com exemplos práticos de código e casos de uso.`,
caminho: `/frameworks/${encodeURIComponent(key)}`,
tipo: "article",
keywords: [
framework.nome, framework.nome_completo,
`${framework.nome} ESP32`, `programar ESP32 com ${framework.nome}`,
"ESP32", "framework ESP32",
],
}),
other: { "article:section": "Frameworks" },
};
}
export async function generateStaticParams() {
return Object.keys(frameworksData).map((key) => ({
key: key,
}));
}