pandeydigant31's picture
design: CSS variables, DM Sans body font, tabular-nums, anti-slop audit pass
d7ed706 verified
Raw
History Blame Contribute Delete
16.6 kB
"""
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)