From 156324be11625e0341940c66ebb9a39b05a68992 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Mon, 4 May 2026 11:37:04 +0800 Subject: [PATCH 1/6] Fix Reset/Save parameter button alignment --- src/epicc/__main__.py | 57 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/src/epicc/__main__.py b/src/epicc/__main__.py index 3ff4dc5..dd3a782 100644 --- a/src/epicc/__main__.py +++ b/src/epicc/__main__.py @@ -31,6 +31,40 @@ load_styles() initialize_state() +# Normalize action-row button alignment (button vs popover trigger) +st.markdown( + """ + + """, + unsafe_allow_html=True, +) + all_models = get_all_models() model_registry: dict[str, BaseSimulationModel] = {m.human_name(): m for m in all_models} @@ -107,16 +141,12 @@ render_validation_error(selected_label, exc, container=param_col) has_input_errors = True - # Reset and Save Parameters buttons side by side - button_col1, button_col2 = st.columns(2) - # Reset Parameters button def _handle_reset() -> None: model_label = cast(str, selected_label) # Safe because we checked above reset_parameters_to_defaults( model_defaults_flat, params, model_label, param_specs=active_model.parameter_specs ) - # Reset scenarios back to model defaults default_scenarios = active_model.default_scenarios if default_scenarios: reset_scenario_state( @@ -124,12 +154,12 @@ def _handle_reset() -> None: default_scenarios, active_model.scenario_parameter_specs or {}, ) - - with button_col1: - st.button("Reset Parameters", on_click=_handle_reset, width='stretch') - - # Save Parameters button (only enabled when parameters are valid) - with button_col2: + + # Force both controls into the same keyed row for CSS alignment + with st.container(key="param-actions-row"): + button_col1, button_col2 = st.columns(2, gap="small", vertical_alignment="top") + button_col1.button("Reset Parameters", on_click=_handle_reset, width="stretch") + if typed_params is not None: render_parameter_export_modal( active_model.human_name(), @@ -138,7 +168,12 @@ def _handle_reset() -> None: container=button_col2, ) else: - st.button("Save Parameters", disabled=True, width='stretch', help="Fix parameter errors first") + button_col2.button( + "Save Parameters", + disabled=True, + width="stretch", + help="Fix parameter errors first", + ) st.divider() run_clicked = st.button( From 6a8fff2959061550b9db48bd903eb3f5f6b6efad Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Fri, 15 May 2026 16:00:03 +0800 Subject: [PATCH 2/6] Fix PDF print layout: button alignment, chart spacing, formula rendering --- src/epicc/__main__.py | 77 +++++++++++++++++++++++- src/epicc/model/models/tb_isolation.yaml | 6 +- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/epicc/__main__.py b/src/epicc/__main__.py index dd3a782..c4c22eb 100644 --- a/src/epicc/__main__.py +++ b/src/epicc/__main__.py @@ -60,6 +60,80 @@ .st-key-param-actions-row [data-testid="stPopover"] > button { width: 100%; } + + /* Report spacing: keep headings away from charts */ + .st-key-results-report [data-testid="stPlotlyChart"], + .st-key-results-report [data-testid="stVegaLiteChart"], + .st-key-results-report [data-testid="stPyplot"] { + margin-bottom: 2rem !important; + } + + .st-key-results-report [data-testid="stMarkdownContainer"] h1, + .st-key-results-report [data-testid="stMarkdownContainer"] h2, + .st-key-results-report [data-testid="stMarkdownContainer"] h3 { + margin-top: 1.25rem !important; + } + + /* -------- Print/PDF display fixes only (SINGLE CONSOLIDATED BLOCK) -------- */ + @media print { + /* Scope strictly to report area */ + .st-key-results-report { + overflow: visible !important; + } + + /* 1) Keep chart block in flow + reserve vertical space below chart */ + .st-key-results-report [data-testid="stPlotlyChart"], + .st-key-results-report [data-testid="stVegaLiteChart"], + .st-key-results-report [data-testid="stPyplot"] { + display: block !important; + position: static !important; + clear: both !important; + overflow: visible !important; + break-inside: avoid !important; + page-break-inside: avoid !important; + margin: 0 0 2.8rem 0 !important; + padding-bottom: 1.2rem !important; + } + + /* Prevent Plotly print-height collapse */ + .st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot { + min-height: 520px !important; + } + + /* 2) Ensure headings/text start after chart and have breathing room */ + .st-key-results-report h1, + .st-key-results-report h2, + .st-key-results-report h3, + .st-key-results-report h4, + .st-key-results-report p, + .st-key-results-report [data-testid="stMarkdownContainer"] { + clear: both !important; + position: static !important; + z-index: auto !important; + margin-top: 1.5rem !important; + } + + .st-key-results-report [data-testid="stMarkdownContainer"] h1, + .st-key-results-report [data-testid="stMarkdownContainer"] h2, + .st-key-results-report [data-testid="stMarkdownContainer"] h3 { + margin-top: 1.5rem !important; + clear: both !important; + } + + /* 3) Formula print fix: show only visual layer, hide assistive MathML */ + .st-key-results-report .katex, + .st-key-results-report .katex-display, + .st-key-results-report mjx-container, + .st-key-results-report .MathJax { + visibility: visible !important; + opacity: 1 !important; + } + + .st-key-results-report .katex .katex-mathml, + .st-key-results-report mjx-assistive-mml { + display: none !important; + } + } """, unsafe_allow_html=True, @@ -205,5 +279,4 @@ def _handle_reset() -> None: renderer.render(None, hint=_HINT) st.divider() - render_pdf_export_button(container=result_col) - + render_pdf_export_button(container=result_col) \ No newline at end of file diff --git a/src/epicc/model/models/tb_isolation.yaml b/src/epicc/model/models/tb_isolation.yaml index 7f0819b..8876f4c 100644 --- a/src/epicc/model/models/tb_isolation.yaml +++ b/src/epicc/model/models/tb_isolation.yaml @@ -327,10 +327,14 @@ report: direct isolation costs, lost productivity, and the long-term costs of secondary TB infections. +
+ $$ - \LaTeX \text{ test.} + \text{LaTeX test.} $$ +
+ ## Scenarios - **14-day Isolation**: Standard isolation protocol for infectious TB patients - **5-day Isolation**: Shortened isolation protocol (increases secondary transmission risk) From 9fd1671e9e8bf4520b730790e0b1c02772923534 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Wed, 27 May 2026 11:08:24 +0800 Subject: [PATCH 3/6] Replace invalid button width arg with use_container_width --- src/epicc/__main__.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/epicc/__main__.py b/src/epicc/__main__.py index c4c22eb..efd831d 100644 --- a/src/epicc/__main__.py +++ b/src/epicc/__main__.py @@ -232,7 +232,12 @@ def _handle_reset() -> None: # Force both controls into the same keyed row for CSS alignment with st.container(key="param-actions-row"): button_col1, button_col2 = st.columns(2, gap="small", vertical_alignment="top") - button_col1.button("Reset Parameters", on_click=_handle_reset, width="stretch") + + button_col1.button( + "Reset Parameters", + on_click=_handle_reset, + use_container_width=True, + ) if typed_params is not None: render_parameter_export_modal( @@ -245,13 +250,16 @@ def _handle_reset() -> None: button_col2.button( "Save Parameters", disabled=True, - width="stretch", + use_container_width=True, help="Fix parameter errors first", ) st.divider() run_clicked = st.button( - "Run Simulation", disabled=has_input_errors, width='stretch', type='primary' + "Run Simulation", + disabled=has_input_errors, + use_container_width=True, + type="primary", ) with result_col: From 2ee682bfc99bdea1e47a9e3be10dec45436ad705 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Tue, 23 Jun 2026 19:37:18 +0800 Subject: [PATCH 4/6] fix(ui): align sidebar actions and improve report/pdf rendering - move injected inline CSS from app wiring into shared stylesheet - remove inline centered div wrapper around display math in tb_isolation model - update report chart rendering to use container width and theme-aware Plotly text/axis styling - refine PDF/print spacing, formula rendering, and chart contrast behavior --- src/epicc/__main__.py | 108 -------------- src/epicc/model/models/tb_isolation.yaml | 4 - src/epicc/ui/report.py | 87 +++++++++++- src/epicc/web/sidebar.css | 173 ++++++++++++++++++++++- 4 files changed, 255 insertions(+), 117 deletions(-) diff --git a/src/epicc/__main__.py b/src/epicc/__main__.py index efd831d..0b65a9d 100644 --- a/src/epicc/__main__.py +++ b/src/epicc/__main__.py @@ -31,114 +31,6 @@ load_styles() initialize_state() -# Normalize action-row button alignment (button vs popover trigger) -st.markdown( - """ - - """, - unsafe_allow_html=True, -) - all_models = get_all_models() model_registry: dict[str, BaseSimulationModel] = {m.human_name(): m for m in all_models} diff --git a/src/epicc/model/models/tb_isolation.yaml b/src/epicc/model/models/tb_isolation.yaml index 8876f4c..e523595 100644 --- a/src/epicc/model/models/tb_isolation.yaml +++ b/src/epicc/model/models/tb_isolation.yaml @@ -327,14 +327,10 @@ report: direct isolation costs, lost productivity, and the long-term costs of secondary TB infections. -
- $$ \text{LaTeX test.} $$ -
- ## Scenarios - **14-day Isolation**: Standard isolation protocol for infectious TB patients - **5-day Isolation**: Shortened isolation protocol (increases secondary transmission risk) diff --git a/src/epicc/ui/report.py b/src/epicc/ui/report.py index 3892e25..9e10c44 100644 --- a/src/epicc/ui/report.py +++ b/src/epicc/ui/report.py @@ -172,20 +172,25 @@ def render(self, run_results: dict[str, Any] | None) -> None: if self._block.title: st.markdown( f"
" - f"" + f"" f"{self._block.title}
", unsafe_allow_html=True, ) if self._block.caption: st.markdown( - f"
" - f"" + f"
" + f"" f"{self._block.caption}
", unsafe_allow_html=True ) - st.plotly_chart(fig, width='stretch', key=f'plotly-{self._uuid}') + st.plotly_chart( + fig, + use_container_width=True, + key=f"plotly-{self._uuid}", + ) + _apply_plotly_theme_fix(self._uuid) def _resolve_columns( self, run_results: dict[str, Any] @@ -280,6 +285,80 @@ def _raw_value(value: Any) -> float: return 0.0 +def _apply_plotly_theme_fix(block_uuid: str) -> None: + st.html( + f""" + + """, + unsafe_allow_javascript=True, + ) + + class ReportRenderer: """Render full report.""" diff --git a/src/epicc/web/sidebar.css b/src/epicc/web/sidebar.css index 3630cc8..5c03195 100644 --- a/src/epicc/web/sidebar.css +++ b/src/epicc/web/sidebar.css @@ -131,7 +131,7 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { .stDataFrame, .stAgGrid, .stTable { - padding-top: 1.5rem !important; + padding-top: 0.5rem !important; padding-bottom: 1.5rem !important; } @@ -159,4 +159,175 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { .st-key-results-report * { display: revert !important; } +} + +/* Action row wrapper */ +.st-key-param-actions-row [data-testid="stHorizontalBlock"] { + align-items: flex-start !important; +} + +/* Normalize column internal spacing */ +.st-key-param-actions-row [data-testid="column"] > div { + padding-top: 0 !important; +} +.st-key-param-actions-row [data-testid="stElementContainer"] { + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +/* Make trigger controls fill width and remove extra top offset */ +.st-key-param-actions-row [data-testid="stButton"], +.st-key-param-actions-row [data-testid="stPopover"] { + width: 100%; + margin-top: 0 !important; + padding-top: 0 !important; +} +.st-key-param-actions-row [data-testid="stButton"] > button, +.st-key-param-actions-row [data-testid="stPopover"] > button { + width: 100%; +} + +/* Report spacing: keep headings away from charts */ +.st-key-results-report [data-testid="stPlotlyChart"], +.st-key-results-report [data-testid="stVegaLiteChart"], +.st-key-results-report [data-testid="stPyplot"] { + margin-bottom: 2rem !important; +} + +.st-key-results-report [data-testid="stMarkdownContainer"] h1, +.st-key-results-report [data-testid="stMarkdownContainer"] h2, +.st-key-results-report [data-testid="stMarkdownContainer"] h3 { + margin-top: 1.25rem !important; +} + +/* -------- Print/PDF display fixes only (SINGLE CONSOLIDATED BLOCK) -------- */ +@media print { + /* Scope strictly to report area */ + .st-key-results-report { + overflow: visible !important; + } + + /* 1) Keep chart block in flow + reserve vertical space below chart */ + .st-key-results-report [data-testid="stPlotlyChart"], + .st-key-results-report [data-testid="stVegaLiteChart"], + .st-key-results-report [data-testid="stPyplot"] { + display: block !important; + position: static !important; + clear: both !important; + overflow: visible !important; + break-inside: avoid !important; + page-break-inside: avoid !important; + margin: 0 0 2.8rem 0 !important; + padding-bottom: 1.2rem !important; + } + + /* Prevent Plotly print-height collapse */ + .st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot { + min-height: 520px !important; + } + + /* 2) Ensure headings/text start after chart and have breathing room */ + .st-key-results-report h1, + .st-key-results-report h2, + .st-key-results-report h3, + .st-key-results-report h4, + .st-key-results-report p, + .st-key-results-report [data-testid="stMarkdownContainer"] { + clear: both !important; + position: static !important; + z-index: auto !important; + margin-top: 2rem !important; + } + + .st-key-results-report [data-testid="stMarkdownContainer"] h1, + .st-key-results-report [data-testid="stMarkdownContainer"] h2, + .st-key-results-report [data-testid="stMarkdownContainer"] h3 { + margin-top: 2rem !important; + clear: both !important; + } + + /* 3) Formula print fix: show only visual layer, hide assistive MathML */ + .st-key-results-report .katex, + .st-key-results-report .katex-display, + .st-key-results-report mjx-container, + .st-key-results-report .MathJax { + visibility: visible !important; + opacity: 1 !important; + } + + .st-key-results-report .katex .katex-mathml, + .st-key-results-report mjx-assistive-mml { + display: none !important; + } + + /* 4) Force strong chart/text contrast for PDF export */ + .st-key-results-report, + .st-key-results-report * { + color: #111827 !important; + text-shadow: none !important; + -webkit-print-color-adjust: exact !important; + print-color-adjust: exact !important; + } + + .st-key-results-report [data-testid="stPlotlyChart"], + .st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot, + .st-key-results-report [data-testid="stPlotlyChart"] .plot-container, + .st-key-results-report [data-testid="stPlotlyChart"] .main-svg { + color: #111827 !important; + } + + .st-key-results-report .js-plotly-plot text, + .st-key-results-report .js-plotly-plot .legendtext, + .st-key-results-report .js-plotly-plot .gtitle, + .st-key-results-report .js-plotly-plot .xtick text, + .st-key-results-report .js-plotly-plot .ytick text, + .st-key-results-report .js-plotly-plot .annotation text, + .st-key-results-report .js-plotly-plot .infolayer text { + fill: #111827 !important; + color: #111827 !important; + } + + .st-key-results-report .js-plotly-plot .xaxislayer-above path, + .st-key-results-report .js-plotly-plot .yaxislayer-above path, + .st-key-results-report .js-plotly-plot .xaxislayer-above line, + .st-key-results-report .js-plotly-plot .yaxislayer-above line { + stroke: #111827 !important; + stroke-opacity: 1 !important; + stroke-width: 1.2 !important; + } + + .st-key-results-report .js-plotly-plot .gridlayer path, + .st-key-results-report .js-plotly-plot .gridlayer line, + .st-key-results-report .js-plotly-plot .zerolinelayer path, + .st-key-results-report .js-plotly-plot .zerolinelayer line { + stroke: #9ca3af !important; + stroke-opacity: 0.9 !important; + } + + .st-key-results-report .js-plotly-plot .scatterlayer .trace path, + .st-key-results-report .js-plotly-plot .barlayer .trace path { + stroke-opacity: 1 !important; + } +} + +/* Plotly text contrast follows the active Streamlit theme */ +.st-key-results-report [data-testid="stPlotlyChart"] { + color: inherit !important; +} + +.st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot, +.st-key-results-report [data-testid="stPlotlyChart"] .plot-container, +.st-key-results-report [data-testid="stPlotlyChart"] .main-svg { + color: inherit !important; +} + +.st-key-results-report .js-plotly-plot text, +.st-key-results-report .js-plotly-plot .legendtext, +.st-key-results-report .js-plotly-plot .gtitle, +.st-key-results-report .js-plotly-plot .xtick text, +.st-key-results-report .js-plotly-plot .ytick text, +.st-key-results-report .js-plotly-plot .annotation text, +.st-key-results-report .js-plotly-plot .infolayer text { + fill: currentColor !important; + color: inherit !important; } \ No newline at end of file From 36b6b31d5d1b109da550d718e0e3eefc5ebf225a Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Mon, 29 Jun 2026 19:28:25 +0800 Subject: [PATCH 5/6] chore(pr): remove inline JS patch and keep review-scope fixes --- src/epicc/ui/report.py | 75 ------------------------------------------ 1 file changed, 75 deletions(-) diff --git a/src/epicc/ui/report.py b/src/epicc/ui/report.py index 9e10c44..0dfe742 100644 --- a/src/epicc/ui/report.py +++ b/src/epicc/ui/report.py @@ -190,7 +190,6 @@ def render(self, run_results: dict[str, Any] | None) -> None: use_container_width=True, key=f"plotly-{self._uuid}", ) - _apply_plotly_theme_fix(self._uuid) def _resolve_columns( self, run_results: dict[str, Any] @@ -285,80 +284,6 @@ def _raw_value(value: Any) -> float: return 0.0 -def _apply_plotly_theme_fix(block_uuid: str) -> None: - st.html( - f""" - - """, - unsafe_allow_javascript=True, - ) - - class ReportRenderer: """Render full report.""" From f2606b68b06bcd78aa29ff23c95d9d9629e0ee32 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Tue, 14 Jul 2026 19:48:51 +0800 Subject: [PATCH 6/6] fix: bypass Streamlit sanitizer to resolve unreliable print JS execution --- src/epicc/ui/export.py | 28 +++--------- src/epicc/web/sidebar.css | 95 ++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 68 deletions(-) diff --git a/src/epicc/ui/export.py b/src/epicc/ui/export.py index 5abf3c5..d3ce90c 100644 --- a/src/epicc/ui/export.py +++ b/src/epicc/ui/export.py @@ -90,18 +90,17 @@ def render_parameter_export_modal( seen.add(cls) unique.append((suffix.lstrip("."), cls)) - if rc.button("Save Parameters", width='stretch', key=f"save_params_btn_{model_name.lower().replace(' ', '_')}"): + if rc.button("Save Parameters", use_container_width=True, key=f"save_params_btn_{model_name.lower().replace(' ', '_')}"): _export_dialog(model_name, param_data, unique, pydantic_model) def render_pdf_export_button(container: Any = None) -> None: # Render a direct Save report as PDF button. - rc = container if container is not None else st clicked = rc.button( "Save report as PDF", disabled=not has_results(), - width='stretch', + use_container_width=True, type='primary', ) @@ -122,25 +121,8 @@ def trigger_print_if_requested() -> None: trigger_token = st.session_state.get(_PRINT_TOKEN_KEY, 0) - # What the hell is this, Streamlit? Why can't I just run JS without this nonsense? Yes, I know - # you don't want me to mess with your UI, but I just want to trigger the browser print dialog, - # is that really so bad? I even told you it was okay to run unsafe JS, but no, you had to run - # it through some weird sanitizer anyways. - # - # What's worse is that you silently drop that JS which fails your mysterious security checks - # instead of throwing an error, leaving me to waste hours debugging why my print button doesn't - # work at all. So here we are, base64 encoding the JS and evaling it in the browser, just to get - # around your broken injection system. I hope you're proud of yourselves. - # - # Seriously!?!? This works? - # - # This is an alternative implementation to something like: - # - # https://github.com/thunderbug1/streamlit-javascript - # - # Which would have a mess build-wise. As far as I know, I'm the first person to come up with this - # workaround, so I'm claiming it as my own invention! Don't tell Streamlit. - + # Reverted to Olivia's original, hacky, but highly effective base64 injection bypass. + # By using st.html, the script runs in the main window context rather than an isolated iframe. with importlib.resources.files("epicc").joinpath("js/print_results.js").open("rb") as f: js = f.read().decode() js64 = base64.b64encode(js.encode()).decode() @@ -153,4 +135,4 @@ def trigger_print_if_requested() -> None: unsafe_allow_javascript=True, ) - st.session_state[_PRINT_REQUESTED_KEY] = False + st.session_state[_PRINT_REQUESTED_KEY] = False \ No newline at end of file diff --git a/src/epicc/web/sidebar.css b/src/epicc/web/sidebar.css index 5c03195..03d29b4 100644 --- a/src/epicc/web/sidebar.css +++ b/src/epicc/web/sidebar.css @@ -25,7 +25,7 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { /* Fix model selector alignment. TODO: be more specific, `:first-child`? */ .stVerticalBlock { - justify-content: center; + justify-content: center; } /* Sidebar section headers */ @@ -95,16 +95,22 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { margin: 0.5in; } - /* [data-testid="stAppViewContainer"] { - margin: 0 !important; - padding: 0 !important; + html, body, #root, .stlite-container, [data-testid="stAppViewContainer"] { + height: auto !important; + overflow: visible !important; + opacity: 1 !important; + filter: none !important; + -webkit-filter: none !important; } - [data-testid="stMainBlockContainer"] { - max-width: 100% !important; - padding-top: 0 !important; - padding-bottom: 0 !important; - } */ + [data-testid="stSelectbox"], + [data-testid="stColumn"] button, + [data-testid="stHeader"], + [data-testid="stSidebar"], + .stAppDeployButton, + footer { + display: none !important; + } .section-divider { page-break-after: auto; @@ -135,8 +141,6 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { padding-bottom: 1.5rem !important; } - /* Show only results. */ - body * { display: none; } @@ -158,6 +162,8 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { .st-key-results-report, .st-key-results-report * { display: revert !important; + opacity: 1 !important; + filter: none !important; } } @@ -200,14 +206,34 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { margin-top: 1.25rem !important; } +/* Plotly text contrast follows the active Streamlit theme */ +.st-key-results-report [data-testid="stPlotlyChart"] { + color: inherit !important; +} + +.st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot, +.st-key-results-report [data-testid="stPlotlyChart"] .plot-container, +.st-key-results-report [data-testid="stPlotlyChart"] .main-svg { + color: inherit !important; +} + +.st-key-results-report .js-plotly-plot text, +.st-key-results-report .js-plotly-plot .legendtext, +.st-key-results-report .js-plotly-plot .gtitle, +.st-key-results-report .js-plotly-plot .xtick text, +.st-key-results-report .js-plotly-plot .ytick text, +.st-key-results-report .js-plotly-plot .annotation text, +.st-key-results-report .js-plotly-plot .infolayer text { + fill: currentColor !important; + color: inherit !important; +} + /* -------- Print/PDF display fixes only (SINGLE CONSOLIDATED BLOCK) -------- */ @media print { - /* Scope strictly to report area */ .st-key-results-report { overflow: visible !important; } - /* 1) Keep chart block in flow + reserve vertical space below chart */ .st-key-results-report [data-testid="stPlotlyChart"], .st-key-results-report [data-testid="stVegaLiteChart"], .st-key-results-report [data-testid="stPyplot"] { @@ -221,22 +247,26 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { padding-bottom: 1.2rem !important; } - /* Prevent Plotly print-height collapse */ .st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot { min-height: 520px !important; } - /* 2) Ensure headings/text start after chart and have breathing room */ .st-key-results-report h1, .st-key-results-report h2, .st-key-results-report h3, - .st-key-results-report h4, + .st-key-results-report h4 { + clear: both !important; + position: static !important; + z-index: auto !important; + margin-top: 2rem !important; + } + .st-key-results-report p, .st-key-results-report [data-testid="stMarkdownContainer"] { clear: both !important; position: static !important; z-index: auto !important; - margin-top: 2rem !important; + margin-top: 0.5rem !important; } .st-key-results-report [data-testid="stMarkdownContainer"] h1, @@ -246,7 +276,6 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { clear: both !important; } - /* 3) Formula print fix: show only visual layer, hide assistive MathML */ .st-key-results-report .katex, .st-key-results-report .katex-display, .st-key-results-report mjx-container, @@ -260,7 +289,7 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { display: none !important; } - /* 4) Force strong chart/text contrast for PDF export */ + /* Force strict contrast engine reset across stlite modules */ .st-key-results-report, .st-key-results-report * { color: #111827 !important; @@ -270,10 +299,8 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { } .st-key-results-report [data-testid="stPlotlyChart"], - .st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot, - .st-key-results-report [data-testid="stPlotlyChart"] .plot-container, - .st-key-results-report [data-testid="stPlotlyChart"] .main-svg { - color: #111827 !important; + .st-key-results-report [data-testid="stPlotlyChart"] * { + opacity: 1 !important; } .st-key-results-report .js-plotly-plot text, @@ -308,26 +335,4 @@ div[data-testid="InputInstructions"] > span:nth-child(1) { .st-key-results-report .js-plotly-plot .barlayer .trace path { stroke-opacity: 1 !important; } -} - -/* Plotly text contrast follows the active Streamlit theme */ -.st-key-results-report [data-testid="stPlotlyChart"] { - color: inherit !important; -} - -.st-key-results-report [data-testid="stPlotlyChart"] .js-plotly-plot, -.st-key-results-report [data-testid="stPlotlyChart"] .plot-container, -.st-key-results-report [data-testid="stPlotlyChart"] .main-svg { - color: inherit !important; -} - -.st-key-results-report .js-plotly-plot text, -.st-key-results-report .js-plotly-plot .legendtext, -.st-key-results-report .js-plotly-plot .gtitle, -.st-key-results-report .js-plotly-plot .xtick text, -.st-key-results-report .js-plotly-plot .ytick text, -.st-key-results-report .js-plotly-plot .annotation text, -.st-key-results-report .js-plotly-plot .infolayer text { - fill: currentColor !important; - color: inherit !important; } \ No newline at end of file