Files
2025-11-30 08:48:10 +08:00

7.7 KiB

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

# 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:

from shiny.express import ui

ui.page_opts(theme=ui.Theme.from_brand(__file__))

# ... rest of app

Shiny Core:

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

ui.Theme.from_brand(brand)

The brand parameter accepts:

File Path (Most Common)

# 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

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

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

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

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

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:

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:

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:

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:

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
# 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:

# 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