""" theme.py — SchemeImpactNet shared design system Editorial / policy-brief aesthetic. Fonts: Fraunces (display) + Source Serif 4 (body) + DM Mono (data/labels) Palette: warm off-white #FAF9F7, deep stone #1C1917, saffron accent #FB923C """ THEME_CSS = """ """ # ── Plotly shared layout (light, editorial) ─────────────────────────────────── PLOTLY_LAYOUT = dict( paper_bgcolor="#FFFFFF", plot_bgcolor="#FAFAF9", font=dict(family="DM Mono, monospace", color="#292524", size=10.5), margin=dict(l=0, r=0, t=44, b=0), legend=dict( bgcolor="rgba(255,255,255,0.92)", bordercolor="#E7E5E4", borderwidth=1, font=dict(size=10), ), xaxis=dict( gridcolor="#F5F5F4", linecolor="#E7E5E4", tickfont=dict(color="#78716C", size=10), title_font=dict(color="#57534E", size=11), zerolinecolor="#E7E5E4", ), yaxis=dict( gridcolor="#F5F5F4", linecolor="#E7E5E4", tickfont=dict(color="#78716C", size=10), title_font=dict(color="#57534E", size=11), zerolinecolor="#E7E5E4", ), ) # ── Colour tokens ───────────────────────────────────────────────────────────── SAFFRON = "#FB923C" # primary accent SAFFRON_D = "#EA580C" # darker saffron SLATE = "#1C1917" # near-black STONE = "#78716C" # muted label BORDER = "#E7E5E4" BG = "#FAF9F7" WHITE = "#FFFFFF" GREEN = "#16A34A" RED = "#DC2626" AMBER = "#D97706" BLUE = "#2563EB" # ── Saffron scale for choropleth / sequential maps ─────────────────────────── SAFFRON_SCALE = [ [0.0, "#FFF7ED"], [0.25, "#FED7AA"], [0.5, "#FB923C"], [0.75, "#EA580C"], [1.0, "#7C2D12"], ] # ── Helpers ─────────────────────────────────────────────────────────────────── def inject_theme(): import streamlit as st st.markdown(THEME_CSS, unsafe_allow_html=True) def page_header(eyebrow: str, title: str, subtitle: str = ""): import streamlit as st sub_html = ( f'

{subtitle}

' if subtitle else "" ) st.markdown(f"""

{eyebrow}

{title}

{sub_html}
""", unsafe_allow_html=True) def section_label(text: str): import streamlit as st st.markdown( f'

' f'{text}

', unsafe_allow_html=True, ) def kpi_html(value: str, label: str, color: str = "#1C1917", note: str = "") -> str: note_html = ( f'

{note}

' if note else "" ) return f"""

{label}

{value}

{note_html}
""" def signal_card_html(value: str, title: str, body: str, accent: str = "#FB923C") -> str: return f"""
{value}

{title}

{body}

""" # NOTE: inject_theme() is now a no-op for page files. # All CSS is injected once in app.py before st.navigation() runs, # which means it persists across every page automatically. def inject_theme(): pass # CSS already injected globally by app.py