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

+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.");