feat: enhance accessibility and theme consistency in components

This commit is contained in:
Lucas Rayan committed 2026-08-20 14:51:58 -03:00
1 parent a279269f8f
commit 3de4f2501d
3 files changed
+170 -26

No files matched your search

+17 -17
View File
@@ -363,22 +363,22 @@ export default function Glossario() {
];
return (
<div className="min-h-screen bg-linear-to-br from-gray-50 via-purple-50 to-pink-50">
<div className="min-h-screen bg-linear-to-br from-slate-100 via-slate-50 to-purple-100/40 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900 transition-colors">
<Header />
<main id="conteudo" className="px-4 pt-16 pb-20 max-w-7xl mx-auto">
{/* Hero Section */}
<section className="text-center mb-16">
<div className="inline-flex items-center gap-2 bg-white backdrop-blur-sm px-4 py-2 rounded-full mb-6 border border-purple-200 shadow-sm">
<div className="inline-flex items-center gap-2 bg-white dark:bg-slate-900 px-4 py-2 rounded-full mb-6 border border-purple-200 dark:border-purple-500/30 shadow-sm">
<span className="text-xl">📖</span>
<span className="text-sm font-semibold text-gray-700">Aprenda os Conceitos</span>
<span className="text-sm font-semibold text-slate-700 dark:text-slate-300">Aprenda os Conceitos</span>
</div>
<h1 className="text-5xl md:text-7xl font-black mb-6 bg-clip-text text-transparent bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 leading-tight tracking-tight">
Glossário Técnico
</h1>
<p className="text-lg md:text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed">
<p className="text-lg md:text-xl text-slate-600 dark:text-slate-400 max-w-3xl mx-auto leading-relaxed">
Entenda termos e conceitos do ecossistema ESP32 explicados em português,
do básico ao avançado.
</p>
@@ -386,8 +386,8 @@ export default function Glossario() {
{/* Navegação Rápida */}
<section className="mb-12">
<div className="bg-white rounded-3xl shadow-lg p-8 border-2 border-gray-100">
<h2 className="text-2xl font-bold text-gray-900 mb-6 text-center">
<div className="bg-white dark:bg-slate-900/40 rounded-3xl shadow-lg p-8 border-2 border-slate-200 dark:border-slate-800/80">
<h2 className="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-6 text-center">
Navegação Rápida
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
@@ -395,10 +395,10 @@ export default function Glossario() {
<a
key={cat.nome}
href={`#${cat.nome.toLowerCase().replace(/\s+/g, '-')}`}
className="flex flex-col items-center gap-2 p-4 rounded-xl bg-linear-to-br from-gray-50 to-white border-2 border-gray-100 hover:border-purple-300 hover:shadow-md transition-all"
className="flex flex-col items-center gap-2 p-4 rounded-xl bg-slate-50 dark:bg-slate-950/40 border-2 border-slate-200 dark:border-slate-800/60 hover:border-purple-300 dark:hover:border-purple-500/50 hover:shadow-md transition-all"
>
<span className="text-4xl">{cat.icone}</span>
<span className="text-sm font-bold text-gray-800 text-center">
<span className="text-sm font-bold text-slate-800 dark:text-slate-200 text-center">
{cat.nome}
</span>
</a>
@@ -415,13 +415,13 @@ export default function Glossario() {
id={categoria.nome.toLowerCase().replace(/\s+/g, '-')}
className="scroll-mt-20"
>
<div className="bg-white rounded-3xl shadow-xl p-8 border-2 border-gray-100">
<div className="bg-white dark:bg-slate-900/40 rounded-3xl shadow-xl p-8 border-2 border-slate-200 dark:border-slate-800/80">
{/* Header da Categoria */}
<div className="flex items-center gap-4 mb-8 pb-6 border-b-2 border-gray-100">
<div className="flex items-center gap-4 mb-8 pb-6 border-b-2 border-slate-200 dark:border-slate-800/80">
<div className={`w-16 h-16 bg-linear-to-br from-${categoria.cor}-500 to-${categoria.cor}-600 rounded-2xl flex items-center justify-center shadow-lg`}>
<span className="text-3xl">{categoria.icone}</span>
</div>
<h2 className="text-3xl font-black text-gray-900">
<h2 className="text-3xl font-black text-slate-900 dark:text-slate-100">
{categoria.nome}
</h2>
</div>
@@ -431,20 +431,20 @@ export default function Glossario() {
{categoria.termos.map((item, idx) => (
<article
key={idx}
className="bg-linear-to-br from-gray-50 to-white p-6 rounded-2xl border-2 border-gray-100 hover:border-purple-200 transition-colors"
className="bg-slate-50 dark:bg-slate-950/40 p-6 rounded-2xl border-2 border-slate-200 dark:border-slate-800/60 hover:border-purple-200 dark:hover:border-purple-500/40 transition-colors"
>
<h3 className="text-xl font-bold text-gray-900 mb-3">
<h3 className="text-xl font-bold text-slate-900 dark:text-slate-100 mb-3">
{item.termo}
</h3>
<p className="text-gray-700 leading-relaxed mb-4">
<p className="text-slate-700 dark:text-slate-300 leading-relaxed mb-4">
{item.definicao}
</p>
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-lg">
<div className="bg-blue-50 dark:bg-blue-950/30 border-l-4 border-blue-500 p-4 rounded-lg">
<div className="flex items-start gap-2">
<span className="text-blue-600 font-bold text-sm shrink-0 mt-0.5">
<span className="text-blue-700 dark:text-blue-400 font-bold text-sm shrink-0 mt-0.5">
💡 Exemplo:
</span>
<p className="text-blue-900 text-sm leading-relaxed">
<p className="text-blue-900 dark:text-blue-200 text-sm leading-relaxed">
{item.exemplo}
</p>
</div>
+111 -6
View File
@@ -41,7 +41,7 @@
</div>
<div style="text-align:right">
<strong>Material de apoio</strong>
Uso livre para fins educacionais
Formação presencial · uso livre para fins educacionais
</div>
</div>
</div>
@@ -147,6 +147,7 @@
<a href="#cap8"><span class="num">8</span><span class="txt">Como escolher a série do seu projeto</span><span class="pontos"></span></a>
<a href="#cap9"><span class="num">9</span><span class="txt">Prática: lendo um DHT11 com o ESP32-S3</span><span class="pontos"></span></a>
<a href="#cap10"><span class="num">10</span><span class="txt">Glossário e próximos passos</span><span class="pontos"></span></a>
<a href="#creditos"><span class="num">·</span><span class="txt">Créditos</span><span class="pontos"></span></a>
</nav>
</section>
@@ -1843,11 +1844,115 @@ Temperatura: 26.1 C | Umidade: 62.0 %</code></pre>
</p>
</div>
<p style="margin-top:10mm;padding-top:4mm;border-top:0.8pt solid var(--linha);font-size:8.5pt;color:var(--tinta-3)">
Material produzido para a <strong>LASER</strong>, 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.
</section>
<!-- ══════════════════════════ CRÉDITOS ══════════════════════════ -->
<section class="capitulo" id="creditos">
<div class="capitulo-abertura">
<div>
<h2>Créditos</h2>
<p class="subtitulo">Quem fez, para quem, e o que você pode fazer com este material.</p>
</div>
</div>
<div class="grupo">
<h3>Sobre este material</h3>
<p class="junto">
Esta apostila é <strong>material de apoio a uma formação presencial</strong> 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.
</p>
<p>
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.
</p>
</div>
<h3>Realização</h3>
<table>
<thead><tr><th style="width:30%">Papel</th><th>Quem</th></tr></thead>
<tbody>
<tr>
<td class="destaque">Realização</td>
<td><strong>LASER</strong>, Liga Acadêmica de Sistemas Embarcados<br>
Centro de Informática · Universidade Federal de Pernambuco<br>
<a href="https://laser.cin.ufpe.br/">laser.cin.ufpe.br</a></td>
</tr>
<tr>
<td class="destaque">Conteúdo e redação</td>
<td><strong>Lucas Rayan Guerra</strong><br>
Ciência Embarcada · <a href="https://cienciaembarcada.com.br">cienciaembarcada.com.br</a></td>
</tr>
<tr>
<td class="destaque">Dados técnicos</td>
<td>ESPDocs, plataforma de documentação do ESP32 em português<br>
<a href="https://espdocs.cienciaembarcada.com.br/">espdocs.cienciaembarcada.com.br</a></td>
</tr>
<tr>
<td class="destaque">Prática e esquemático</td>
<td>Montagem em Fritzing, arquivo editável disponível para download no capítulo 9</td>
</tr>
</tbody>
</table>
<div class="grupo">
<h3>Fontes e verificação</h3>
<p class="junto">
Nenhum número desta apostila foi estimado. Cada dado tem origem rastreável:
</p>
<table>
<thead><tr><th style="width:38%">Informação</th><th>Origem</th></tr></thead>
<tbody>
<tr><td class="destaque">Arquitetura, memória, periféricos</td><td>Datasheets oficiais da Espressif Systems</td></tr>
<tr><td class="destaque">Blocos de segurança por série</td><td>Definições de hardware do ESP-IDF (<code>soc_caps.h</code>)</td></tr>
<tr><td class="destaque">Versão mínima do ESP-IDF</td><td>Índice de versões publicado pela Espressif</td></tr>
<tr><td class="destaque">Suporte no core Arduino</td><td>Repositório oficial <code>arduino-esp32</code></td></tr>
<tr><td class="destaque">Encapsulamento</td><td>Datasheets oficiais, capítulo de <em>packaging</em></td></tr>
<tr><td class="destaque">Pinos com restrição</td><td>Datasheets, cruzados com a base de pinagem do ESPDocs</td></tr>
<tr><td class="destaque">Diagrama de blocos do ESP32-S3</td><td>Imagem da Espressif Systems, reproduzida para fins didáticos</td></tr>
</tbody>
<caption>Onde a Espressif ainda não publicou documentação, como no ESP32-H4, isso está dito no texto em vez de preenchido por estimativa.</caption>
</table>
</div>
<h3>Uso e distribuição</h3>
<p>
Este material pode ser <strong>usado, copiado e adaptado livremente para fins
educacionais</strong>, 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.
</p>
<div class="caixa atencao">
<div class="caixa-titulo">Independência</div>
<p>
Este é um material independente, produzido pela LASER, <strong>sem vínculo, patrocínio
ou revisão da Espressif Systems</strong>. Os nomes ESP32, ESP-IDF e demais marcas
citadas pertencem aos seus respectivos titulares.
</p>
<p>
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.
</p>
</div>
<div class="caixa dica">
<div class="caixa-titulo">Encontrou um erro?</div>
<p>
Correções e sugestões são bem-vindas e ajudam as próximas turmas. Reporte pelo ESPDocs,
em <a href="https://espdocs.cienciaembarcada.com.br/">espdocs.cienciaembarcada.com.br</a>,
ou fale com a coordenação da LASER.
</p>
</div>
<p style="margin-top:12mm;padding-top:4mm;border-top:0.8pt solid var(--linha);font-size:8.5pt;color:var(--tinta-3);text-align:center">
LASER · Liga Acadêmica de Sistemas Embarcados · Centro de Informática · UFPE<br>
Formação em ESP32 · Módulo 1: Conhecendo a Família
</p>
</section>
+42 -3
View File
@@ -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.");