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-side-card,
.cyber-header-bar,
.theme-toggle-btn,
.cyber-status-badge {
-fx-effect: null !important;
}
.theme-light .cyber-action-card:hover,
.theme-dark .cyber-action-card:hover,
.cyber-side-card:hover,
.theme-toggle-btn:hover {
.cyber-action-card:hover,
.cyber-side-card:hover {
-fx-effect: null !important;
}
@@ -10,15 +10,7 @@
.root {
-fx-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-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 */
-fx-color-background: #f1f5f9;
-fx-color-surface: #ffffff;
@@ -73,7 +65,7 @@
-fx-control-inner-background: -fx-color-surface;
-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-secondary: -fx-color-text-muted;
-fx-bg-primary: -fx-color-surface;
@@ -81,70 +73,6 @@
-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
========================================================================== */
@@ -213,27 +141,6 @@
-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
========================================================================== */
@@ -46,8 +46,8 @@ class DesignTokenGuardrailTest {
continue;
}
// Detecta entrada nos blocos de tokens .theme-light e .theme-dark
if (trimmed.contains(".theme-light") || trimmed.contains(".theme-dark")) {
// Detecta entrada no bloco canônico de tokens .root
if (trimmed.startsWith(".root") || trimmed.contains(".root ")) {
insideTokenBlock = true;
}
@@ -248,8 +248,8 @@ class DesignTokenGuardrailTest {
}
@Test
@DisplayName("(c) Relação de contraste WCAG 2.1 deve ser >= 4.5:1 para texto normal nos temas claro e escuro")
void shouldMeetWcagContrastRequirementsInBothThemes() throws Exception {
@DisplayName("(c) Relação de contraste WCAG 2.1 deve ser >= 4.5:1 para texto normal no tema único (.root)")
void shouldMeetWcagContrastRequirementsInTheme() throws Exception {
InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
assertThat(stream).isNotNull();
@@ -258,14 +258,11 @@ class DesignTokenGuardrailTest {
content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
}
// Extrai mapa de tokens por tema (.theme-light e .theme-dark)
java.util.Map<String, String> lightTokens = extractTokensFromBlock(content, ".theme-light");
java.util.Map<String, String> darkTokens = extractTokensFromBlock(content, ".theme-dark");
// Extrai mapa de tokens definidos no bloco .root
java.util.Map<String, String> tokens = extractTokensFromBlock(content, ".root");
assertThat(tokens).isNotEmpty();
assertThat(lightTokens).isNotEmpty();
assertThat(darkTokens).isNotEmpty();
// Pares a validar em cada tema: (texto, fundo)
// Pares a validar: (texto, fundo)
String[][] pairs = {
{"-fx-color-text", "-fx-color-background"},
{"-fx-color-text", "-fx-color-surface"},
@@ -281,24 +278,12 @@ class DesignTokenGuardrailTest {
String textToken = pair[0];
String bgToken = pair[1];
// Tema Claro
if (lightTokens.containsKey(textToken) && lightTokens.containsKey(bgToken)) {
String textColor = lightTokens.get(textToken);
String bgColor = lightTokens.get(bgToken);
if (tokens.containsKey(textToken) && tokens.containsKey(bgToken)) {
String textColor = tokens.get(textToken);
String bgColor = tokens.get(bgToken);
double ratio = calculateContrastRatio(textColor, bgColor);
if (ratio < 4.5) {
contrastFailures.add(String.format("Tema Claro: %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)",
contrastFailures.add(String.format("%s (%s) sobre %s (%s) = %.2f:1 (mínimo 4.5:1)",
textToken, textColor, bgToken, bgColor, ratio));
}
}