style(dashboard): redesign Neon Trading Dashboard ispirato a screenshot riferimento

Sostituita palette Modern Fintech con look "neon trading" pink+cyan:
- BG #0A0A0F near-black con dual radial glow (pink top-left, cyan bottom-right)
- Surface #13131A card base, hover #1C1C26
- Primary pink #FF2D87 (max fitness, key metrics)
- Secondary cyan #00D9FF (median, entropy)
- Accent amber #FFB800 (p90, warnings)
- Success neon green #00E676, danger neon red #FF3D60

Effetti visivi aggiunti:
- Card top-border glow gradient (linea pink luminosa)
- Card hover: pink shadow esterno + border 0.5 opacity
- Inset shadow per dare profondità (subtle highlight top)
- Brand-dot pulsing animation 2s (glow pink pulsato)
- Header glassmorphic blur(20px) saturate(180%)
- Chart fill area gradient sotto le curve max/entropy
- Linee Plotly spline smoothing 0.6 (curve morbide)
- Marker bordo bianco per pop

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 22:43:48 +02:00
parent 0486e19829
commit 2acba2077b
+80 -46
View File
@@ -5,15 +5,15 @@ Default port 8080. Streamlit resta su 8501 durante la migrazione.
Riusa ``dashboard.data`` (Repository helpers) senza modifiche al backend.
Palette "Modern Fintech Dark":
- BG: #0A0E1A (deep blue-black)
- Surface: #131829 (card base)
- Surface elevata: #1A2138 (hover/active)
- Primary cyan: #06B6D4 (performance highlights)
- Secondary purple: #A78BFA (median, secondary curves)
- Accent amber: #F59E0B (warnings, p90)
- Success green: #10B981, Danger red: #EF4444
- Text: #F1F5F9 (primary), #94A3B8 (muted)
Palette "Neon Trading Dashboard" (ispirata screenshot 2026-05-11):
- BG: #0A0A0F (near-black con tinge blu)
- Surface: #13131A (card base)
- Surface elevata: #1C1C26 (hover/active)
- Primary pink: #FF2D87 (highlight key metrics, max fitness)
- Secondary cyan: #00D9FF (median, secondary curves)
- Accent amber: #FFB800 (warnings, p90)
- Success neon green: #00E676, Danger neon red: #FF3D60
- Text: #FFFFFF (primary), #7A7A8C (muted)
"""
from __future__ import annotations
@@ -38,18 +38,19 @@ from multi_swarm.dashboard.data import (
DB_PATH = os.environ.get("DB_PATH", "./runs.db")
REFRESH_INTERVAL_S = 3.0
# --- Modern Fintech Dark palette ---
COLOR_BG = "#0A0E1A"
COLOR_SURFACE = "#131829"
COLOR_SURFACE_2 = "#1A2138"
COLOR_BORDER = "rgba(148, 163, 184, 0.12)"
COLOR_PRIMARY = "#06B6D4"
COLOR_SECONDARY = "#A78BFA"
COLOR_ACCENT = "#F59E0B"
COLOR_SUCCESS = "#10B981"
COLOR_DANGER = "#EF4444"
COLOR_TEXT = "#F1F5F9"
COLOR_TEXT_MUTED = "#94A3B8"
# --- Neon Trading Dashboard palette ---
COLOR_BG = "#0A0A0F"
COLOR_SURFACE = "#13131A"
COLOR_SURFACE_2 = "#1C1C26"
COLOR_BORDER = "rgba(255, 45, 135, 0.12)"
COLOR_BORDER_HOVER = "rgba(255, 45, 135, 0.45)"
COLOR_PRIMARY = "#FF2D87"
COLOR_SECONDARY = "#00D9FF"
COLOR_ACCENT = "#FFB800"
COLOR_SUCCESS = "#00E676"
COLOR_DANGER = "#FF3D60"
COLOR_TEXT = "#FFFFFF"
COLOR_TEXT_MUTED = "#7A7A8C"
_STATUS_BADGE: dict[str, tuple[str, str]] = {
"running": ("● running", "positive"),
@@ -67,18 +68,40 @@ code, pre, .q-code {{ font-family: 'JetBrains Mono', 'Fira Code', monospace !imp
body, .q-page-container, .q-page {{
background: {COLOR_BG} !important;
color: {COLOR_TEXT};
background-image: radial-gradient(ellipse at top, rgba(6, 182, 212, 0.04) 0%, transparent 50%);
background-image:
radial-gradient(ellipse 800px 400px at 20% 0%, rgba(255, 45, 135, 0.08) 0%, transparent 60%),
radial-gradient(ellipse 600px 400px at 80% 100%, rgba(0, 217, 255, 0.06) 0%, transparent 60%);
background-attachment: fixed;
}}
.q-card {{
background: {COLOR_SURFACE} !important;
color: {COLOR_TEXT} !important;
border: 1px solid {COLOR_BORDER};
border-radius: 12px !important;
box-shadow: 0 1px 2px rgba(0,0,0,0.3), 0 4px 12px rgba(0,0,0,0.15);
transition: border-color 0.2s, transform 0.2s;
border-radius: 14px !important;
box-shadow:
0 1px 2px rgba(0,0,0,0.5),
0 8px 24px rgba(0,0,0,0.25),
inset 0 1px 0 rgba(255,255,255,0.04);
transition: all 0.2s ease;
position: relative;
overflow: hidden;
}}
.q-card::before {{
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 45, 135, 0.4), transparent);
opacity: 0.5;
}}
.q-card:hover {{
border-color: rgba(255, 45, 135, 0.5);
box-shadow:
0 1px 2px rgba(0,0,0,0.5),
0 8px 32px rgba(255, 45, 135, 0.15),
inset 0 1px 0 rgba(255,255,255,0.05);
}}
.q-card:hover {{ border-color: rgba(6, 182, 212, 0.35); }}
.metric-card {{
padding: 20px 16px;
@@ -108,11 +131,11 @@ body, .q-page-container, .q-page {{
.metric-card.accent-green .text-h4 {{ color: {COLOR_SUCCESS} !important; }}
.q-header {{
background: rgba(19, 24, 41, 0.85) !important;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
background: rgba(10, 10, 15, 0.75) !important;
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid {COLOR_BORDER} !important;
box-shadow: none !important;
box-shadow: 0 1px 0 rgba(255, 45, 135, 0.15) !important;
}}
.nav-link {{
@@ -131,7 +154,7 @@ body, .q-page-container, .q-page {{
}}
.nav-link.active {{
color: {COLOR_PRIMARY} !important;
background: rgba(6, 182, 212, 0.08);
background: rgba(255, 45, 135, 0.08);
}}
.nav-link.active::after {{
content: '';
@@ -153,11 +176,16 @@ body, .q-page-container, .q-page {{
gap: 8px;
}}
.brand-dot {{
width: 8px;
height: 8px;
width: 10px;
height: 10px;
border-radius: 50%;
background: {COLOR_PRIMARY};
box-shadow: 0 0 12px {COLOR_PRIMARY};
box-shadow: 0 0 16px {COLOR_PRIMARY}, 0 0 4px {COLOR_PRIMARY};
animation: pulse-pink 2s ease-in-out infinite;
}}
@keyframes pulse-pink {{
0%, 100% {{ box-shadow: 0 0 16px {COLOR_PRIMARY}, 0 0 4px {COLOR_PRIMARY}; }}
50% {{ box-shadow: 0 0 24px {COLOR_PRIMARY}, 0 0 8px {COLOR_PRIMARY}; }}
}}
.q-linear-progress {{ height: 8px !important; border-radius: 6px !important; }}
@@ -187,8 +215,8 @@ body, .q-page-container, .q-page {{
letter-spacing: 0.06em;
}}
.q-table tbody tr {{ transition: background 0.15s; }}
.q-table tbody tr:hover {{ background: rgba(6, 182, 212, 0.05) !important; }}
.q-table tbody tr.selected {{ background: rgba(6, 182, 212, 0.12) !important; }}
.q-table tbody tr:hover {{ background: rgba(255, 45, 135, 0.05) !important; }}
.q-table tbody tr.selected {{ background: rgba(255, 45, 135, 0.12) !important; }}
.q-table td {{ border-bottom: 1px solid {COLOR_BORDER} !important; font-feature-settings: 'tnum'; }}
.text-h6 {{ font-weight: 600 !important; letter-spacing: -0.015em !important; }}
@@ -202,11 +230,11 @@ body, .q-page-container, .q-page {{
}}
code, pre {{
background: #0D1220 !important;
background: #0F0F15 !important;
color: {COLOR_TEXT} !important;
border: 1px solid {COLOR_BORDER};
border-radius: 8px !important;
padding: 12px !important;
border-radius: 10px !important;
padding: 14px !important;
}}
.q-badge {{ border-radius: 6px !important; font-weight: 500 !important; padding: 4px 10px !important; font-size: 12px !important; }}
@@ -312,15 +340,18 @@ def _convergence_figure(gens_df: Any) -> go.Figure:
go.Scatter(
x=gens_df["generation_idx"], y=gens_df["fitness_max"],
name="max", mode="lines+markers",
line={"color": COLOR_PRIMARY, "width": 3},
marker={"size": 9, "color": COLOR_PRIMARY},
line={"color": COLOR_PRIMARY, "width": 3, "shape": "spline", "smoothing": 0.6},
marker={"size": 9, "color": COLOR_PRIMARY,
"line": {"color": "#fff", "width": 1}},
fill="tozeroy",
fillcolor="rgba(255, 45, 135, 0.12)",
)
)
fig.add_trace(
go.Scatter(
x=gens_df["generation_idx"], y=gens_df["fitness_p90"],
name="p90", mode="lines+markers",
line={"color": COLOR_ACCENT, "width": 2, "dash": "dot"},
line={"color": COLOR_ACCENT, "width": 2, "dash": "dot", "shape": "spline"},
marker={"size": 7, "color": COLOR_ACCENT},
)
)
@@ -328,7 +359,7 @@ def _convergence_figure(gens_df: Any) -> go.Figure:
go.Scatter(
x=gens_df["generation_idx"], y=gens_df["fitness_median"],
name="median", mode="lines+markers",
line={"color": COLOR_SECONDARY, "width": 2},
line={"color": COLOR_SECONDARY, "width": 2, "shape": "spline"},
marker={"size": 7, "color": COLOR_SECONDARY},
)
)
@@ -340,7 +371,7 @@ def _convergence_figure(gens_df: Any) -> go.Figure:
xaxis={"title": "generation", "gridcolor": "rgba(148, 163, 184, 0.08)", "dtick": 1},
yaxis={"title": "fitness", "gridcolor": "rgba(148, 163, 184, 0.08)"},
title={"text": "Fitness convergence", "font": {"color": COLOR_TEXT, "size": 18}},
legend={"bgcolor": "rgba(19, 24, 41, 0.95)", "bordercolor": COLOR_PRIMARY, "borderwidth": 1},
legend={"bgcolor": "rgba(19, 19, 26, 0.95)", "bordercolor": COLOR_PRIMARY, "borderwidth": 1},
margin={"l": 50, "r": 30, "t": 50, "b": 50},
)
return fig
@@ -353,8 +384,11 @@ def _entropy_figure(gens_df: Any) -> go.Figure:
go.Scatter(
x=gens_df["generation_idx"], y=gens_df["entropy"],
mode="lines+markers",
line={"color": COLOR_PRIMARY, "width": 3},
marker={"size": 9, "color": COLOR_PRIMARY},
line={"color": COLOR_SECONDARY, "width": 3, "shape": "spline", "smoothing": 0.6},
marker={"size": 9, "color": COLOR_SECONDARY,
"line": {"color": "#fff", "width": 1}},
fill="tozeroy",
fillcolor="rgba(0, 217, 255, 0.12)",
name="entropy",
)
)