diff --git a/app/opengraph-image.png b/app/opengraph-image.png index 755bbf8..8e9c3b6 100644 Binary files a/app/opengraph-image.png and b/app/opengraph-image.png differ diff --git a/components/Footer.jsx b/components/Footer.jsx index 9f9d3e5..6cdde6f 100644 --- a/components/Footer.jsx +++ b/components/Footer.jsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import Logo from "@/components/Logo"; import { ArrowUpRight } from "lucide-react"; import { SITE } from "@/lib/seo"; @@ -41,8 +42,7 @@ export default function Footer() {
- {/* eslint-disable-next-line @next/next/no-img-element */} - + ESPDocs

diff --git a/components/Header.jsx b/components/Header.jsx index 7a63f50..ca4be7c 100644 --- a/components/Header.jsx +++ b/components/Header.jsx @@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import Link from "next/link"; +import Logo from "@/components/Logo"; import { usePathname } from "next/navigation"; import { useTheme } from "next-themes"; import { @@ -121,15 +122,7 @@ export default function Header() {

- {/* eslint-disable-next-line @next/next/no-img-element */} - + ESPDocs diff --git a/components/Logo.jsx b/components/Logo.jsx new file mode 100644 index 0000000..ca0b005 --- /dev/null +++ b/components/Logo.jsx @@ -0,0 +1,32 @@ +/** + * Marca do ESPDocs: o encapsulamento do chip com pinos à esquerda; os pinos e + * o corpo viram linhas de texto, a documentação. Inline para seguir o tema do + * site (--accent e --copper trocam entre claro e escuro, ao contrário de uma + * imagem externa, que só enxerga a preferência do sistema). + */ +export default function Logo({ className = "h-8 w-8", title }) { + return ( + + {title && {title}} + + + + + + + + + + + + + ); +} diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index 1d90bd0..fe99025 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png index 3382e21..bef6273 100644 Binary files a/public/favicon-16x16.png and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png index 30e9849..e7954eb 100644 Binary files a/public/favicon-32x32.png and b/public/favicon-32x32.png differ diff --git a/public/favicon-48x48.png b/public/favicon-48x48.png index 2817f0e..e20bbb5 100644 Binary files a/public/favicon-48x48.png and b/public/favicon-48x48.png differ diff --git a/public/favicon.ico b/public/favicon.ico index ab9e2da..5d418bb 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/icon-192.png b/public/icon-192.png index 34a65cc..18d4b36 100644 Binary files a/public/icon-192.png and b/public/icon-192.png differ diff --git a/public/icon-512.png b/public/icon-512.png index 39fcaaa..a2f2885 100644 Binary files a/public/icon-512.png and b/public/icon-512.png differ diff --git a/public/icon.svg b/public/icon.svg index 158d5fa..61df1e9 100644 --- a/public/icon.svg +++ b/public/icon.svg @@ -1,29 +1,17 @@ ESPDocs - - - - - - - - + - - - - + + - - - - - - - - + + + + diff --git a/public/marca/espdocs-lockup.svg b/public/marca/espdocs-lockup.svg index 4b98d7a..2a6eb8a 100644 --- a/public/marca/espdocs-lockup.svg +++ b/public/marca/espdocs-lockup.svg @@ -1,19 +1,18 @@ ESPDocs - - - - - - - - + + + - - + + + + - ESPDocs + ESPDocs diff --git a/public/marca/espdocs-marca.svg b/public/marca/espdocs-marca.svg index 84d2267..e620fda 100644 --- a/public/marca/espdocs-marca.svg +++ b/public/marca/espdocs-marca.svg @@ -1,29 +1,17 @@ ESPDocs - - - - - - - - + - - - - + + - - - - - - - - + + + + diff --git a/public/site.webmanifest b/public/site.webmanifest index 2691d43..6848e15 100644 --- a/public/site.webmanifest +++ b/public/site.webmanifest @@ -2,10 +2,11 @@ "name": "ESPDocs", "short_name": "ESPDocs", "description": "Documentação do ecossistema ESP32 em português brasileiro", + "lang": "pt-BR", "start_url": "/", "display": "standalone", - "background_color": "#020617", - "theme_color": "#8b5cf6", + "background_color": "#07090d", + "theme_color": "#07090d", "icons": [ { "src": "/favicon-48x48.png", @@ -15,12 +16,14 @@ { "src": "/icon-192.png", "sizes": "192x192", - "type": "image/png" + "type": "image/png", + "purpose": "any maskable" }, { "src": "/icon-512.png", "sizes": "512x512", - "type": "image/png" + "type": "image/png", + "purpose": "any maskable" } ] -} \ No newline at end of file +} diff --git a/scripts/generate-favicons.mjs b/scripts/generate-favicons.mjs index f7d6f50..cba1f50 100644 --- a/scripts/generate-favicons.mjs +++ b/scripts/generate-favicons.mjs @@ -2,12 +2,31 @@ import fs from 'fs'; import path from 'path'; import puppeteer from 'puppeteer'; -const svgPath = fs.existsSync(path.resolve('public/icon.svg')) - ? path.resolve('public/icon.svg') - : fs.existsSync(path.resolve('app/icon.svg')) - ? path.resolve('app/icon.svg') - : path.resolve('public/marca/espdocs-marca.svg'); -const svgContent = fs.readFileSync(svgPath, 'utf8'); +/** + * Gera todos os ativos de marca a partir da geometria do logo: + * favicons (transparentes), ícones de app (tile obsidiana), site.webmanifest + * e a imagem de compartilhamento (Open Graph, 1200x630). + * + * Cores = tokens de app/globals.css. Uso: npm run favicons + * (usa o Chrome do Puppeteer; defina CHROME_PATH para usar outro binário) + */ + +const COR = { + obsidiana: "#07090d", + tealEscuro: "#2dd4bf", + cobreEscuro: "#ffb39b", + tealMedio: "#1a8f83", // legível em aba clara e escura + cobreMedio: "#c8694e", + texto: "#f4f6f8", + suave: "#a6afbd", +}; + +const marca = (teal, cobre) => ` + + + + +`; function createIco(pngBuffers) { // pngBuffers: array of { width, height, buffer } @@ -39,92 +58,97 @@ function createIco(pngBuffers) { return Buffer.concat([header, ...entries, ...pngBuffers.map(b => b.buffer)]); } + +const pagina = (corpo, css = "") => ` + + + +${corpo}`; + async function run() { const browser = await puppeteer.launch({ + executablePath: process.env.CHROME_PATH || undefined, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); - const html = ` - - - - - ${svgContent} - `; - - await page.setContent(html); - - const sizes = [16, 32, 48, 96, 180, 192, 512]; + // 1. Favicons: marca sozinha, fundo transparente. O logo ocupa x 2..55; centraliza. + const favicon = pagina(`
${marca(COR.tealMedio, COR.cobreMedio).replace('`); + await page.setContent(favicon); const rendered = {}; - - for (const size of sizes) { + for (const size of [16, 32, 48]) { await page.setViewport({ width: size, height: size, deviceScaleFactor: 1 }); - const buffer = await page.screenshot({ - type: 'png', - omitBackground: true, - clip: { x: 0, y: 0, width: size, height: size } - }); - rendered[size] = buffer; - console.log(`Rendered ${size}x${size}`); + rendered[size] = await page.screenshot({ type: 'png', omitBackground: true, clip: { x: 0, y: 0, width: size, height: size } }); } + // 2. Ícones de app: tile obsidiana, marca com margem de segurança (maskable). + const tile = pagina(`
+
${marca(COR.tealEscuro, COR.cobreEscuro).replace('
`); + await page.setContent(tile); + for (const size of [180, 192, 512]) { + await page.setViewport({ width: size, height: size, deviceScaleFactor: 1 }); + rendered[size] = await page.screenshot({ type: 'png', clip: { x: 0, y: 0, width: size, height: size } }); + } + + // 3. Open Graph / Twitter: 1200x630. + const og = pagina(` +
+
${marca(COR.tealEscuro, COR.cobreEscuro).replace('
+

Documentação do ESP32
em português

+

Especificações, pinagem, comparação e seleção das 12 séries do ESP32.

+
SériesSeletorComparadorDiagnósticoLivro
+
+
`, ` + body{background:${COR.obsidiana};font-family:'Manrope',system-ui,sans-serif;color:${COR.texto};position:relative} + body::before{content:"";position:absolute;inset:0;background:radial-gradient(52rem 30rem at 50% -14%,rgba(45,212,191,.16),transparent 70%),radial-gradient(40rem 26rem at 100% 30%,rgba(56,140,248,.09),transparent 70%)} + main{position:relative;height:100%;padding:76px 88px 0} + .topo{display:flex;align-items:center;gap:20px} + .logo{width:68px;height:68px} + .nome{font:600 44px 'Space Grotesk',sans-serif;letter-spacing:-0.045em} + h1{margin-top:64px;font:600 86px/1.03 'Space Grotesk',sans-serif;letter-spacing:-0.04em} + p{margin-top:30px;max-width:900px;font-size:30px;line-height:1.45;color:${COR.suave}} + .chips{position:absolute;left:88px;bottom:62px;display:flex;gap:12px} + .chips span{border:1.5px solid #2e3846;border-radius:999px;padding:9px 22px;font-size:22px;font-weight:500;color:${COR.texto}} + .barra{position:absolute;left:0;right:0;bottom:0;height:10px;background:${COR.tealEscuro}}`); + await page.setViewport({ width: 1200, height: 630, deviceScaleFactor: 1 }); + await page.setContent(og, { waitUntil: 'networkidle0' }); + await page.evaluate(() => document.fonts.ready); + const ogPng = await page.screenshot({ type: 'png', clip: { x: 0, y: 0, width: 1200, height: 630 } }); + await browser.close(); - // Create ICO from 16, 32, 48 const icoBuffer = createIco([ { width: 16, height: 16, buffer: rendered[16] }, { width: 32, height: 32, buffer: rendered[32] }, { width: 48, height: 48, buffer: rendered[48] } ]); - - // Write favicon.ico to public fs.writeFileSync('public/favicon.ico', icoBuffer); - console.log('Saved favicon.ico to public/'); - - // Write PNG favicons to public fs.writeFileSync('public/favicon-48x48.png', rendered[48]); fs.writeFileSync('public/favicon-32x32.png', rendered[32]); fs.writeFileSync('public/favicon-16x16.png', rendered[16]); fs.writeFileSync('public/apple-touch-icon.png', rendered[180]); fs.writeFileSync('public/icon-192.png', rendered[192]); fs.writeFileSync('public/icon-512.png', rendered[512]); - console.log('Saved PNG icons to public/'); + fs.writeFileSync('app/opengraph-image.png', ogPng); + console.log('Favicons, ícones de app e imagem Open Graph gerados.'); - // Create site.webmanifest const manifest = { name: "ESPDocs", short_name: "ESPDocs", description: "Documentação do ecossistema ESP32 em português brasileiro", + lang: "pt-BR", start_url: "/", display: "standalone", - background_color: "#020617", - theme_color: "#8b5cf6", + background_color: COR.obsidiana, + theme_color: COR.obsidiana, icons: [ - { - src: "/favicon-48x48.png", - sizes: "48x48", - type: "image/png" - }, - { - src: "/icon-192.png", - sizes: "192x192", - type: "image/png" - }, - { - src: "/icon-512.png", - sizes: "512x512", - type: "image/png" - } + { src: "/favicon-48x48.png", sizes: "48x48", type: "image/png" }, + { src: "/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any maskable" }, + { src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any maskable" } ] }; - - fs.writeFileSync('public/site.webmanifest', JSON.stringify(manifest, null, 2)); - console.log('Saved site.webmanifest to public/'); + fs.writeFileSync('public/site.webmanifest', JSON.stringify(manifest, null, 2) + '\n'); + console.log('site.webmanifest gerado.'); } run().catch(err => {