Initial commit
This commit is contained in:
395
skills/brand-yml/references/shiny-python.md
Normal file
395
skills/brand-yml/references/shiny-python.md
Normal file
@@ -0,0 +1,395 @@
|
||||
# Using brand.yml with Shiny for Python
|
||||
|
||||
Guide for applying brand.yml styling to Shiny for Python applications using ui.Theme.
|
||||
|
||||
## Overview
|
||||
|
||||
Shiny for Python integrates brand.yml through the `ui.Theme.from_brand()` method, which creates custom themes from `_brand.yml` files. This enables consistent branding across Shiny apps with minimal configuration.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Install Shiny with theme support
|
||||
pip install "shiny[theme]"
|
||||
|
||||
# Or install separately
|
||||
pip install shiny libsass
|
||||
|
||||
# Optional: Install brand_yml for programmatic access
|
||||
pip install brand_yml
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Automatic Discovery
|
||||
|
||||
Place `_brand.yml` at your app directory root:
|
||||
|
||||
```
|
||||
my-app/
|
||||
├── _brand.yml
|
||||
├── app.py
|
||||
└── ...
|
||||
```
|
||||
|
||||
Then use `ui.Theme.from_brand()`:
|
||||
|
||||
**Shiny Express:**
|
||||
|
||||
```python
|
||||
from shiny.express import ui
|
||||
|
||||
ui.page_opts(theme=ui.Theme.from_brand(__file__))
|
||||
|
||||
# ... rest of app
|
||||
```
|
||||
|
||||
**Shiny Core:**
|
||||
|
||||
```python
|
||||
from shiny import App, ui
|
||||
|
||||
app_ui = ui.page_fluid(
|
||||
ui.Theme.from_brand(__file__),
|
||||
ui.h2("My App"),
|
||||
# ... rest of UI
|
||||
)
|
||||
|
||||
def server(input, output, session):
|
||||
pass
|
||||
|
||||
app = App(app_ui, server)
|
||||
```
|
||||
|
||||
## ui.Theme.from_brand() Parameters
|
||||
|
||||
```python
|
||||
ui.Theme.from_brand(brand)
|
||||
```
|
||||
|
||||
The `brand` parameter accepts:
|
||||
|
||||
### File Path (Most Common)
|
||||
|
||||
```python
|
||||
# Use __file__ for app directory
|
||||
ui.Theme.from_brand(__file__)
|
||||
|
||||
# Explicit file path
|
||||
ui.Theme.from_brand("path/to/_brand.yml")
|
||||
|
||||
# Explicit directory (auto-finds _brand.yml)
|
||||
ui.Theme.from_brand("branding/")
|
||||
```
|
||||
|
||||
### Brand Object
|
||||
|
||||
```python
|
||||
from brand_yml import Brand
|
||||
|
||||
brand = Brand.from_yaml("_brand.yml")
|
||||
ui.Theme.from_brand(brand)
|
||||
```
|
||||
|
||||
## Search Behavior
|
||||
|
||||
When given `__file__` or a directory path, the method searches for `_brand.yml`:
|
||||
|
||||
1. In the specified directory
|
||||
2. In `_brand/` subdirectory
|
||||
3. In `brand/` subdirectory
|
||||
4. In parent directories (recursive)
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Shiny Express App
|
||||
|
||||
```python
|
||||
from shiny.express import input, render, ui
|
||||
|
||||
ui.page_opts(
|
||||
title="My Dashboard",
|
||||
theme=ui.Theme.from_brand(__file__)
|
||||
)
|
||||
|
||||
with ui.sidebar():
|
||||
ui.input_slider("n", "Number of observations", 1, 100, 50)
|
||||
|
||||
@render.plot
|
||||
def histogram():
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
data = np.random.randn(input.n())
|
||||
plt.hist(data, bins=20)
|
||||
plt.xlabel("Value")
|
||||
plt.ylabel("Frequency")
|
||||
```
|
||||
|
||||
### Shiny Core App
|
||||
|
||||
```python
|
||||
from shiny import App, render, ui
|
||||
|
||||
app_ui = ui.page_sidebar(
|
||||
ui.sidebar(
|
||||
ui.input_slider("n", "Number of observations", 1, 100, 50),
|
||||
),
|
||||
ui.output_plot("histogram"),
|
||||
title="My Dashboard",
|
||||
theme=ui.Theme.from_brand(__file__)
|
||||
)
|
||||
|
||||
def server(input, output, session):
|
||||
@render.plot
|
||||
def histogram():
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
data = np.random.randn(input.n())
|
||||
plt.hist(data, bins=20)
|
||||
plt.xlabel("Value")
|
||||
plt.ylabel("Frequency")
|
||||
|
||||
app = App(app_ui, server)
|
||||
```
|
||||
|
||||
### With Custom Path
|
||||
|
||||
```python
|
||||
from shiny.express import ui
|
||||
|
||||
# Shared brand file
|
||||
ui.page_opts(theme=ui.Theme.from_brand("../shared-branding/_brand.yml"))
|
||||
|
||||
# Named brand file
|
||||
ui.page_opts(theme=ui.Theme.from_brand("company-brand.yml"))
|
||||
|
||||
# Directory with _brand.yml inside
|
||||
ui.page_opts(theme=ui.Theme.from_brand("branding/"))
|
||||
```
|
||||
|
||||
### Multiple Page Types
|
||||
|
||||
```python
|
||||
from shiny import App, ui
|
||||
|
||||
# page_fluid
|
||||
app_ui = ui.page_fluid(
|
||||
theme=ui.Theme.from_brand(__file__),
|
||||
# ... content
|
||||
)
|
||||
|
||||
# page_sidebar
|
||||
app_ui = ui.page_sidebar(
|
||||
theme=ui.Theme.from_brand(__file__),
|
||||
ui.sidebar(
|
||||
# ... sidebar content
|
||||
),
|
||||
# ... main content
|
||||
)
|
||||
|
||||
# page_navbar
|
||||
app_ui = ui.page_navbar(
|
||||
ui.nav_panel("Tab 1", # ...),
|
||||
ui.nav_panel("Tab 2", # ...),
|
||||
title="My App",
|
||||
theme=ui.Theme.from_brand(__file__)
|
||||
)
|
||||
|
||||
# page_fillable
|
||||
app_ui = ui.page_fillable(
|
||||
theme=ui.Theme.from_brand(__file__),
|
||||
# ... content
|
||||
)
|
||||
```
|
||||
|
||||
## Combining with Custom Theme Rules
|
||||
|
||||
Extend brand.yml themes with custom Sass:
|
||||
|
||||
```python
|
||||
from shiny.express import ui
|
||||
|
||||
theme = (
|
||||
ui.Theme.from_brand(__file__)
|
||||
.add_rules("""
|
||||
.custom-card {
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
""")
|
||||
)
|
||||
|
||||
ui.page_opts(theme=theme)
|
||||
```
|
||||
|
||||
Available theme methods (chainable):
|
||||
- `.add_defaults()` - Override Bootstrap variables
|
||||
- `.add_functions()` - Add Sass functions
|
||||
- `.add_mixins()` - Add Sass mixins
|
||||
- `.add_rules()` - Add CSS rules
|
||||
- `.add_uses()` - Add Sass declarations
|
||||
|
||||
## Programmatic Access with brand_yml
|
||||
|
||||
For advanced use cases, access brand data programmatically:
|
||||
|
||||
```python
|
||||
from brand_yml import Brand
|
||||
|
||||
# Read brand file
|
||||
brand = Brand.from_yaml("_brand.yml")
|
||||
|
||||
# Or from string
|
||||
yaml_content = """
|
||||
color:
|
||||
palette:
|
||||
blue: "#0066cc"
|
||||
primary: blue
|
||||
"""
|
||||
brand = Brand.from_yaml_str(yaml_content)
|
||||
|
||||
# Access brand elements
|
||||
brand.meta.name # Organization name
|
||||
brand.color.palette.blue # "#0066cc"
|
||||
brand.color.primary # "blue"
|
||||
brand.typography.base.family # Font family name
|
||||
|
||||
# Use in UI
|
||||
from shiny import ui
|
||||
|
||||
app_ui = ui.page_fluid(
|
||||
theme=ui.Theme.from_brand(brand),
|
||||
ui.h2(brand.meta.name),
|
||||
# ... more content
|
||||
)
|
||||
```
|
||||
|
||||
## Sample _brand.yml for Shiny
|
||||
|
||||
Minimal example:
|
||||
|
||||
```yaml
|
||||
color:
|
||||
palette:
|
||||
brand-blue: "#0066cc"
|
||||
brand-gray: "#666666"
|
||||
primary: brand-blue
|
||||
foreground: brand-gray
|
||||
background: "#ffffff"
|
||||
|
||||
typography:
|
||||
fonts:
|
||||
- family: Inter
|
||||
source: google
|
||||
weight: [400, 600]
|
||||
base:
|
||||
family: Inter
|
||||
size: 16px
|
||||
headings:
|
||||
family: Inter
|
||||
weight: 600
|
||||
```
|
||||
|
||||
More complete example:
|
||||
|
||||
```yaml
|
||||
meta:
|
||||
name: My Company
|
||||
link: https://mycompany.com
|
||||
|
||||
color:
|
||||
palette:
|
||||
blue: "#0066cc"
|
||||
navy: "#003366"
|
||||
gray: "#666666"
|
||||
light-gray: "#f5f5f5"
|
||||
primary: blue
|
||||
secondary: gray
|
||||
success: "#28a745"
|
||||
info: blue
|
||||
warning: "#ffc107"
|
||||
danger: "#dc3545"
|
||||
foreground: navy
|
||||
background: "#ffffff"
|
||||
|
||||
typography:
|
||||
fonts:
|
||||
- family: Inter
|
||||
source: google
|
||||
weight: [400, 500, 600, 700]
|
||||
style: [normal, italic]
|
||||
- family: Fira Code
|
||||
source: google
|
||||
weight: [400, 500]
|
||||
base:
|
||||
family: Inter
|
||||
size: 16px
|
||||
line-height: 1.5
|
||||
headings:
|
||||
family: Inter
|
||||
weight: 600
|
||||
line-height: 1.2
|
||||
monospace:
|
||||
family: Fira Code
|
||||
size: 14px
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use __file__**: Most reliable way to locate `_brand.yml` in app directory
|
||||
- **Start simple**: Begin with colors and one font
|
||||
- **Test paths**: If brand doesn't apply, try explicit paths
|
||||
- **Version control**: Include `_brand.yml` in git repository
|
||||
- **Precompile for production**: Use `.to_css()` to avoid runtime Sass compilation
|
||||
|
||||
```python
|
||||
# Development
|
||||
theme = ui.Theme.from_brand(__file__)
|
||||
|
||||
# Production (precompile)
|
||||
theme_css = ui.Theme.from_brand(__file__).to_css()
|
||||
# Save to static/theme.css, then reference in production
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Theme not applying?**
|
||||
- Check file is named `_brand.yml` (with underscore)
|
||||
- Verify `libsass` is installed: `pip install libsass`
|
||||
- Try explicit path: `ui.Theme.from_brand("path/to/_brand.yml")`
|
||||
- Check for YAML syntax errors
|
||||
|
||||
**Colors not matching?**
|
||||
- Ensure hex colors have quotes: `"#0066cc"`
|
||||
- Verify color names match palette definitions
|
||||
- Check semantic colors reference valid palette names
|
||||
|
||||
**Fonts not loading?**
|
||||
- Verify Google Fonts spelling and availability
|
||||
- Ensure `source: google` is specified
|
||||
- Check font family names match exactly
|
||||
- Internet connection required for Google Fonts
|
||||
|
||||
**Import errors?**
|
||||
- Install theme support: `pip install "shiny[theme]"`
|
||||
- Or install libsass separately: `pip install libsass`
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
For production apps with many instances, precompile the theme:
|
||||
|
||||
```python
|
||||
# build_theme.py
|
||||
from shiny import ui
|
||||
|
||||
theme = ui.Theme.from_brand("_brand.yml")
|
||||
css = theme.to_css()
|
||||
|
||||
with open("static/brand-theme.css", "w") as f:
|
||||
f.write(css)
|
||||
|
||||
# Then in app.py, reference the CSS file directly
|
||||
# This avoids runtime Sass compilation overhead
|
||||
```
|
||||
Reference in New Issue
Block a user