File size: 3,250 Bytes
6bef416 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | from __future__ import annotations
from collections.abc import Iterable
import streamlit as st
CSS = """
<style>
:root {
--app-bg: #0b1020;
--panel: #111827;
--border: rgba(148, 163, 184, 0.28);
--muted: #94a3b8;
}
.stApp {
background: linear-gradient(180deg, #08111f 0%, #0b1020 45%, #0f172a 100%);
}
.block-container {
max-width: 1480px;
padding-top: 1.25rem;
padding-bottom: 3rem;
}
[data-testid="stSidebar"] {
background: #0b1220;
border-right: 1px solid var(--border);
}
h1, h2, h3 {
letter-spacing: -0.02em;
}
[data-testid="stMetric"] {
background: rgba(15, 23, 42, 0.72);
border: 1px solid var(--border);
border-radius: 16px;
padding: 14px 14px 12px 14px;
}
[data-testid="stDataFrame"] {
border-radius: 12px;
}
.small-note {
color: var(--muted);
font-size: 0.92rem;
line-height: 1.5;
}
</style>
"""
def inject_css() -> None:
"""Inject static CSS only; do not interpolate user or dataset values into this markup."""
st.markdown(CSS, unsafe_allow_html=True)
def badge(text: str, kind: str = "info") -> str:
"""Return a plain-text badge label for native Streamlit rendering."""
prefix = {
"good": "✅",
"warn": "⚠️",
"bad": "🚨",
"info": "ℹ️",
}.get(kind, "•")
return f"{prefix} {text}"
def hero(title: str, subtitle: str, badges: Iterable[str] | None = None) -> None:
st.title(title)
st.markdown(subtitle)
if badges:
st.caption(" · ".join(str(item) for item in badges))
st.divider()
def decision_strip(items: list[tuple[str, str, str]]) -> None:
"""Render the decision brief with native Streamlit elements.
This keeps the decision brief stable across Streamlit versions.
"""
st.subheader("Decision brief")
cols = st.columns(len(items))
for col, (label, value, hint) in zip(cols, items, strict=False):
with col:
with st.container(border=True):
st.caption(label)
st.markdown(f"**{value}**")
st.caption(hint)
def metric_card(label: str, value: str, caption: str, status: str = "info") -> None:
st.metric(label=label, value=value)
st.caption(f"{status.title()} · {caption}")
def status_kind(status: str) -> str:
s = status.lower()
if any(x in s for x in ["stable", "good", "healthy", "pass"]):
return "good"
if any(x in s for x in ["risk", "high", "bad", "fail"]):
return "bad"
if any(x in s for x in ["watch", "review", "warn"]):
return "warn"
return "info"
def section_title(title: str, subtitle: str = "", chip: str = "") -> None:
label = f" — {chip}" if chip else ""
st.markdown(f"## {title}{label}")
if subtitle:
st.caption(subtitle)
def callout(kind: str, title: str, body: str) -> None:
text = f"**{title}**\n\n{body}"
if kind in {"good", "success", "pass"}:
st.success(text)
elif kind in {"warn", "warning", "review"}:
st.warning(text)
elif kind in {"bad", "error", "risk", "fail"}:
st.error(text)
else:
st.info(text)
def trace_box(title: str, body: str) -> None:
with st.container(border=True):
st.markdown(f"**{title}**")
st.write(body)
|