<!-- markdownlint-disable MD024 -->
# 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
```
