refactor(ui): unify css tokens under single light theme

This commit is contained in:
Claudecio Martins
2026-10-20 21:02:31 -03:00
parent 4010a9bba5
commit 14b342c3a2
3 changed files with 15 additions and 126 deletions
@@ -3,14 +3,11 @@
.cyber-action-card, .cyber-action-card,
.cyber-side-card, .cyber-side-card,
.cyber-header-bar, .cyber-header-bar,
.theme-toggle-btn,
.cyber-status-badge { .cyber-status-badge {
-fx-effect: null !important; -fx-effect: null !important;
} }
.theme-light .cyber-action-card:hover, .cyber-action-card:hover,
.theme-dark .cyber-action-card:hover, .cyber-side-card:hover {
.cyber-side-card:hover,
.theme-toggle-btn:hover {
-fx-effect: null !important; -fx-effect: null !important;
} }
@@ -10,15 +10,7 @@
.root { .root {
-fx-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; -fx-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-fx-font-size: 13px; -fx-font-size: 13px;
-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;
}
/* --- Tema Claro (Light Theme) --- */
.theme-light {
/* Superfícies & Fundos */ /* Superfícies & Fundos */
-fx-color-background: #f1f5f9; -fx-color-background: #f1f5f9;
-fx-color-surface: #ffffff; -fx-color-surface: #ffffff;
@@ -73,7 +65,7 @@
-fx-control-inner-background: -fx-color-surface; -fx-control-inner-background: -fx-color-surface;
-fx-text-fill: -fx-color-text; -fx-text-fill: -fx-color-text;
/* Tokens semânticos complementares e aliases de compatibilidade */ /* Tokens semânticos essenciais usados pelo Java */
-fx-text-primary: -fx-color-text; -fx-text-primary: -fx-color-text;
-fx-text-secondary: -fx-color-text-muted; -fx-text-secondary: -fx-color-text-muted;
-fx-bg-primary: -fx-color-surface; -fx-bg-primary: -fx-color-surface;
@@ -81,70 +73,6 @@
-fx-accent: -fx-color-primary; -fx-accent: -fx-color-primary;
} }
/* --- Tema Escuro (Dark Theme) --- */
.theme-dark {
/* Superfícies & Fundos */
-fx-color-background: #090d16;
-fx-color-surface: #111827;
-fx-color-surface-hover: #1e293b;
-fx-color-surface-active: #334155;
-fx-color-surface-subtle: #0f172a;
/* Bordas */
-fx-color-border: #334155;
-fx-color-border-subtle: #1e293b;
-fx-color-border-hover: #475569;
/* Tipografia (WCAG AA: contraste >= 4.5:1) */
-fx-color-text: #f8fafc;
-fx-color-text-muted: #94a3b8;
-fx-color-text-inverse: #0f172a;
/* Primária (Azul da Marca CyberCore Dark) */
-fx-color-primary: #38bdf8;
-fx-color-primary-hover: #0284c7;
-fx-color-primary-active: #0369a1;
-fx-color-primary-subtle: rgba(56, 189, 248, 0.15);
/* Sucesso */
-fx-color-success: #34d399;
-fx-color-success-hover: #10b981;
-fx-color-success-subtle: rgba(52, 211, 153, 0.15);
-fx-color-success-border: rgba(52, 211, 153, 0.4);
/* Aviso */
-fx-color-warning: #fbbf24;
-fx-color-warning-hover: #f59e0b;
-fx-color-warning-subtle: rgba(251, 191, 36, 0.15);
-fx-color-warning-border: rgba(251, 191, 36, 0.4);
/* Erro */
-fx-color-error: #f87171;
-fx-color-error-hover: #ef4444;
-fx-color-error-subtle: rgba(248, 113, 113, 0.15);
-fx-color-error-border: rgba(248, 113, 113, 0.4);
/* Foco & Realce */
-fx-color-focus: #38bdf8;
/* Sombras & Efeitos */
-fx-color-shadow: rgba(0, 0, 0, 0.4);
-fx-color-shadow-hover: rgba(0, 0, 0, 0.6);
/* Lookups nativos do JavaFX */
-fx-base: -fx-color-background;
-fx-background: -fx-color-background;
-fx-control-inner-background: -fx-color-surface;
-fx-text-fill: -fx-color-text;
/* Tokens semânticos complementares e aliases de compatibilidade */
-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;
}
/* ========================================================================== /* ==========================================================================
2. REGRAS GERAIS DE ESTRUTURA E CONTAINERS 2. REGRAS GERAIS DE ESTRUTURA E CONTAINERS
========================================================================== */ ========================================================================== */
@@ -213,27 +141,6 @@
-fx-font-weight: bold; -fx-font-weight: bold;
} }
/* Botão de Alternância de Tema */
.theme-toggle-btn {
-fx-background-color: -fx-color-surface;
-fx-border-color: -fx-color-border;
-fx-border-radius: 18px;
-fx-border-width: 1px;
-fx-background-radius: 18px;
-fx-text-fill: -fx-color-text;
-fx-padding: 4px 12px;
-fx-font-size: 11px;
-fx-font-weight: bold;
-fx-cursor: hand;
-fx-effect: dropshadow(three-pass-box, -fx-color-shadow, 4, 0, 0, 1);
}
.theme-toggle-btn:hover {
-fx-background-color: -fx-color-surface-hover;
-fx-border-color: -fx-color-primary;
-fx-text-fill: -fx-color-primary;
}
/* ========================================================================== /* ==========================================================================
3. HUB DE AÇÕES E PAINEL LATERAL 3. HUB DE AÇÕES E PAINEL LATERAL
========================================================================== */ ========================================================================== */
@@ -46,8 +46,8 @@ class DesignTokenGuardrailTest {
continue; continue;
} }
// Detecta entrada nos blocos de tokens .theme-light e .theme-dark // Detecta entrada no bloco canônico de tokens .root
if (trimmed.contains(".theme-light") || trimmed.contains(".theme-dark")) { if (trimmed.startsWith(".root") || trimmed.contains(".root ")) {
insideTokenBlock = true; insideTokenBlock = true;
} }
@@ -248,8 +248,8 @@ class DesignTokenGuardrailTest {
} }
@Test @Test
@DisplayName("(c) Relação de contraste WCAG 2.1 deve ser >= 4.5:1 para texto normal nos temas claro e escuro") @DisplayName("(c) Relação de contraste WCAG 2.1 deve ser >= 4.5:1 para texto normal no tema único (.root)")
void shouldMeetWcagContrastRequirementsInBothThemes() throws Exception { void shouldMeetWcagContrastRequirementsInTheme() throws Exception {
InputStream stream = getClass().getResourceAsStream("/styles/theme.css"); InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
assertThat(stream).isNotNull(); assertThat(stream).isNotNull();
@@ -258,14 +258,11 @@ class DesignTokenGuardrailTest {
content = new String(stream.readAllBytes(), StandardCharsets.UTF_8); content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
} }
// Extrai mapa de tokens por tema (.theme-light e .theme-dark) // Extrai mapa de tokens definidos no bloco .root
java.util.Map<String, String> lightTokens = extractTokensFromBlock(content, ".theme-light"); java.util.Map<String, String> tokens = extractTokensFromBlock(content, ".root");
java.util.Map<String, String> darkTokens = extractTokensFromBlock(content, ".theme-dark"); assertThat(tokens).isNotEmpty();
assertThat(lightTokens).isNotEmpty(); // Pares a validar: (texto, fundo)
assertThat(darkTokens).isNotEmpty();
// Pares a validar em cada tema: (texto, fundo)
String[][] pairs = { String[][] pairs = {
{"-fx-color-text", "-fx-color-background"}, {"-fx-color-text", "-fx-color-background"},
{"-fx-color-text", "-fx-color-surface"}, {"-fx-color-text", "-fx-color-surface"},
@@ -281,24 +278,12 @@ class DesignTokenGuardrailTest {
String textToken = pair[0]; String textToken = pair[0];
String bgToken = pair[1]; String bgToken = pair[1];
// Tema Claro if (tokens.containsKey(textToken) && tokens.containsKey(bgToken)) {
if (lightTokens.containsKey(textToken) && lightTokens.containsKey(bgToken)) { String textColor = tokens.get(textToken);
String textColor = lightTokens.get(textToken); String bgColor = tokens.get(bgToken);
String bgColor = lightTokens.get(bgToken);
double ratio = calculateContrastRatio(textColor, bgColor); double ratio = calculateContrastRatio(textColor, bgColor);
if (ratio < 4.5) { if (ratio < 4.5) {
contrastFailures.add(String.format("Tema Claro: %s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)", contrastFailures.add(String.format("%s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)",
textToken, textColor, bgToken, bgColor, ratio));
}
}
// Tema Escuro
if (darkTokens.containsKey(textToken) && darkTokens.containsKey(bgToken)) {
String textColor = darkTokens.get(textToken);
String bgColor = darkTokens.get(bgToken);
double ratio = calculateContrastRatio(textColor, bgColor);
if (ratio < 4.5) {
contrastFailures.add(String.format("Tema Escuro: %s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)",
textToken, textColor, bgToken, bgColor, ratio)); textToken, textColor, bgToken, bgColor, ratio));
} }
} }