diff --git a/TODO.md b/TODO.md index f50fceb..b710c5e 100644 --- a/TODO.md +++ b/TODO.md @@ -201,6 +201,12 @@ After completing a milestone, create a pull request with your changes for review - [x] Enforce maximum file size during upload - [x] Add tests covering oversized file uploads +## PR20: Theme Improvements + +- [x] Extend sidebar and Plotly figure styling for both themes +- [x] Add global theme toggle stored in session state +- [x] Update tests to verify theme CSS output + ## Notes for Development - Create comprehensive commit messages that clearly describe changes diff --git a/app.py b/app.py index 3144964..cc69bc9 100644 --- a/app.py +++ b/app.py @@ -8,6 +8,11 @@ st.set_page_config(page_title="PredictStream", layout="wide") +if "theme" not in st.session_state: + st.session_state["theme"] = "Light" + +ui.apply_theme(st.session_state["theme"]) + def main() -> None: """Render the home page with navigation links.""" @@ -17,6 +22,7 @@ def main() -> None: "Use the sidebar to navigate to different sections of the application." ) with st.sidebar: + st.selectbox("Theme", ["Light", "Dark"], key="theme") st.page_link("app.py", label="Home", icon="🏠") st.page_link("pages/data_explorer.py", label="Data Explorer", icon="📊") st.page_link("pages/modeling.py", label="Modeling", icon="🧠") diff --git a/pages/data_explorer.py b/pages/data_explorer.py index 58681d9..39f573f 100644 --- a/pages/data_explorer.py +++ b/pages/data_explorer.py @@ -17,13 +17,7 @@ def main() -> None: """Render the data exploration and modeling page.""" ui.apply_branding() st.title("Data Explorer") - theme = st.sidebar.selectbox( - "Theme", - ["Light", "Dark"], - help="Toggle interface theme", - key="theme", - ) - st.markdown(ui.get_theme_css(theme), unsafe_allow_html=True) + ui.apply_theme() with st.expander("Getting Started"): st.markdown(ui.getting_started_markdown()) diff --git a/pages/modeling.py b/pages/modeling.py index f884bfe..dce4f56 100644 --- a/pages/modeling.py +++ b/pages/modeling.py @@ -32,6 +32,7 @@ def main() -> None: """Render the modeling page.""" ui.apply_branding() st.title("Modeling") + ui.apply_theme() data_keys = _available_datasets() if not data_keys: diff --git a/pages/prediction.py b/pages/prediction.py index 1903c91..c5c3e4c 100644 --- a/pages/prediction.py +++ b/pages/prediction.py @@ -22,6 +22,7 @@ def main() -> None: """Render the prediction page.""" ui.apply_branding() st.title("Prediction") + ui.apply_theme() with st.sidebar: mode = st.radio("Mode", ["Single", "Batch"], key="pred_mode") diff --git a/pages/report.py b/pages/report.py index a1b7fcc..79c97eb 100644 --- a/pages/report.py +++ b/pages/report.py @@ -22,6 +22,7 @@ def main() -> None: """Render the report generation page.""" ui.apply_branding() st.title("Report Generator") + ui.apply_theme() with st.sidebar: data_utils.upload_data_to_session( diff --git a/pages/time_series.py b/pages/time_series.py index fe5518d..30838ab 100644 --- a/pages/time_series.py +++ b/pages/time_series.py @@ -18,6 +18,7 @@ def main() -> None: """Render the time series analysis page.""" ui.apply_branding() st.title("Time Series Analysis") + ui.apply_theme() df = st.session_state.get("data") if df is None or df.empty: diff --git a/tests/test_ui.py b/tests/test_ui.py index d994b82..e1f5ba7 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -4,8 +4,11 @@ def test_get_theme_css(): - css = ui.get_theme_css("Dark") - assert "background-color" in css + light = ui.get_theme_css("Light") + dark = ui.get_theme_css("Dark") + for css in (light, dark): + assert "stSidebar" in css + assert "plotly-chart" in css def test_getting_started_markdown(): diff --git a/utils/ui.py b/utils/ui.py index 7a6bca7..00f09b9 100644 --- a/utils/ui.py +++ b/utils/ui.py @@ -8,8 +8,19 @@ import streamlit as st THEME_CSS: Dict[str, str] = { - "Light": "", - "Dark": "body { background-color: #0e1117; color: #f0f0f0; }", + "Light": """ + +""", + "Dark": """ + +""", } # Branding colors @@ -31,6 +42,13 @@ def get_theme_css(theme: str) -> str: return THEME_CSS.get(theme, "") +def apply_theme(theme: str | None = None) -> None: + """Apply the selected theme's CSS to the app.""" + if theme is None: + theme = st.session_state.get("theme", "Light") + st.markdown(get_theme_css(theme), unsafe_allow_html=True) + + def getting_started_markdown() -> str: """Return markdown text for the getting started guide.""" return (