From 3de4f2501d8b7ccf845e12e778f4aed4e487a1e8 Mon Sep 17 00:00:00 2001 From: Lucas Rayan Date: Thu, 20 Aug 2026 14:51:58 -0300 Subject: [PATCH] feat: enhance accessibility and theme consistency in components --- app/glossario/page.jsx | 34 +++++----- public/apostilas/1/index.html | 117 ++++++++++++++++++++++++++++++++-- scripts/auditar-temas.mjs | 45 ++++++++++++- 3 files changed, 170 insertions(+), 26 deletions(-) diff --git a/app/glossario/page.jsx b/app/glossario/page.jsx index 98aa32d..eb360f9 100644 --- a/app/glossario/page.jsx +++ b/app/glossario/page.jsx @@ -363,22 +363,22 @@ export default function Glossario() { ]; return ( -
+
{/* Hero Section */}
-
+
📖 - Aprenda os Conceitos + Aprenda os Conceitos

Glossário Técnico

-

+

Entenda termos e conceitos do ecossistema ESP32 explicados em português, do básico ao avançado.

@@ -386,8 +386,8 @@ export default function Glossario() { {/* Navegação Rápida */}
-
-

+
+

Navegação Rápida

@@ -395,10 +395,10 @@ export default function Glossario() { {cat.icone} - + {cat.nome} @@ -415,13 +415,13 @@ export default function Glossario() { id={categoria.nome.toLowerCase().replace(/\s+/g, '-')} className="scroll-mt-20" > -
+
{/* Header da Categoria */} -
+
{categoria.icone}
-

+

{categoria.nome}

@@ -431,20 +431,20 @@ export default function Glossario() { {categoria.termos.map((item, idx) => (
-

+

{item.termo}

-

+

{item.definicao}

-
+
- + 💡 Exemplo: -

+

{item.exemplo}

diff --git a/public/apostilas/1/index.html b/public/apostilas/1/index.html index 95e218e..1150b97 100644 --- a/public/apostilas/1/index.html +++ b/public/apostilas/1/index.html @@ -41,7 +41,7 @@
Material de apoio - Uso livre para fins educacionais + Formação presencial · uso livre para fins educacionais
@@ -147,6 +147,7 @@ 8Como escolher a série do seu projeto 9Prática: lendo um DHT11 com o ESP32-S3 10Glossário e próximos passos + ·Créditos

@@ -1843,11 +1844,115 @@ Temperatura: 26.1 C | Umidade: 62.0 %

-

- Material produzido para a LASER, Liga Acadêmica de Sistemas Embarcados do Centro de Informática - da UFPE. Dados técnicos conferidos nos datasheets oficiais da - Espressif e nas definições de hardware do ESP-IDF. Este é um material independente, sem - vínculo com a Espressif Systems. +

+ + +
+
+
+

Créditos

+

Quem fez, para quem, e o que você pode fazer com este material.

+
+
+ +
+

Sobre este material

+

+ Esta apostila é material de apoio a uma formação presencial da LASER. + Ela foi escrita para ser lida junto com o encontro, não no lugar dele: os capítulos + seguem a ordem da aula, e o capítulo 9 é a prática que acontece na bancada, com o + hardware em mãos e monitoria no local. +

+

+ Ela também funciona sozinha, como referência para consultar depois. As tabelas + comparativas, a lista de pinos com restrição e o guia de solução de problemas foram + escritos pensando em quem volta ao texto meses depois, no meio de um projeto. +

+
+ +

Realização

+ + + + + + + + + + + + + + + + + + + + + +
PapelQuem
RealizaçãoLASER, Liga Acadêmica de Sistemas Embarcados
+ Centro de Informática · Universidade Federal de Pernambuco
+ laser.cin.ufpe.br
Conteúdo e redaçãoLucas Rayan Guerra
+ Ciência Embarcada · cienciaembarcada.com.br
Dados técnicosESPDocs, plataforma de documentação do ESP32 em português
+ espdocs.cienciaembarcada.com.br
Prática e esquemáticoMontagem em Fritzing, arquivo editável disponível para download no capítulo 9
+ +
+

Fontes e verificação

+ +

+ Nenhum número desta apostila foi estimado. Cada dado tem origem rastreável: +

+ + + + + + + + + + + + + +
InformaçãoOrigem
Arquitetura, memória, periféricosDatasheets oficiais da Espressif Systems
Blocos de segurança por sérieDefinições de hardware do ESP-IDF (soc_caps.h)
Versão mínima do ESP-IDFÍndice de versões publicado pela Espressif
Suporte no core ArduinoRepositório oficial arduino-esp32
EncapsulamentoDatasheets oficiais, capítulo de packaging
Pinos com restriçãoDatasheets, cruzados com a base de pinagem do ESPDocs
Diagrama de blocos do ESP32-S3Imagem da Espressif Systems, reproduzida para fins didáticos
Onde a Espressif ainda não publicou documentação, como no ESP32-H4, isso está dito no texto em vez de preenchido por estimativa.
+
+ +

Uso e distribuição

+ +

+ Este material pode ser usado, copiado e adaptado livremente para fins + educacionais, desde que citada a origem. Se for adaptá-lo para outra formação, + mantenha os créditos desta página e ajuste as partes que dependem do contexto da LASER. +

+ +
+
Independência
+

+ Este é um material independente, produzido pela LASER, sem vínculo, patrocínio + ou revisão da Espressif Systems. Os nomes ESP32, ESP-IDF e demais marcas + citadas pertencem aos seus respectivos titulares. +

+

+ Para decisão de engenharia ou projeto industrial, a referência final é sempre o + datasheet e a errata do fabricante, na revisão de silício que você tem em mãos. +

+
+ +
+
Encontrou um erro?
+

+ Correções e sugestões são bem-vindas e ajudam as próximas turmas. Reporte pelo ESPDocs, + em espdocs.cienciaembarcada.com.br, + ou fale com a coordenação da LASER. +

+
+ +

+ LASER · Liga Acadêmica de Sistemas Embarcados · Centro de Informática · UFPE
+ Formação em ESP32 · Módulo 1: Conhecendo a Família

diff --git a/scripts/auditar-temas.mjs b/scripts/auditar-temas.mjs index f3dad85..6a2316d 100644 --- a/scripts/auditar-temas.mjs +++ b/scripts/auditar-temas.mjs @@ -9,7 +9,12 @@ * 2. PAR INVERTIDO: no tema escuro o texto deveria clarear (número menor) * e o fundo/borda deveria escurecer (número maior). * 3. CONTRASTE BAIXO: par abaixo de 4.5:1 (AA para texto). - * 4. COR FIXA INLINE: style={{ borderColor: 'rgba(...)' }} com valor literal. + * 4. SEM TEMA ESCURO: arquivo que pinta cor mas não tem nenhuma variante + * dark:. Foi assim que o glossário passou meses só com + * tema claro sem ninguém notar. + * 5. FAMÍLIA FORA DO PADRÃO: gray/zinc/neutral/stone no lugar de slate. Além + * de destoar, escapa das checagens acima, que olham slate. + * 6. COR FIXA INLINE: style={{ borderColor: 'rgba(...)' }} com valor literal. * Style inline vence classe, então anula a cor do tema * (inclusive as variantes dark: e hover:). Cor vinda do * dado (serie.cor) é permitida, ver DESIGN.md seção 5. @@ -79,6 +84,13 @@ const RX_TEXTO_INVERTIDO = /\btext-white dark:text-slate-(?:[5-9]\d0|950)\b/; const RX_BLOCO_STYLE = /style=\{\{([\s\S]*?)\}\}/g; // Cor literal: aspas seguidas de #, rgb(), hsl() ou nome de cor. const RX_COR_FIXA = /\b(borderColor|backgroundColor|color)\s*:\s*['"](?:#|rgb|hsl|transparent|white|black)/g; +// Qualquer classe que pinte cor, para medir se o arquivo tem tema escuro. +const RX_PINTA = /\b(?:text|bg|border|divide|from|via|to)-(?:slate|gray|zinc|neutral|stone|white)(?:-\d+)?(?:\/\d+)?\b/g; +// Famílias de cinza fora do padrão do projeto (que é slate). +const RX_FAMILIA_ERRADA = /\b(?:text|bg|border|divide|from|via|to)-(gray|zinc|neutral|stone)-\d+/g; +// Variante dark: DE VERDADE, seguida de um utilitário. Procurar só por "dark:" +// daria falso negativo: a palavra aparece em comentário e em texto visível. +const RX_VARIANTE_DARK = /\bdark:(?:hover:|focus:|group-hover:|active:)?[a-z]+-/; /** Cor literal dentro de style={{}}: vence a classe e mata a variante dark:. */ function coresFixasInline(arq) { @@ -99,11 +111,26 @@ function main() { const invertidos = []; const fracos = []; const fixas = []; + const semTemaEscuro = []; + const familiaErrada = new Map(); for (const arq of arquivos()) { fixas.push(...coresFixasInline(arq)); - const linhas = readFileSync(arq, "utf8").split("\n"); + const bruto = readFileSync(arq, "utf8"); + // Um arquivo que pinta muita cor e não tem nenhuma variante dark: quase + // certamente não acompanha o tema. + const pinta = (bruto.match(RX_PINTA) || []).length; + const temDark = RX_VARIANTE_DARK.test(bruto); + if (pinta >= 8 && !temDark) { + semTemaEscuro.push([arq, `${pinta} classes de cor, nenhuma variante dark:`]); + } + for (const m of bruto.matchAll(RX_FAMILIA_ERRADA)) { + const chave = `${arq} ${m[1]}-*`; + familiaErrada.set(chave, (familiaErrada.get(chave) ?? 0) + 1); + } + + const linhas = bruto.split("\n"); linhas.forEach((linha, i) => { const n = i + 1; @@ -163,13 +190,25 @@ function main() { } } + secao("ARQUIVOS SEM TEMA ESCURO", semTemaEscuro); + + if (familiaErrada.size) { + const total = [...familiaErrada.values()].reduce((a, b) => a + b, 0); + console.log(`\nFAMÍLIA DE CINZA FORA DO PADRÃO: ${total}`); + console.log(" (o projeto usa slate; gray/zinc/neutral/stone destoam e escapam da auditoria)"); + for (const [k, v] of [...familiaErrada].sort((a, b) => b[1] - a[1])) { + console.log(` ${v}x ${k}`); + } + } + secao("PARES INVERTIDOS", invertidos); secao(`PARES ABAIXO DE AA (${AA}:1)`, fracos); secao("COR FIXA EM STYLE INLINE", fixas); const total = [...inexistentes.values()].reduce((a, b) => a + b, 0) + - invertidos.length + fracos.length + fixas.length; + [...familiaErrada.values()].reduce((a, b) => a + b, 0) + + invertidos.length + fracos.length + fixas.length + semTemaEscuro.length; console.log(`\n${total === 0 ? "OK, nenhum problema." : `${total} problema(s).`}`); console.log("Exceção legítima: painel escuro nos DOIS temas (tooltip, bloco de código)."); console.log("Nesses casos não use variante dark:. Ver DESIGN.md seção 4.");