Issue #138: Add configurable theme settings and custom CSS injection#196
Issue #138: Add configurable theme settings and custom CSS injection#196onemriganka wants to merge 1 commit intoOpenMS:mainfrom onemriganka:issue-138-theming
Conversation
WalkthroughThis change introduces a dynamic theming feature for the Streamlit application. The application now imports a theme configuration dictionary ( Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant A as app.py
participant C as config.py
participant S as Streamlit
U->>A: Launch Application
A->>C: Import THEME_CONFIG
C-->>A: Return theme configuration
A->>A: Create custom_css using THEME_CONFIG values
A->>S: Inject CSS using st.markdown
S-->>U: Render styled UI
Assessment against linked issues
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
config.py (2)
5-9: Consider extracting default values as constants.To improve maintainability, consider extracting the default color values as named constants at the top of the file. This would make future updates easier and provide a single place to modify defaults.
import os + # Default theme colors and fonts + DEFAULT_PRIMARY_COLOR = "#F63366" + DEFAULT_BACKGROUND_COLOR = "#FFFFFF" + DEFAULT_SECONDARY_BG_COLOR = "#F0F2F6" + DEFAULT_TEXT_COLOR = "#000000" + DEFAULT_FONT = "sans serif" + # Theme configuration with defaults; these can be overridden via environment variables. THEME_CONFIG = { - "primaryColor": os.getenv("STREAMLIT_PRIMARY_COLOR", "#F63366"), - "backgroundColor": os.getenv("STREAMLIT_BACKGROUND_COLOR", "#FFFFFF"), - "secondaryBackgroundColor": os.getenv("STREAMLIT_SECONDARY_BACKGROUND_COLOR", "#F0F2F6"), - "textColor": os.getenv("STREAMLIT_TEXT_COLOR", "#000000"), - "font": os.getenv("STREAMLIT_FONT", "sans serif") + "primaryColor": os.getenv("STREAMLIT_PRIMARY_COLOR", DEFAULT_PRIMARY_COLOR), + "backgroundColor": os.getenv("STREAMLIT_BACKGROUND_COLOR", DEFAULT_BACKGROUND_COLOR), + "secondaryBackgroundColor": os.getenv("STREAMLIT_SECONDARY_BACKGROUND_COLOR", DEFAULT_SECONDARY_BG_COLOR), + "textColor": os.getenv("STREAMLIT_TEXT_COLOR", DEFAULT_TEXT_COLOR), + "font": os.getenv("STREAMLIT_FONT", DEFAULT_FONT) }
5-5: The primaryColor is defined but not used in the CSS.The
THEME_CONFIGincludes aprimaryColorkey, but it's not being used in the custom CSS in app.py. Either use this color in the CSS or remove it from the configuration to maintain consistency.app.py (2)
28-28: Security consideration with unsafe_allow_html.Using
unsafe_allow_html=Trueis necessary for CSS injection but opens potential security risks if the input isn't properly controlled. Since you're using environment variables with predefined defaults, the risk is minimal, but it's good practice to add a comment explaining the security implications.# Render the custom CSS on the page + # Note: unsafe_allow_html is required for CSS injection and is safe here as we control the input st.markdown(custom_css, unsafe_allow_html=True)
11-25: Consider enhancing CSS structure with additional comments and organization.The CSS structure could be improved with more comprehensive comments and logical grouping of styles. This would make future additions easier.
# Inject custom CSS using the theme configuration custom_css = f""" <style> + /* + * Global theme settings + * These styles apply to the main components of the Streamlit UI + */ + /* Container styling */ .reportview-container {{ background-color: {THEME_CONFIG['backgroundColor']}; color: {THEME_CONFIG['textColor']}; font-family: {THEME_CONFIG['font']}; }} /* Sidebar styling */ .sidebar .sidebar-content {{ background: {THEME_CONFIG['secondaryBackgroundColor']}; }} </style> """
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app.py(1 hunks)config.py(1 hunks)
🔇 Additional comments (4)
config.py (1)
1-10: Well-structured theme configuration with environment variable support.The implementation allows for flexible theme customization through environment variables with sensible defaults, which aligns well with the PR objectives.
app.py (3)
7-9: Clean import with good comment.The import statement is clear and well-commented, making the source of the theme configuration obvious.
21-23: Same concern with sidebar CSS selector.The
.sidebar .sidebar-contentselector might also be subject to change in future Streamlit updates. Consider using more stable selectors if available.
14-18:Details
✅ Verification successful
Streamlit CSS class selector may change in future versions.
The
.reportview-containerclass is being used for styling, but Streamlit's internal class names might change in future updates. Consider using more stable selectors or check Streamlit's documentation for recommended theming approaches.
🌐 Web query:
What are the recommended ways to customize Streamlit UI with CSS?💡 Result:
To customize the appearance of a Streamlit app using CSS, here are the recommended approaches and considerations based on community practices and official guidance:
1. Inline CSS via
st.markdownEmbed CSS directly in your app using
st.markdownwithunsafe_allow_html=True:st.markdown(""" <style> /* Customize headers */ h1 { font-size: 24px !important; color: #2E86C1; } /* Style buttons */ .stButton > button { background: linear-gradient(45deg, #4CAF50, #45a049); border-radius: 8px; } </style> """, unsafe_allow_html=True)This method is simple but requires caution since Streamlit’s HTML/CSS structure may change in updates, breaking styles[1][10].
2. External CSS Files
Load styles from a separate CSS file for cleaner code:
# Import CSS from an external file def load_css(file_path): with open(file_path, "r") as f: css = f.read() st.markdown(f"<style>{css}</style>", unsafe_allow_html=True) load_css("styles.css") # Call this in your main scriptThis approach is ideal for larger projects (e.g., GitHub templates like Streamlit UI Template) and promotes reusability[6].
3. Targeting Specific Elements
Use browser DevTools to inspect Streamlit’s auto-generated class/ID names (e.g.,
.stButton,.stTextInput) and apply styles:/* Style input boxes */ .stTextInput input { border: 2px solid #3498DB !important; padding: 10px !important; } /* Modify metric widgets */ [data-testid="stMetric"] { background: #F8F9F9; border: 1px solid #D0D3D4; border-radius: 10px; }Note: Class/ID names (e.g.,
st-emotion-cache-zt5igj) are unstable and may change with updates[12][14].
4. Native Theming (Non-CSS)
For foundational styling, configure the
config.tomlfile:[theme] primaryColor = "#4CAF50" backgroundColor = "#FFFFFF" secondaryBackgroundColor = "#F0F2F6" textColor = "#31333F" font = "sans serif"This officially supported method controls colors, fonts, and widget accents globally[2][8].
5. Third-Party Components
Leverage community-built tools like:
streamlit-extrasfor badges, cards, and spacers.streamlit.components.v1.htmlto inject custom HTML/CSS templates[1][9].
Key Considerations
- Avoid dependency on auto-generated classes: Streamlit’s internal class names (e.g.,
.st-emotion-cache-1vbkxwb) are not stable across versions[14].- Use
!importantsparingly: Override default styles carefully to avoid conflicts.- Test across themes: Ensure CSS works in both light/dark modes[9].
- Performance: Heavy CSS animations or external fonts may slow load times[11].
For detailed tutorials, see Streamlit’s Theming Guide or community videos like Styling with Custom CSS[4][10].
Citations:
- 1: https://discuss.streamlit.io/t/faq-how-to-customize-the-style-or-appearance-of-your-streamlit-app/63878
- 2: https://blog.streamlit.io/introducing-theming/
- 3: https://discuss.streamlit.io/t/how-can-i-customize-the-layout-of-my-streamlit-app/69269
- 4: https://www.youtube.com/watch?v=jbJpAdGlKVY
- 5: https://docs.kanaries.net/topics/Streamlit/streamlit-theming
- 6: https://github.com/microsoft/Streamlit_UI_Template
- 7: https://blog.streamlit.io/designing-streamlit-apps-for-the-user-part-ii/
- 8: https://docs.streamlit.io/develop/concepts/configuration/theming
- 9: https://docs.streamlit.io/develop/concepts/custom-components/intro
- 10: https://www.youtube.com/watch?v=gr_KyGfO_eU
- 11: https://discuss.streamlit.io/t/app-with-highly-customized-ui-blazing-fast-load-latest-streamlit-features-usecase-and-user-experience-at-heart/66954
- 12: https://discuss.streamlit.io/t/applying-custom-css-to-manually-created-containers/33428
- 13: https://discuss.streamlit.io/t/css-styling/35243
- 14: https://discuss.streamlit.io/t/css-customization-on-streamlit/60145
Attention: Update Styling Approach for Streamlit's Future Compatibility
File: app.py
Lines: 14–18.reportview-container { background-color: {THEME_CONFIG['backgroundColor']}; color: {THEME_CONFIG['textColor']}; font-family: {THEME_CONFIG['font']}; }While this styling currently works, note that the
.reportview-containerclass is an internal selector generated by Streamlit and may change in future releases. As a more future-proof alternative, consider one of the following approaches:
- Inline CSS via
st.markdown: Inject styles directly into your app usingst.markdownwith theunsafe_allow_html=Trueflag.- External CSS Files: Maintain your styles in a separate CSS file and load them at runtime.
- Native Theming: Leverage Streamlit’s official theming support in the
config.tomlfile for global styling.Refer to Streamlit’s Theming Guide for detailed recommendations to ensure your UI remains stable across updates.
| # Inject custom CSS using the theme configuration | ||
| custom_css = f""" | ||
| <style> | ||
| /* Container styling */ | ||
| .reportview-container {{ | ||
| background-color: {THEME_CONFIG['backgroundColor']}; | ||
| color: {THEME_CONFIG['textColor']}; | ||
| font-family: {THEME_CONFIG['font']}; | ||
| }} | ||
|
|
||
| /* Sidebar styling */ | ||
| .sidebar .sidebar-content {{ | ||
| background: {THEME_CONFIG['secondaryBackgroundColor']}; | ||
| }} | ||
| </style> | ||
| """ |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Missing usage of primaryColor in custom CSS.
The custom CSS does not use the primaryColor from THEME_CONFIG even though it's defined in the configuration. Consider applying this color to relevant UI elements like buttons, links, or headers.
# Inject custom CSS using the theme configuration
custom_css = f"""
<style>
/* Container styling */
.reportview-container {{
background-color: {THEME_CONFIG['backgroundColor']};
color: {THEME_CONFIG['textColor']};
font-family: {THEME_CONFIG['font']};
}}
/* Sidebar styling */
.sidebar .sidebar-content {{
background: {THEME_CONFIG['secondaryBackgroundColor']};
}}
+ /* Button and link styling */
+ .stButton>button, .stTextInput>div>div>input:focus, a {{
+ color: {THEME_CONFIG['primaryColor']};
+ }}
+
+ /* Header styling */
+ h1, h2, h3, h4, h5, h6 {{
+ color: {THEME_CONFIG['primaryColor']};
+ }}
</style>
"""📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Inject custom CSS using the theme configuration | |
| custom_css = f""" | |
| <style> | |
| /* Container styling */ | |
| .reportview-container {{ | |
| background-color: {THEME_CONFIG['backgroundColor']}; | |
| color: {THEME_CONFIG['textColor']}; | |
| font-family: {THEME_CONFIG['font']}; | |
| }} | |
| /* Sidebar styling */ | |
| .sidebar .sidebar-content {{ | |
| background: {THEME_CONFIG['secondaryBackgroundColor']}; | |
| }} | |
| </style> | |
| """ | |
| # Inject custom CSS using the theme configuration | |
| custom_css = f""" | |
| <style> | |
| /* Container styling */ | |
| .reportview-container {{ | |
| background-color: {THEME_CONFIG['backgroundColor']}; | |
| color: {THEME_CONFIG['textColor']}; | |
| font-family: {THEME_CONFIG['font']}; | |
| }} | |
| /* Sidebar styling */ | |
| .sidebar .sidebar-content {{ | |
| background: {THEME_CONFIG['secondaryBackgroundColor']}; | |
| }} | |
| /* Button and link styling */ | |
| .stButton>button, .stTextInput>div>div>input:focus, a {{ | |
| color: {THEME_CONFIG['primaryColor']}; | |
| }} | |
| /* Header styling */ | |
| h1, h2, h3, h4, h5, h6 {{ | |
| color: {THEME_CONFIG['primaryColor']}; | |
| }} | |
| </style> | |
| """ |
|
Could you describe how your PR differs from #172 and why the CSS injection is needed? |
|
In contrast to #172—which primarily focused on enhancing visualization themes (i.e., aligning plot themes across libraries and improving responsiveness)—this PR introduces a modular, configuration-driven theming system. It adds a dedicated configuration file (config.py) that reads theme settings (colors, fonts, etc.) from environment variables and then uses CSS injection in app.py to override Streamlit’s default styling. The CSS injection is needed because Streamlit’s built-in theming (via config.toml) is somewhat limited when it comes to granular UI customizations. By dynamically generating a custom CSS block from the external configuration, we can ensure consistent and immediate application of user-defined styles (for both containers and sidebars) without modifying core code. This approach also maintains backward compatibility—if no environment variables are provided, default styling is preserved. |
|
Thanks for the suggestion. However, I dont think functionality to set themes via environment variables is needed. |
PR Title:
Enhance Theme Customization via Config File (Fixes #138)
PR Description:
This pull request addresses issue [#138] by introducing an external configuration for theming, which enables users to customize the app’s appearance without modifying core code. The changes include:
New
config.pyFile:A new file is added at the root level that defines theme settings (primaryColor, backgroundColor, secondaryBackgroundColor, textColor, and font). These settings can be overridden via environment variables, ensuring flexibility for different deployment scenarios.
Custom CSS Injection in
app.py:The main application file (
app.py) is modified to import the theme configuration fromconfig.pyand inject a custom CSS block based on these settings. This ensures the entire app adheres to the specified theme without affecting the existing navigation and page rendering logic.Backward Compatibility:
The changes are additive and do not alter the core functionality of the app. If no environment variables are provided, default values are used, ensuring the existing look is maintained.
Documentation Updates:
Instructions have been added within this PR’s notes to inform users how to customize theme settings using environment variables.
Testing Instructions:
This approach modularizes theme customization, making future enhancements straightforward and providing a more user-friendly configuration method.
Summary by CodeRabbit