refactor(ui): unify css tokens under single light theme
This commit is contained in:
@@ -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
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|||||||
+12
-27
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user