fix(ui): resolve dark theme text contrast bug and eliminate inline color styles
CI Pipeline / Build & Verify (Linux x86_64) (push) Canceled after 0s
CI Pipeline / Build & Verify (Windows) (push) Canceled after 0s

This commit is contained in:
Claudecio Martins
2026-10-20 20:52:08 -03:00
parent caaa1de8a0
commit 4010a9bba5
35 changed files with 678 additions and 73 deletions
+79
View File
@@ -0,0 +1,79 @@
# UI Troubleshooting & Resolução de Estilos (JavaFX CSS)
Este documento registra a investigação da causa raiz de problemas visuais na interface gráfica do CyberCore Signer, em especial a falha de renderização de textos escuros sobre fundo escuro no tema escuro.
---
## 1. O Problema: Texto Preto sobre Fundo Escuro (Contraste ~1.1:1)
### Sintoma
No tema escuro (`ThemeManager.AppTheme.DARK` / `.theme-dark`), determinados textos nos diálogos `ExitConfirmationDialog` (texto explicativo da ação), `PdfSignDialog` (nome e metadados do documento PDF selecionado, dica de arraste do selo visual) e `PdfCertificateSignDialog` (opção de memorização segura de PIN) eram renderizados com a cor preta (`#000000`) sobre um fundo escuro (`#090d16` ou `#111827`).
O contraste medido entre o texto e o fundo situava-se em aproximadamente **1.1:1 a 1.2:1**, violando completamente as diretrizes de acessibilidade WCAG AA (mínimo de 4.5:1 para texto normal) e tornando o texto praticamente invisível.
---
## 2. Investigação Técnica e Causa Raiz
### 2.1 Lookups Inexistentes no CSS
Ao inspecionar o código-fonte Java dessas telas, verificou-se o uso de estilos inline via `setStyle(...)`:
```java
subtitle.setStyle("-fx-text-fill: -fx-text-secondary; -fx-font-size: 12px;");
lblDocName.setStyle("-fx-font-weight: bold; -fx-font-size: 12.5px; -fx-text-fill: -fx-text-primary;");
lblDocMeta.setStyle("-fx-font-size: 11px; -fx-text-fill: -fx-text-secondary;");
previewBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; ...");
chkRememberPin.setStyle("-fx-font-size: 11.5px; -fx-text-fill: -fx-text-primary;");
```
Os tokens `-fx-text-primary`, `-fx-text-secondary`, `-fx-bg-primary`, `-fx-border-light` e `-fx-accent` **não estavam declarados** no arquivo de folha de estilos `theme.css`.
### 2.2 Por que o JavaFX rebaixa para preto (`#000000`)?
No motor de estilização do JavaFX (`com.sun.javafx.css` / Modena):
1. Quando uma declaração inline em `setStyle(...)` utiliza uma cor de lookup (`-fx-text-fill: <variavel>`), o analisador CSS do JavaFX tenta resolver essa variável percorrendo os ancestrais do nó até o `.root` da cena.
2. Caso a variável **não seja encontrada**, o `StyleConverter` do JavaFX não lança exceção em tempo de execução. Em vez disso, seu mecanismo de tolerância a falhas adota o valor fallback padrão da especificação base de controles: **`Color.BLACK` (`rgba(0, 0, 0, 1.0)`)**.
3. Em um tema claro, o fundo é branco ou cinza claro (`#ffffff` / `#f1f5f9`), de modo que o texto preto parece ter funcionado por pura coincidência de luminância.
4. No tema escuro, no entanto, o fundo da aplicação é escuro (`#090d16` a `#111827`), fazendo com que o fallback para `#000000` destrua o contraste da interface.
### 2.3 Problemas Adicionais com `setStyle(...)`
- **Prioridade Forçada:** `setStyle(...)` tem especificidade de estilo inline (prioridade máxima), sobrescrevendo quaisquer regras de temas `.theme-light` e `.theme-dark` que tentem ajustar o elemento.
- **Acoplamento:** Espalha lógica visual e regras de cor pelo código Java, dificultando testes de regressão automatizados e auditorias de contraste.
---
## 3. Diretrizes e Solução Adotada
### 3.1 Definição Completa de Tokens e Aliases no `theme.css`
Todos os tokens semânticos e seus aliases de compatibilidade são declarados formalmente nos blocos canônicos de tokens:
```css
.theme-light {
-fx-text-primary: -fx-color-text;
-fx-text-secondary: -fx-color-text-muted;
-fx-bg-primary: -fx-color-surface;
-fx-border-light: -fx-color-border-subtle;
-fx-accent: -fx-color-primary;
}
.theme-dark {
-fx-text-primary: -fx-color-text;
-fx-text-secondary: -fx-color-text-muted;
-fx-bg-primary: -fx-color-surface;
-fx-border-light: -fx-color-border;
-fx-accent: -fx-color-primary;
}
```
### 3.2 Eliminação de Cores Inline
É terminantemente proibido utilizar `setStyle(...)` com propriedades que afetem cores:
- Proibidos em `setStyle`: `-fx-text-fill`, `-fx-background-color`, `-fx-border-color`, `-fx-fill`, `-fx-stroke` ou códigos `#hex`.
- Permitidos em `setStyle` exclusivamente quando estritamente necessário: geometria e layout (`-fx-min-width`, `-fx-pref-width`, `-fx-padding`).
Toda estilização cromática deve ser feita atribuindo classes semânticas:
- `.text-primary`: Texto principal com alta luminância no escuro e baixa no claro.
- `.text-secondary`, `.text-muted`: Texto secundário / auxiliar com contraste garantido >= 4.5:1.
- `.text-accent`: Destaque com a cor primária da marca.
- `.surface-box`, `.toolbar-box`: Caixas de superfície neutra e barras de ferramentas.
### 3.3 Testes Automatizados de Guarda-Corpo
O projeto mantém testes unitários automatizados em `DesignTokenGuardrailTest` e `CssColorAndContrastGuardrailTest` que:
1. Fazem varredura estática de todo o código Java da UI para reprovar qualquer `setStyle` que declare cor ou hexadecimal.
2. Validam matematicamente a fórmula de luminância relativa WCAG 2.1 para cada par texto/fundo nos dois temas, garantindo conformidade rigorosa.