Spaces:
Sleeping
Sleeping
| """ | |
| Custom CSS styles for CSH2 Web Dashboard — Dark HUD / Brutalist Industrial Theme | |
| Uses CSS variables for maintainability. Font pairing: JetBrains Mono (labels/data) + DM Sans (body/prose). | |
| """ | |
| import streamlit as st | |
| def inject_custom_css(): | |
| """Inject Dark HUD theme with CSS variables and dual-font typography""" | |
| st.markdown(""" | |
| <style> | |
| /* ============================================================ | |
| A. FONT IMPORTS | |
| ============================================================ */ | |
| @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=DM+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap'); | |
| /* ============================================================ | |
| B. DESIGN TOKENS (:root variables) | |
| ============================================================ */ | |
| :root { | |
| /* Backgrounds */ | |
| --bg-base: #0C0A08; | |
| --bg-panel: #12100E; | |
| --bg-elevated: #1A1714; | |
| --bg-hover: #201C18; | |
| /* Borders */ | |
| --border: #2A2520; | |
| --border-heavy: #3A3530; | |
| /* Text */ | |
| --text-primary: #E8E0D4; | |
| --text-secondary: #B0A898; | |
| --text-muted: #6B6358; | |
| --text-dim: #2A2520; | |
| /* Accent — Amber */ | |
| --accent: #D4A04A; | |
| --accent-light: #E8C47A; | |
| --accent-bg: rgba(212, 160, 74, 0.1); | |
| --accent-bg-hover:rgba(212, 160, 74, 0.25); | |
| /* Semantic */ | |
| --status-nominal: #5B9A6E; | |
| --status-warning: #CC8F00; | |
| --status-alarm: #CC3030; | |
| --info: #5BA3B5; | |
| --info-bg: rgba(91, 163, 181, 0.1); | |
| /* Typography */ | |
| --font-mono: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; | |
| --font-body: 'DM Sans', 'Inter', system-ui, sans-serif; | |
| /* Spacing */ | |
| --space-xs: 4px; | |
| --space-sm: 8px; | |
| --space-md: 12px; | |
| --space-lg: 16px; | |
| --space-xl: 24px; | |
| /* Radii */ | |
| --radius: 2px; | |
| } | |
| /* ============================================================ | |
| C. GLOBAL TYPOGRAPHY | |
| ============================================================ */ | |
| /* Mono for UI chrome: labels, metrics, navigation, buttons */ | |
| [data-testid="stMetric"] label, | |
| [data-testid="stMetric"] [data-testid="stMetricValue"], | |
| [data-testid="stSidebarNavLink"], | |
| .stButton > button, | |
| .stDownloadButton > button, | |
| .stTabs [data-baseweb="tab"], | |
| h1, h2, h3, | |
| .hud-subtitle, | |
| .brand-footer { | |
| font-family: var(--font-mono) !important; | |
| } | |
| /* Sans-serif for body: paragraphs, analysis prose, captions, tooltips */ | |
| p, li, .stCaption, .stMarkdown, | |
| .analysis-card div, | |
| [data-testid="stExpander"] summary, | |
| .stTextInput > div > div > input, | |
| .stTextArea > div > div > textarea, | |
| .stSelectbox, | |
| .stMultiSelect { | |
| font-family: var(--font-body) !important; | |
| } | |
| /* Tabular numbers on all numeric data */ | |
| [data-testid="stMetricValue"], | |
| .stDataFrame td, | |
| .stDataFrame th { | |
| font-variant-numeric: tabular-nums !important; | |
| } | |
| /* Restore Material Icons for Streamlit internal UI */ | |
| [data-testid="stSidebarCollapseButton"], | |
| [data-testid="stSidebarCollapseButton"] *, | |
| [data-testid="stSidebarNavLink"] span[data-testid="stIconMaterial"], | |
| [data-testid="stSidebarNavLink"] span[data-testid="stIconMaterial"] *, | |
| .material-symbols-rounded, | |
| .material-icons { | |
| font-family: 'Material Symbols Rounded', 'Material Icons', sans-serif !important; | |
| font-variant-ligatures: normal !important; | |
| } | |
| /* ============================================================ | |
| D. MAIN CONTAINER | |
| ============================================================ */ | |
| .stApp { | |
| background-color: var(--bg-base); | |
| } | |
| .main .block-container { | |
| padding-top: 2rem; | |
| padding-bottom: 2rem; | |
| max-width: 1400px; | |
| } | |
| /* ============================================================ | |
| E. HEADINGS | |
| ============================================================ */ | |
| h1 { | |
| color: var(--accent) !important; | |
| font-weight: 700 !important; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| font-size: 1.5rem !important; | |
| } | |
| h2 { | |
| color: var(--text-secondary) !important; | |
| font-weight: 600 !important; | |
| border-bottom: 1px solid var(--border); | |
| padding-bottom: 0.3rem; | |
| text-transform: uppercase; | |
| font-size: 1.1rem !important; | |
| letter-spacing: 0.5px; | |
| } | |
| h3 { | |
| color: var(--text-secondary) !important; | |
| font-weight: 500 !important; | |
| font-size: 0.95rem !important; | |
| letter-spacing: 0.3px; | |
| } | |
| /* ============================================================ | |
| F. METRIC CARDS — HUD PANELS WITH CORNER BRACKETS | |
| ============================================================ */ | |
| div[data-testid="stMetric"] { | |
| background-color: var(--bg-panel); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| padding: var(--space-md) var(--space-lg); | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); | |
| position: relative; | |
| transition: box-shadow 0.3s ease; | |
| } | |
| div[data-testid="stMetric"]:hover { | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); | |
| } | |
| /* Corner bracket — top left */ | |
| div[data-testid="stMetric"]::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 12px; | |
| height: 12px; | |
| border-top: 2px solid var(--accent); | |
| border-left: 2px solid var(--accent); | |
| pointer-events: none; | |
| } | |
| /* Corner bracket — bottom right */ | |
| div[data-testid="stMetric"]::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: 0; | |
| right: 0; | |
| width: 12px; | |
| height: 12px; | |
| border-bottom: 2px solid var(--accent); | |
| border-right: 2px solid var(--accent); | |
| pointer-events: none; | |
| } | |
| div[data-testid="stMetric"] label { | |
| color: var(--text-muted) !important; | |
| font-size: 0.7rem !important; | |
| text-transform: uppercase; | |
| letter-spacing: 1.5px; | |
| } | |
| div[data-testid="stMetric"] [data-testid="stMetricValue"] { | |
| color: #FFFFFF !important; | |
| font-size: 1.5rem !important; | |
| font-weight: 700 !important; | |
| } | |
| div[data-testid="stMetric"] [data-testid="stMetricDelta"] svg { | |
| fill: var(--status-nominal); | |
| } | |
| /* ============================================================ | |
| G. SIDEBAR | |
| ============================================================ */ | |
| section[data-testid="stSidebar"] { | |
| background-color: var(--bg-base); | |
| border-right: 1px solid var(--border); | |
| } | |
| section[data-testid="stSidebar"]::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| background: linear-gradient(90deg, transparent, var(--accent), transparent); | |
| pointer-events: none; | |
| } | |
| section[data-testid="stSidebar"] h1, | |
| section[data-testid="stSidebar"] h2, | |
| section[data-testid="stSidebar"] h3 { | |
| color: var(--accent) !important; | |
| font-size: 1rem !important; | |
| } | |
| section[data-testid="stSidebar"] .stCaption { | |
| color: var(--text-muted) !important; | |
| } | |
| /* ============================================================ | |
| H. BUTTONS — OUTLINE / HUD STYLE | |
| ============================================================ */ | |
| .stButton > button { | |
| border-radius: var(--radius); | |
| border: 1px solid var(--accent); | |
| background-color: transparent; | |
| color: var(--accent) !important; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| font-size: 0.8rem; | |
| transition: all 0.2s; | |
| } | |
| .stButton > button:hover { | |
| background-color: var(--accent-bg); | |
| border-color: var(--accent); | |
| box-shadow: 0 0 12px rgba(212, 160, 74, 0.2); | |
| color: var(--accent) !important; | |
| } | |
| .stButton > button:focus { | |
| color: var(--accent) !important; | |
| } | |
| /* Primary buttons */ | |
| .stButton > button[kind="primary"], | |
| .stButton > button[data-testid="stBaseButton-primary"] { | |
| background-color: rgba(212, 160, 74, 0.15); | |
| border-color: var(--accent); | |
| color: var(--accent) !important; | |
| } | |
| .stButton > button[kind="primary"]:hover, | |
| .stButton > button[data-testid="stBaseButton-primary"]:hover { | |
| background-color: var(--accent-bg-hover); | |
| } | |
| /* Download button — info accent */ | |
| .stDownloadButton > button { | |
| background-color: transparent !important; | |
| border: 1px solid var(--info) !important; | |
| color: var(--info) !important; | |
| border-radius: var(--radius); | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| font-size: 0.8rem; | |
| } | |
| .stDownloadButton > button:hover { | |
| background-color: var(--info-bg) !important; | |
| box-shadow: 0 0 12px rgba(91, 163, 181, 0.2) !important; | |
| } | |
| /* ============================================================ | |
| I. ANALYSIS CARDS — TERMINAL OUTPUT AESTHETIC | |
| ============================================================ */ | |
| .analysis-card { | |
| background-color: var(--bg-base); | |
| border: 1px solid var(--border); | |
| border-left: 3px solid var(--accent); | |
| border-radius: var(--radius); | |
| padding: 20px 24px; | |
| margin: var(--space-lg) 0; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |
| position: relative; | |
| } | |
| .analysis-card::before { | |
| content: '\25B8 ANALYSIS OUTPUT'; | |
| display: block; | |
| color: var(--accent); | |
| font-family: var(--font-mono); | |
| font-size: 0.65rem; | |
| letter-spacing: 2px; | |
| text-transform: uppercase; | |
| margin-bottom: var(--space-md); | |
| padding-bottom: var(--space-sm); | |
| border-bottom: 1px solid var(--border); | |
| opacity: 0.7; | |
| } | |
| .analysis-card h4 { | |
| color: var(--accent) !important; | |
| font-family: var(--font-mono) !important; | |
| margin-top: 0; | |
| font-size: 0.9rem !important; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .analysis-card div { | |
| color: var(--text-secondary) !important; | |
| font-family: var(--font-body) !important; | |
| line-height: 1.7; | |
| font-size: 0.85rem; | |
| } | |
| /* ============================================================ | |
| J. TABS — UNDERLINE ACCENT | |
| ============================================================ */ | |
| .stTabs [data-baseweb="tab-list"] { | |
| gap: 0px; | |
| border-bottom: 2px solid var(--border); | |
| } | |
| .stTabs [data-baseweb="tab"] { | |
| border-radius: 0; | |
| padding: var(--space-sm) 20px; | |
| color: var(--text-muted); | |
| border-bottom: 3px solid transparent; | |
| font-size: 0.8rem; | |
| letter-spacing: 0.5px; | |
| text-transform: uppercase; | |
| } | |
| .stTabs [data-baseweb="tab"][aria-selected="true"] { | |
| color: var(--accent) !important; | |
| border-bottom: 3px solid var(--accent) !important; | |
| background-color: rgba(212, 160, 74, 0.05); | |
| } | |
| /* ============================================================ | |
| K. FORM ELEMENTS | |
| ============================================================ */ | |
| .streamlit-expanderHeader { | |
| background-color: var(--bg-panel) !important; | |
| border-radius: var(--radius); | |
| border: 1px solid var(--border); | |
| color: var(--text-secondary) !important; | |
| } | |
| .stSelectbox > div > div { | |
| border-radius: var(--radius); | |
| border-color: var(--border); | |
| } | |
| hr { | |
| border-color: var(--border) !important; | |
| } | |
| .stDataFrame { | |
| border-radius: var(--radius); | |
| overflow: hidden; | |
| border: 1px solid var(--border); | |
| } | |
| .stTextInput > div > div > input, | |
| .stTextArea > div > div > textarea { | |
| border-color: var(--border); | |
| border-radius: var(--radius); | |
| color: var(--text-primary); | |
| } | |
| .stTextInput > div > div > input:focus, | |
| .stTextArea > div > div > textarea:focus { | |
| border-color: var(--accent); | |
| box-shadow: 0 0 6px rgba(212, 160, 74, 0.15); | |
| } | |
| .stMultiSelect > div > div { | |
| border-radius: var(--radius); | |
| border-color: var(--border); | |
| } | |
| .stDateInput > div > div > input { | |
| border-radius: var(--radius); | |
| border-color: var(--border); | |
| } | |
| /* ============================================================ | |
| L. SPINNER | |
| ============================================================ */ | |
| .stSpinner > div { | |
| border-color: var(--accent) !important; | |
| } | |
| /* ============================================================ | |
| M. STATUS ANIMATIONS | |
| ============================================================ */ | |
| @keyframes pulse-nominal { | |
| 0%, 100% { box-shadow: 0 0 6px rgba(91, 154, 110, 0.1); } | |
| 50% { box-shadow: 0 0 12px rgba(91, 154, 110, 0.2); } | |
| } | |
| @keyframes pulse-amber { | |
| 0%, 100% { box-shadow: 0 0 6px rgba(204, 143, 0, 0.1); } | |
| 50% { box-shadow: 0 0 12px rgba(204, 143, 0, 0.2); } | |
| } | |
| @keyframes pulse-red { | |
| 0%, 100% { box-shadow: 0 0 6px rgba(204, 48, 48, 0.1); } | |
| 50% { box-shadow: 0 0 12px rgba(204, 48, 48, 0.2); } | |
| } | |
| /* ============================================================ | |
| N. STATUS CLASSES | |
| ============================================================ */ | |
| .status-nominal { | |
| border-color: var(--status-nominal) !important; | |
| } | |
| .status-nominal .hud-indicator { | |
| background-color: var(--status-nominal); | |
| box-shadow: 0 0 6px var(--status-nominal); | |
| } | |
| .status-warning { | |
| border-color: var(--status-warning) !important; | |
| animation: pulse-amber 2s ease-in-out infinite; | |
| } | |
| .status-warning .hud-indicator { | |
| background-color: var(--status-warning); | |
| box-shadow: 0 0 6px var(--status-warning); | |
| } | |
| .status-alarm { | |
| border-color: var(--status-alarm) !important; | |
| animation: pulse-red 1.5s ease-in-out infinite; | |
| } | |
| .status-alarm .hud-indicator { | |
| background-color: var(--status-alarm); | |
| box-shadow: 0 0 6px var(--status-alarm); | |
| } | |
| .hud-indicator { | |
| display: inline-block; | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| margin-right: 6px; | |
| vertical-align: middle; | |
| } | |
| /* ============================================================ | |
| O. HUD PAGE HEADER | |
| ============================================================ */ | |
| .hud-page-header { | |
| margin-bottom: 1.5rem; | |
| } | |
| .hud-subtitle { | |
| color: var(--text-muted); | |
| font-size: 0.75rem; | |
| letter-spacing: 0.5px; | |
| margin-bottom: 0.5rem; | |
| } | |
| .hud-header-line { | |
| height: 1px; | |
| background: linear-gradient(90deg, var(--accent), var(--border) 60%, transparent); | |
| } | |
| /* ============================================================ | |
| P. HIDE ERROR HELPER LINKS | |
| ============================================================ */ | |
| [data-testid="stException"] a[href*="google"], | |
| [data-testid="stException"] a[href*="chatgpt"], | |
| [data-testid="stException"] a[href*="copy"], | |
| div[data-testid="stException"] div:last-child { | |
| display: none !important; | |
| } | |
| /* ============================================================ | |
| Q. BRAND FOOTER | |
| ============================================================ */ | |
| .brand-footer { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| width: 100%; | |
| text-align: center; | |
| padding: 6px; | |
| background-color: var(--bg-base); | |
| color: var(--text-muted); | |
| font-size: 0.65rem; | |
| border-top: 1px solid var(--border); | |
| z-index: 999; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| } | |
| /* ============================================================ | |
| R. HIDE STREAMLIT BRANDING | |
| ============================================================ */ | |
| #MainMenu { visibility: hidden; } | |
| footer { visibility: hidden; } | |
| </style> | |
| """, unsafe_allow_html=True) | |