kaveh's picture
changed CV to cross validation and increased font size in first page
34cacad
"""Light shared styles (no heavy themes; keeps default Streamlit + plotly_white)."""
from __future__ import annotations
import streamlit as st
# Feature Insights multipage hub: same title + tagline on every sub-page.
FEATURE_INSIGHTS_TITLE = "Feature Insights"
FEATURE_INSIGHTS_CAPTION = (
"Latent-shift probes, attention rollout, and combined rankings across RNA, ATAC, and Flux."
)
def inject_app_styles() -> None:
"""Panel labels, page background, and shared chrome (all pages)."""
st.markdown(
"""
<style>
/*
* Full page: white (#fff, same as Plotly plotly_white paper) + subtle dot texture only.
* Line grid is reserved for the home banner (.ff-hero), not the app shell.
*/
.stApp {
background-color: #ffffff !important;
background-image: radial-gradient(rgba(15, 23, 42, 0.055) 1px, transparent 1px) !important;
background-size: 20px 20px !important;
background-attachment: fixed !important;
}
[data-testid="stAppViewContainer"] .block-container {
background-color: transparent !important;
}
[data-testid="stHeader"] {
background-color: #ffffff !important;
background-image: radial-gradient(rgba(15, 23, 42, 0.055) 1px, transparent 1px) !important;
background-size: 20px 20px !important;
border-bottom: 1px solid rgba(226, 232, 240, 0.95);
backdrop-filter: none;
}
/* Plotly embed: match page paper colour (avoids grey Streamlit chrome around charts) */
[data-testid="stPlotlyChart"],
[data-testid="stPlotlyChart"] > div,
[data-testid="stPlotlyChart"] .js-plotly-plot,
[data-testid="stPlotlyChart"] .plotly-graph-div {
background-color: #ffffff !important;
}
/* Sidebar: distinct from main white canvas (theme secondary + light edge) */
[data-testid="stSidebar"] {
background-color: #f1f5fb !important;
background-image: radial-gradient(rgba(15, 23, 42, 0.045) 1px, transparent 1px) !important;
background-size: 18px 18px !important;
border-right: 1px solid rgba(148, 163, 184, 0.35) !important;
}
[data-testid="stSidebar"] [data-testid="stSidebarNavLink"] p,
[data-testid="stSidebar"] [data-testid="stSidebarNavLink"] span {
font-size: 1.05rem !important;
line-height: 1.35 !important;
}
/* st.title() headings in main column (hero title keeps its own rule below on home) */
section[data-testid="stMain"] h1 {
font-size: clamp(1.95rem, 3.2vw, 2.35rem) !important;
font-weight: 700 !important;
letter-spacing: -0.02em !important;
}
.latent-panel-title {
font-size: 0.82rem;
font-weight: 600;
color: #475569;
margin: 0 0 0.35rem 0;
letter-spacing: 0.02em;
}
.latent-panel-title-gap { margin-top: 0.85rem; }
</style>
""",
unsafe_allow_html=True,
)
def plot_help_popover(
help_md: str,
*,
key: str,
page_link: tuple[str, str] | None = None,
) -> None:
"""Small help control next to a figure; opens Markdown guidance for biologists.
If ``page_link`` is ``(path, label)``, a ``st.page_link`` is rendered after the markdown
(e.g. ``("pages/1_Single_Cell_Explorer.py", "Single-Cell Explorer")``).
"""
with st.popover(
" ",
help="What does this figure show?",
icon=":material/help_outline:",
type="tertiary",
width="content",
key=key,
):
st.markdown(help_md)
if page_link:
page_path, page_label = page_link
st.page_link(page_path, label=page_label)
def plot_caption_with_help(caption: str, help_md: str, *, key: str) -> None:
"""One-line caption with an aligned help popover (typical layout above a chart)."""
try:
cap_col, help_col = st.columns([0.9, 0.1], gap="small", vertical_alignment="center")
except TypeError:
cap_col, help_col = st.columns([0.9, 0.1], gap="small")
with cap_col:
st.caption(caption)
with help_col:
plot_help_popover(help_md, key=key)
def inject_home_landing_styles() -> None:
"""Hero, nav cards, and section labels (home page only)."""
st.markdown(
"""
<style>
.ff-hero {
position: relative;
overflow: hidden;
border-radius: 16px;
padding: 1.5rem 1.85rem 1.45rem;
margin-bottom: 1.35rem;
border: 1px solid rgba(129, 140, 248, 0.45);
box-shadow:
0 4px 24px rgba(30, 27, 75, 0.18),
inset 0 1px 0 rgba(255, 255, 255, 0.12);
background:
linear-gradient(118deg, rgba(15, 23, 42, 0.97) 0%, rgba(49, 46, 129, 0.94) 38%, rgba(67, 56, 202, 0.92) 72%, rgba(79, 70, 229, 0.88) 100%);
}
/* Banner: visible line grid (Shape2Force-style) over the gradient */
.ff-hero::before {
content: "";
position: absolute;
inset: 0;
opacity: 0.55;
pointer-events: none;
background-image:
linear-gradient(rgba(255, 255, 255, 0.11) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.11) 1px, transparent 1px);
background-size: 20px 20px;
}
.ff-hero::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(ellipse 85% 65% at 18% 0%, rgba(129, 140, 248, 0.35) 0%, transparent 55%);
}
.ff-hero-inner {
position: relative;
z-index: 1;
}
.ff-hero-title-row {
display: flex;
align-items: center;
gap: 0.55rem;
flex-wrap: wrap;
margin-bottom: 0.4rem;
}
.ff-hero-emoji {
font-size: clamp(1.75rem, 4.5vw, 2.35rem);
line-height: 1;
filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.2));
user-select: none;
}
/* Narrower hero title so it does not inherit the large st.title size above */
section[data-testid="stMain"] .ff-hero .ff-hero-text h1 {
font-size: clamp(1.65rem, 4vw, 2.1rem) !important;
font-weight: 700 !important;
margin: 0 !important;
color: #f8fafc !important;
letter-spacing: -0.03em !important;
line-height: 1.15 !important;
text-shadow: 0 1px 18px rgba(0, 0, 0, 0.25) !important;
}
.ff-hero-sub {
margin: 0;
max-width: 100%;
font-size: 0.98rem;
line-height: 1.55;
color: rgba(226, 232, 240, 0.95);
font-weight: 400;
}
.ff-section-label {
font-size: 0.72rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #64748b;
margin: 0 0 0.35rem 0;
}
.ff-nav-slot-marker {
display: block !important;
width: 0 !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
clip-path: inset(50%) !important;
}
/* Nav-card colour rules live in home.py so they stay in sync with the per-rerun script. */
</style>
""",
unsafe_allow_html=True,
)