Skip to content

Branding Material UI

Customize panel-material-ui apps with consistent theming, colors, typography, and brand assets.

Contents

Styling Layers

Layer Scope Use
theme_config Global (flows to children) App-wide palette, typography, shape, component defaults
sx Local instance One-off styling, nested selectors, dark/light mode overrides
styles Local instance Outer container box (spacing, borders, backgrounds)
stylesheets Local instance Classic Panel internals via CSS selectors

sx Examples

# Basic styling
pmui.Button(label="Custom", sx={"color": "white", "backgroundColor": "black"})

# Hover states
pmui.Button(sx={"&:hover": {"backgroundColor": "gray"}})

# Dark/light mode overrides
pmui.Button(sx={"&.mui-dark:hover": {"backgroundColor": "orange"}})

# Target nested MUI parts
pmui.FloatSlider(sx={"& .MuiSlider-thumb": {"borderRadius": 0}})

Palette

Each color category has four tokens: main, light, dark, contrastText. Only main is required; others auto-compute.

theme_config = {
    "palette": {
        "primary": {"main": "#6200ea"},
        "secondary": {"main": "#03dac6"},
        "error": {"main": "#b00020"},
    }
}

Token Reference

Token Use
primary.main Primary brand color
primary.contrastText Text on primary background
text.primary Main text color
text.secondary Muted/secondary text
background.default Page background
background.paper Card/paper background

Accessibility

theme_config = {
    "palette": {
        "contrastThreshold": 4.5,  # WCAG 2.1 compliance (default: 3)
        "tonalOffset": 0.2,        # Light/dark variant shift
        "primary": {"main": "#3f50b5"},
    }
}

Typography

theme_config = {
    "typography": {
        "fontFamily": "Montserrat, Helvetica Neue, Arial, sans-serif",
        "fontSize": 14,  # Base size in px
        "h1": {"fontSize": "2.5rem", "fontWeight": 700},
        "body1": {"fontWeight": 500},
        "button": {"fontStyle": "italic"},
    }
}

Variants: h1-h6, subtitle1, subtitle2, body1, body2, button, caption, overline.

Responsive Typography

pmui.Typography("Responsive", sx={
    "fontSize": "1.2rem",
    "@media (min-width: 600px)": {"fontSize": "1.5rem"},
    "@media (min-width: 900px)": {"fontSize": "2.4rem"},
})

Shape

theme_config = {
    "shape": {"borderRadius": 8}  # Default corner radius for all components
}

Component Overrides

Override defaults for all instances of a component type:

theme_config = {
    "components": {
        "MuiButton": {
            "defaultProps": {"disableRipple": True},
            "styleOverrides": {"root": {"fontSize": "1rem"}},
        },
        "MuiPaper": {
            "styleOverrides": {"root": {"padding": "16px"}},
        },
    }
}

Variant-Based Overrides

theme_config = {
    "components": {
        "MuiCard": {
            "styleOverrides": {
                "root": {
                    "variants": [{
                        "props": {"variant": "outlined"},
                        "style": {"borderWidth": "3px"},
                    }]
                }
            }
        }
    }
}

Icons

Use Material Icons from fonts.google.com/icons.

Icon Parameter

Buttons and some widgets accept icon directly:

pmui.Button(label="Save", icon="save")
pmui.Button(label="Delete", icon="delete_outlined")  # Outlined variant
pmui.ButtonIcon(icon="settings")

Token Syntax in Labels

Embed icons in text with :material/icon_name::

pmui.Select(options=["Zoom :material/zoom:", "Pan :material/pan_tool:"])
pmui.Button(label="Warning :material/warning@color=warning:")

Token options: @size=large, @color=warning, @variant=outlined.

HTML/Markdown

pmui.Typography('<span class="material-icons">lightbulb</span> Idea')

Brand Assets

Logo and Favicon

pmui.Page.param.logo.default = "/path/to/logo.png"
pmui.Page.favicon = "/path/to/favicon.ico"
pmui.Page.meta.name = "My App"

Custom CSS

pmui.Page.config.raw_css.append("body { font-family: Montserrat; }")
pmui.Page.config.css_files.append(
    "https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap"
)

Component Defaults

pn.pane.Image.stylesheets = ["img {border-radius: 8px}"]
pn.widgets.Tabulator.param.theme.default = "materialize"
pmui.Button.param.disable_elevation.default = True

Chart Theming

Plots auto-theme when using pmui.Page or pmui.ThemeToggle.

Categorical Palette

primary = "#6200ea"
colors = pmui.theme.generate_palette(primary)

df.hvplot.scatter(x="x", y="y", color="category", cmap=colors)

Continuous Colormap

cmap = pmui.theme.linear_gradient("#ffffff", "#6200ea", n=256)

Complete Example

import panel as pn
import panel_material_ui as pmui

pn.extension()

THEME = {
    "light": {
        "palette": {
            "primary": {"main": "#4099da"},
            "secondary": {"main": "#644c76"},
        },
        "typography": {
            "fontFamily": "Montserrat, sans-serif",
            "fontSize": 14,
        },
        "shape": {"borderRadius": 8},
    },
    "dark": {
        "palette": {
            "primary": {"main": "#64b5f6"},
            "secondary": {"main": "#9575cd"},
        },
        "typography": {
            "fontFamily": "Montserrat, sans-serif",
            "fontSize": 14,
        },
        "shape": {"borderRadius": 8},
    },
}

pmui.Page(
    title="Branded App",
    theme_config=THEME,
    sidebar=[pmui.Button(label="Action", icon="bolt", color="primary")],
    main=[pmui.Typography("Welcome", variant="h4")],
).servable()