jmontp's picture
Major update
c3a09bd
Raw History Blame Contribute Delete
31.3 kB
"""
Unified styling module for both Streamlit UI and matplotlib plots.
Contains all styling definitions to ensure consistency across the application.
Note: When used outside of Streamlit environment (e.g., in Jupyter notebooks),
you may see warnings about missing ScriptRunContext or Session state. These
warnings are harmless and can be safely ignored - the core plotting functions
(get_plot_style, set_plot_style, PLOT_COLORS) work correctly regardless.
"""
import warnings
import logging
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.font_manager as fm
# Suppress Streamlit warnings when running outside streamlit environment
warnings.filterwarnings('ignore', category=UserWarning, module='streamlit')
warnings.filterwarnings('ignore', message='.*ScriptRunContext.*')
warnings.filterwarnings('ignore', message='.*Session state.*')
warnings.filterwarnings('ignore', message='.*missing ScriptRunContext.*')
warnings.filterwarnings('ignore', message='.*does not function when running.*')
warnings.filterwarnings('ignore', module='streamlit.runtime.*')
warnings.filterwarnings('ignore', module='streamlit.runtime.scriptrunner_utils.*')
warnings.filterwarnings('ignore', module='streamlit.runtime.state.*')
# Suppress Streamlit loggers that generate warnings outside streamlit environment
logging.getLogger('streamlit.runtime.scriptrunner_utils.script_run_context').setLevel(logging.ERROR)
logging.getLogger('streamlit.runtime.state.session_state_proxy').setLevel(logging.ERROR)
logging.getLogger('streamlit').setLevel(logging.ERROR)
try:
# Set logging level before importing to suppress initial warnings
for logger_name in ['streamlit', 'streamlit.runtime', 'streamlit.runtime.scriptrunner_utils',
'streamlit.runtime.state', 'streamlit.runtime.scriptrunner_utils.script_run_context',
'streamlit.runtime.state.session_state_proxy']:
logging.getLogger(logger_name).setLevel(logging.ERROR)
import streamlit as st
_STREAMLIT_AVAILABLE = True
except ImportError:
_STREAMLIT_AVAILABLE = False
# Create a mock streamlit module for non-streamlit environments
class MockStreamlit:
class session_state:
dark_theme = False
st = MockStreamlit()
def _suppress_streamlit_warnings(func):
"""Decorator to suppress streamlit warnings in functions."""
def wrapper(*args, **kwargs):
with warnings.catch_warnings():
warnings.simplefilter('ignore')
return func(*args, **kwargs)
return wrapper
# ==========================
# Shared Color Themes
# ==========================
# Light theme colors - consistent across UI and plots
LIGHT_COLORS = {
'background': '#F7FAFC',
'figure_background': '#FFFFFF',
'sidebar_bg_start': '#F0F4F8',
'sidebar_bg_end': '#E4EBF3',
'border_light': '#E2E8F0',
'border_medium': '#CBD5E1',
'text_primary': '#1F2933',
'text_secondary': '#334E68',
'text_tertiary': '#52606D',
'text_light': '#829AB1',
'card_background': '#FFFFFF',
'code_background': '#EEF2FF',
'code_text': '#1E3A8A',
'button_bg_start': '#2563EB',
'button_bg_end': '#1D4ED8',
'button_hover_start': '#1D4ED8',
'button_hover_end': '#1E40AF',
'alert_error_bg': '#FDE8E8',
'alert_error_border': '#F76B6B',
'alert_error_text': '#B91C1C',
'alert_info_bg': '#E0F2FE',
'alert_info_border': '#3B82F6',
'alert_info_text': '#1E3A8A',
'warning_bg': '#FEF3C7',
'warning_border': '#F59E0B',
'success_bg': '#DCFCE7',
'success_border': '#16A34A',
'generate_button_bg': '#047857',
'generate_button_hover': '#0F9D58',
'panel_background': '#FFFFFF',
'panel_border': '#E2E8F0',
'panel_shadow': '0 8px 24px rgba(15, 23, 42, 0.08)',
# Plot-specific colors
'axes_background': '#FFFFFF',
'grid_color': '#E2E8F0',
'spine_color': '#CBD5E1',
}
# Paper theme colors - pure white backgrounds for publication
PAPER_COLORS = {
'background': '#FFFFFF',
'figure_background': '#FFFFFF',
'sidebar_bg_start': '#F5F7FA',
'sidebar_bg_end': '#E4EBF3',
'border_light': '#E2E8F0',
'border_medium': '#CBD5E1',
'text_primary': '#1F2933',
'text_secondary': '#334E68',
'text_tertiary': '#52606D',
'text_light': '#829AB1',
'card_background': '#FFFFFF',
'code_background': '#EEF2FF',
'code_text': '#1E3A8A',
'button_bg_start': '#2563EB',
'button_bg_end': '#1D4ED8',
'button_hover_start': '#1D4ED8',
'button_hover_end': '#1E40AF',
'alert_error_bg': '#FDE8E8',
'alert_error_border': '#F76B6B',
'alert_error_text': '#B91C1C',
'alert_info_bg': '#E0F2FE',
'alert_info_border': '#3B82F6',
'alert_info_text': '#1E3A8A',
'warning_bg': '#FEF3C7',
'warning_border': '#F59E0B',
'success_bg': '#DCFCE7',
'success_border': '#16A34A',
'generate_button_bg': '#047857',
'generate_button_hover': '#0F9D58',
'panel_background': '#FFFFFF',
'panel_border': '#E2E8F0',
'panel_shadow': '0 8px 24px rgba(15, 23, 42, 0.08)',
# Plot-specific colors - pure white for papers
'axes_background': '#FFFFFF',
'grid_color': '#E2E8F0',
'spine_color': '#CBD5E1',
}
# Dark theme colors - consistent across UI and plots
DARK_COLORS = {
'background': '#0F172A',
'figure_background': '#1E293B',
'sidebar_bg_start': '#111C2E',
'sidebar_bg_end': '#1B2537',
'border_light': '#27364C',
'border_medium': '#334155',
'text_primary': '#F8FAFC',
'text_secondary': '#CBD5F5',
'text_tertiary': '#94A3B8',
'text_light': '#64748B',
'card_background': '#1F2937',
'code_background': '#1E3A5F',
'code_text': '#C7D2FE',
'button_bg_start': '#3B82F6',
'button_bg_end': '#2563EB',
'button_hover_start': '#2563EB',
'button_hover_end': '#1D4ED8',
'alert_error_bg': '#451A1A',
'alert_error_border': '#F87171',
'alert_error_text': '#FCA5A5',
'alert_info_bg': '#1E293B',
'alert_info_border': '#60A5FA',
'alert_info_text': '#BFDBFE',
'warning_bg': '#3D2D12',
'warning_border': '#FBBF24',
'success_bg': '#163225',
'success_border': '#34D399',
'generate_button_bg': '#10B981',
'generate_button_hover': '#34D399',
'panel_background': '#1F2937',
'panel_border': '#334155',
'panel_shadow': '0 18px 36px rgba(2, 6, 23, 0.55)',
# Plot-specific colors
'axes_background': '#0F172A',
'grid_color': '#27364C',
'spine_color': '#334155',
}
@_suppress_streamlit_warnings
def get_current_colors():
"""Return the active color scheme, defaulting to the light palette."""
try:
dark_mode = getattr(st.session_state, 'dark_theme', False)
except Exception:
dark_mode = False
return DARK_COLORS if dark_mode else LIGHT_COLORS
# ==========================
# Plot Styling
# ==========================
# Font configuration
DEFAULT_FONT_FAMILY = 'sans-serif'
try:
fm.fontManager.addfont('/usr/share/fonts/truetype/msttcorefonts/Arial.ttf')
PLOT_STYLE_FONT_FAMILY = 'Arial'
print("Successfully loaded Arial font.")
except FileNotFoundError:
print("Arial.ttf not found. Using default system font.")
PLOT_STYLE_FONT_FAMILY = DEFAULT_FONT_FAMILY
except Exception as e:
print(f"An error occurred while trying to load Arial font: {e}. Using default system font.")
PLOT_STYLE_FONT_FAMILY = DEFAULT_FONT_FAMILY
# Color constants for plots
PLOT_COLORS = {
'input_similarity': sns.color_palette('rocket', as_cmap=True),
'output_difference': sns.cubehelix_palette(start=.2, rot=-.3, dark=0, light=0.85,
reverse=True, as_cmap=True),
'conflict': sns.cubehelix_palette(start=2, rot=0, dark=0, light=0.85,
reverse=True, as_cmap=True),
'output_biomechanical': sns.cubehelix_palette(start=2.8, rot=0.4, dark=0, light=0.85,
reverse=True, as_cmap=True)
}
# Additional palettes
purple_helix = sns.cubehelix_palette(start=.2, rot=-.4, dark=0, light=0.85,
reverse=True, as_cmap=True)
my_purple_helix = sns.cubehelix_palette(start=.2, rot=-.1, dark=0, light=0.85,
reverse=True, as_cmap=True)
def get_plot_style(style='default'):
"""Get plot style with specified color theme.
Args:
style: 'default' for cream theme, 'paper' for pure white backgrounds, 'dark' for dark theme
"""
if style == 'paper':
theme_colors = PAPER_COLORS
elif style == 'dark':
theme_colors = DARK_COLORS
else: # default
theme_colors = get_current_colors()
return {
'font_family': PLOT_STYLE_FONT_FAMILY,
'font_size': 18,
'title_size': 20,
'label_size': 18,
'tick_size': 15,
'tick_length': 5,
'tick_width': 0.5,
'tick_pad': 5,
'label_pad_x': -15,
'label_pad_y': -35,
'figure_dpi': 300,
'aspect_ratio': 'equal',
'subplot_wspace': 0.05,
'subplot_hspace': 0.1,
# Theme-specific styling
'figure_facecolor': theme_colors['figure_background'],
'axes_facecolor': theme_colors['axes_background'],
'text_color': theme_colors['text_primary'],
'grid_color': theme_colors['grid_color'],
'spine_color': theme_colors['spine_color'],
}
def set_plot_style(style='default'):
"""Set consistent plot styling across all figures.
Args:
style: 'default' for cream theme, 'paper' for pure white backgrounds, 'dark' for dark theme
"""
plot_style = get_plot_style(style=style)
plt.rcParams['font.family'] = plot_style['font_family']
plt.rcParams['font.size'] = plot_style['font_size']
plt.rcParams['axes.labelsize'] = plot_style['label_size']
plt.rcParams['axes.titlesize'] = plot_style['title_size']
plt.rcParams['xtick.labelsize'] = plot_style['tick_size']
plt.rcParams['ytick.labelsize'] = plot_style['tick_size']
plt.rcParams['xtick.major.pad'] = plot_style['tick_pad']
plt.rcParams['ytick.major.pad'] = plot_style['tick_pad']
plt.rcParams['figure.dpi'] = plot_style['figure_dpi']
plt.rcParams['figure.subplot.wspace'] = plot_style['subplot_wspace']
plt.rcParams['figure.subplot.hspace'] = plot_style['subplot_hspace']
# Apply theme styling
plt.rcParams['figure.facecolor'] = plot_style['figure_facecolor']
plt.rcParams['axes.facecolor'] = plot_style['axes_facecolor']
plt.rcParams['text.color'] = plot_style['text_color']
plt.rcParams['axes.labelcolor'] = plot_style['text_color']
plt.rcParams['xtick.color'] = plot_style['text_color']
plt.rcParams['ytick.color'] = plot_style['text_color']
plt.rcParams['axes.edgecolor'] = plot_style['spine_color']
plt.rcParams['grid.color'] = plot_style['grid_color']
plt.rcParams['grid.alpha'] = 0.7
def apply_theme_to_figure(fig, ax=None):
"""Apply current theme to an existing figure and axes"""
theme_colors = get_current_colors()
if fig:
fig.patch.set_facecolor(theme_colors['figure_background'])
if ax is not None:
# Handle single axes or iterables of axes robustly
if hasattr(ax, 'flatten'):
axes_list = ax.flatten()
elif isinstance(ax, (list, tuple)):
axes_list = ax
elif hasattr(ax, '__iter__'):
axes_list = list(ax)
else:
axes_list = [ax]
for axis in axes_list:
if axis is not None:
axis.set_facecolor(theme_colors['axes_background'])
# Update text colors
axis.title.set_color(theme_colors['text_primary'])
axis.xaxis.label.set_color(theme_colors['text_primary'])
axis.yaxis.label.set_color(theme_colors['text_primary'])
# Update tick colors
axis.tick_params(colors=theme_colors['text_primary'])
# Update spine colors
for spine in axis.spines.values():
spine.set_color(theme_colors['spine_color'])
# Update grid
axis.grid(True, color=theme_colors['grid_color'], alpha=0.7)
return fig, ax
def set_paper_plot_style():
"""Convenience function to set pure white backgrounds for paper publication."""
set_plot_style(style='paper')
# Legacy function name for backward compatibility
def apply_cream_theme_to_figure(fig, ax=None):
"""Apply current theme to an existing figure and axes (legacy function name)"""
return apply_theme_to_figure(fig, ax)
# ==========================
# Streamlit UI Styling
# ==========================
def get_base_css():
"""Returns the base CSS styling used across all pages."""
return f"""
<style>
/* Main background styling */
.stApp {{
background: {get_current_colors()['background']};
color: {get_current_colors()['text_primary']} !important;
}}
header[role="banner"] {{
display: none !important;
}}
header[data-testid="stHeader"] {{
display: none !important;
}}
div[data-testid="stDecoration"] {{
display: none !important;
}}
div[data-testid="stToolbar"] {{
display: none !important;
}}
.main .block-container {{
padding-top: 6rem;
padding-bottom: 2rem;
background-color: {get_current_colors()['background']};
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
margin-top: 1rem;
color: {get_current_colors()['text_primary']} !important;
}}
/* Explicit text color styling for all elements */
.stApp, .stApp * {{
color: {get_current_colors()['text_primary']} !important;
}}
code {{
background: {get_current_colors()['code_background']};
color: {get_current_colors()['code_text']} !important;
padding: 0.15rem 0.45rem;
border-radius: 6px;
font-weight: 600;
}}
pre code {{
display: block;
padding: 0.75rem 1rem;
border-radius: 10px;
}}
/* Button styling */
.stButton>button {{
width: 100%;
margin-top: 1rem;
margin-bottom: 1rem;
background: linear-gradient(45deg, {get_current_colors()['button_bg_start']}, {get_current_colors()['button_bg_end']});
color: white !important;
border: none;
border-radius: 10px;
padding: 0.75rem 1rem;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(107, 107, 107, 0.2);
}}
.stButton>button:hover {{
background: linear-gradient(45deg, {get_current_colors()['button_hover_start']}, {get_current_colors()['button_hover_end']});
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(107, 107, 107, 0.3);
color: white !important;
}}
/* Sidebar styling */
section[data-testid="stSidebar"] {{
background: linear-gradient(180deg, {get_current_colors()['sidebar_bg_start']} 0%, {get_current_colors()['figure_background']} 85%);
border-right: 1px solid {get_current_colors()['border_medium']};
color: {get_current_colors()['text_primary']} !important;
margin-top: 5.5rem;
height: calc(100vh - 5.5rem);
}}
section[data-testid="stSidebar"] > div {{
background: transparent;
padding: 1.75rem 1.2rem 2.3rem 1.2rem;
color: {get_current_colors()['text_primary']} !important;
}}
/* Headers styling */
h1 {{
padding-bottom: 1rem;
border-bottom: 3px solid {get_current_colors()['border_medium']};
color: {get_current_colors()['text_primary']} !important;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.05);
}}
h2 {{
margin-top: 2rem;
padding-bottom: 0.5rem;
color: {get_current_colors()['text_secondary']} !important;
font-weight: 600;
}}
h3 {{
margin-top: 1.5rem;
color: {get_current_colors()['text_tertiary']} !important;
font-weight: 600;
}}
/* Alert box styling */
.auth-alert {{
background: {get_current_colors()['alert_error_bg']};
color: {get_current_colors()['alert_error_text']} !important;
padding: 15px;
border-radius: 10px;
margin: 15px 0;
border: 1px solid {get_current_colors()['alert_error_border']};
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
}}
.auth-info {{
background: {get_current_colors()['alert_info_bg']};
color: {get_current_colors()['alert_info_text']} !important;
padding: 15px;
border-radius: 10px;
margin: 15px 0;
border: 1px solid {get_current_colors()['alert_info_border']};
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
}}
/* Hide default multipage navigation */
section[data-testid="stSidebar"] nav[data-testid="stSidebarNav"] {{
display: none !important;
}}
section[data-testid="stSidebar"] div[data-testid="stSidebarNav"] {{
display: none !important;
}}
section[data-testid="stSidebarNav"] {{
display: none !important;
}}
nav[data-testid="stSidebarNav"] {{
display: none !important;
}}
/* Top navigation */
.top-nav-outer {{
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
padding: 0.85rem 1.5rem;
background: linear-gradient(180deg, {get_current_colors()['background']} 0%, {get_current_colors()['figure_background']} 100%);
border-bottom: 1px solid {get_current_colors()['border_medium']};
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1);
}}
.top-nav-inner {{
max-width: 1100px;
margin: 0 auto;
display: flex;
gap: 0.75rem;
justify-content: center;
flex-wrap: wrap;
}}
.top-nav-inner .nav-link {{
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 150px;
padding: 0.55rem 1rem;
font-weight: 600;
border-radius: 12px;
text-decoration: none;
background: linear-gradient(180deg, {get_current_colors()['figure_background']} 0%, {get_current_colors()['background']} 100%) !important;
border: 2px solid {get_current_colors()['border_medium']} !important;
color: {get_current_colors()['text_secondary']} !important;
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08);
transition: all 0.2s ease;
}}
.top-nav-inner .nav-link:hover {{
background: linear-gradient(180deg, {get_current_colors()['background']} 0%, {get_current_colors()['figure_background']} 100%) !important;
color: {get_current_colors()['text_primary']} !important;
border-color: {get_current_colors()['text_secondary']} !important;
transform: translateY(-2px);
}}
.nav-active {{
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 160px;
padding: 0.65rem 1.1rem;
border-radius: 14px;
border: 2px solid {get_current_colors()['text_secondary']};
background: linear-gradient(180deg, {get_current_colors()['figure_background']} 0%, {get_current_colors()['background']} 100%);
font-weight: 700;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.12);
color: {get_current_colors()['text_primary']} !important;
text-align: center;
}}
</style>
"""
def get_home_page_css():
"""Returns additional CSS specific to the home page."""
return f"""
<style>
/* Hide sidebar affordance on the landing page where it is unused */
[data-testid="stSidebar"] {{
display: none !important;
}}
/* Navigation button styling */
.nav-button {{
border: 2px solid {get_current_colors()['border_medium']};
border-radius: 12px;
padding: 20px;
text-align: center;
margin-bottom: 15px;
background: {get_current_colors()['background']};
transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
color: {get_current_colors()['text_primary']} !important;
}}
.nav-button:hover {{
background: {get_current_colors()['figure_background']};
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
border-color: {get_current_colors()['text_secondary']};
color: {get_current_colors()['text_primary']} !important;
}}
.nav-button h3 {{
color: {get_current_colors()['text_secondary']} !important;
margin-bottom: 0.5rem;
}}
/* Description boxes */
.description-box {{
text-align: center;
padding: 15px;
border-radius: 10px;
margin-top: -10px;
background: {get_current_colors()['card_background']};
border: 1px solid {get_current_colors()['border_medium']};
box-shadow: none;
color: {get_current_colors()['text_primary']} !important;
}}
/* Hero banner */
.hero {{
background: linear-gradient(135deg, {get_current_colors()['figure_background']}, {get_current_colors()['background']});
border: 2px solid {get_current_colors()['border_medium']};
border-radius: 18px;
padding: 2.5rem 3rem;
margin-bottom: 2.5rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
color: {get_current_colors()['text_primary']} !important;
}}
.hero-overline {{
text-transform: uppercase;
letter-spacing: 0.28rem;
font-size: 0.75rem;
font-weight: 700;
color: {get_current_colors()['text_tertiary']} !important;
display: inline-block;
margin-bottom: 0.8rem;
}}
.hero-subtext {{
font-size: 1.1rem;
line-height: 1.7rem;
color: {get_current_colors()['text_secondary']} !important;
margin-top: 1rem;
max-width: 720px;
}}
/* Grid layouts for value props and steps */
.value-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.25rem;
margin-top: 1.5rem;
}}
.value-card {{
background: {get_current_colors()['card_background']};
border: 1px solid {get_current_colors()['border_medium']};
border-radius: 12px;
padding: 1.25rem;
box-shadow: none;
color: {get_current_colors()['text_primary']} !important;
}}
.value-card h3 {{
margin-bottom: 0.6rem;
color: {get_current_colors()['text_secondary']} !important;
font-size: 1.1rem;
}}
.checklist {{
list-style: none;
padding: 0;
margin: 1rem 0 0 0;
}}
.checklist li {{
display: flex;
align-items: flex-start;
gap: 0.6rem;
margin-bottom: 0.75rem;
color: {get_current_colors()['text_secondary']} !important;
}}
.checklist span {{
font-weight: 600;
color: {get_current_colors()['text_primary']} !important;
}}
.step-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.5rem;
margin-top: 1.5rem;
}}
.step-card {{
background: {get_current_colors()['card_background']};
border: 1px solid {get_current_colors()['border_medium']};
border-radius: 12px;
padding: 1.5rem;
box-shadow: none;
position: relative;
color: {get_current_colors()['text_primary']} !important;
}}
.step-number {{
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
background: {get_current_colors()['text_secondary']};
color: {get_current_colors()['background']} !important;
font-weight: 700;
margin-bottom: 1rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}}
.deliverables-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.25rem;
margin-top: 1.5rem;
}}
.deliverable-card {{
background: {get_current_colors()['card_background']};
border: 1px solid {get_current_colors()['border_medium']};
border-radius: 12px;
padding: 1.25rem;
box-shadow: none;
color: {get_current_colors()['text_primary']} !important;
}}
.auth-help {{
margin-top: 1.5rem;
color: {get_current_colors()['text_secondary']} !important;
text-align: center;
font-size: 0.95rem;
}}
.auth-help a {{
color: {get_current_colors()['text_secondary']} !important;
font-weight: 600;
}}
</style>
"""
def get_documentation_page_css():
"""Returns additional CSS specific to the documentation page."""
return f"""
<style>
/* Reduce sidebar padding so controls sit right below the nav */
[data-testid="stSidebar"] > div {{
padding: 1.05rem 1.1rem 1.8rem 1.1rem !important;
}}
.gaussian-explanation-container {{
padding: 20px;
background: {get_current_colors()['background']};
border-radius: 12px;
margin-bottom: 20px;
border: 2px solid {get_current_colors()['border_medium']};
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);
color: {get_current_colors()['text_primary']} !important;
}}
.metric-card {{
background: {get_current_colors()['background']};
padding: 15px;
border-radius: 10px;
border: 2px solid {get_current_colors()['border_medium']};
margin: 10px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
color: {get_current_colors()['text_primary']} !important;
}}
.glossary-term {{
background: {get_current_colors()['background']};
padding: 15px;
border-radius: 10px;
border-left: 4px solid {get_current_colors()['text_secondary']};
margin: 10px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
color: {get_current_colors()['text_primary']} !important;
}}
</style>
"""
def get_tool_page_css():
"""Returns additional CSS specific to the analysis tool page."""
return f"""
<style>
/* Reduce sidebar padding so controls sit right below the nav */
[data-testid="stSidebar"] > div {{
padding: 1.05rem 1.1rem 1.8rem 1.1rem !important;
}}
/* Expander styling */
.streamlit-expanderHeader {{
background-color: {get_current_colors()['figure_background']};
border: 2px solid {get_current_colors()['border_medium']};
border-radius: 8px;
color: {get_current_colors()['text_primary']} !important;
font-weight: 600;
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
}}
/* Generate button special styling */
div[data-testid="stButton"] button {{
background: linear-gradient(45deg, {get_current_colors()['generate_button_bg']}, {get_current_colors()['generate_button_hover']}) !important;
color: white !important;
border: none !important;
font-weight: 700 !important;
font-size: 1.1rem !important;
padding: 1rem !important;
box-shadow: 0 3px 15px rgba(34, 139, 34, 0.3) !important;
}}
div[data-testid="stButton"] button:hover {{
background: linear-gradient(45deg, {get_current_colors()['generate_button_hover']}, #7CFC00) !important;
transform: translateY(-3px) !important;
box-shadow: 0 5px 20px rgba(34, 139, 34, 0.4) !important;
color: white !important;
}}
.quick-start {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}}
.quick-step {{
background: linear-gradient(135deg, {get_current_colors()['card_background']}, {get_current_colors()['figure_background']});
border: 1px solid {get_current_colors()['border_medium']};
border-radius: 14px;
padding: 1rem 1.25rem;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08);
color: {get_current_colors()['text_primary']} !important;
}}
.quick-step .step-index {{
display: inline-block;
margin-bottom: 0.45rem;
font-size: 0.85rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
background: {get_current_colors()['text_secondary']};
color: {get_current_colors()['background']} !important;
padding: 0.2rem 0.75rem;
border-radius: 999px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}}
.sidebar-step-title {{
font-size: 0.85rem;
letter-spacing: 0.1em;
text-transform: uppercase;
font-weight: 700;
margin: 1.1rem 0 0.5rem 0;
color: {get_current_colors()['text_secondary']} !important;
}}
.tooltip-container {{
display: flex;
flex-direction: column;
gap: 0.75rem;
background: {get_current_colors()['figure_background']};
border: 1px solid {get_current_colors()['border_medium']};
border-radius: 16px;
padding: 1rem;
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.08);
margin-bottom: 1rem;
}}
.tooltip-content {{
background: {get_current_colors()['background']};
border-radius: 12px;
padding: 0.75rem 1rem;
box-shadow: inset 0 0 0 1px {get_current_colors()['border_light']};
display: flex;
flex-direction: column;
gap: 0.45rem;
}}
.tooltip-content .color-legend {{
display: flex;
align-items: center;
gap: 0.5rem;
color: {get_current_colors()['text_secondary']} !important;
}}
.tooltip-content .color-box {{
width: 14px;
height: 14px;
border-radius: 4px;
border: 1px solid {get_current_colors()['border_light']};
}}
</style>
"""
def apply_base_styling():
"""Apply the base styling to the current Streamlit page."""
if not _STREAMLIT_AVAILABLE:
return
st.markdown(get_base_css(), unsafe_allow_html=True)
def apply_home_page_styling():
"""Apply styling specific to the home page."""
if not _STREAMLIT_AVAILABLE:
return
st.markdown(get_base_css(), unsafe_allow_html=True)
st.markdown(get_home_page_css(), unsafe_allow_html=True)
def apply_documentation_page_styling():
"""Apply styling specific to the documentation page."""
if not _STREAMLIT_AVAILABLE:
return
st.markdown(get_base_css(), unsafe_allow_html=True)
st.markdown(get_documentation_page_css(), unsafe_allow_html=True)
def apply_tool_page_styling():
"""Apply styling specific to the analysis tool page."""
if not _STREAMLIT_AVAILABLE:
return
st.markdown(get_base_css(), unsafe_allow_html=True)
st.markdown(get_tool_page_css(), unsafe_allow_html=True)