Skip to content

Theme

One Theme holds the app's design tokens: Colors, Typography, a Spacing scale, and Radii. Components read it with use_theme and derive styles from it once per theme with use_styles. The built-in navigators draw their headers, tab bars, and drawers from the same tokens. ThemeProvider supplies a light and a dark theme and picks one from the effective color scheme; without it, LIGHT_THEME and DARK_THEME apply.

Design tokens shared by the application and the navigators.

A Theme is a frozen, keyword-only dataclass: whether it's dark, a Colors palette, Typography text styles, a Spacing scale, and Radii. Components read it with use_theme; the built-in navigators draw their headers, tab bars, and drawers from the same tokens, so one theme styles the whole app.

Without a provider, the theme follows the system appearance: LIGHT_THEME or DARK_THEME. Provide your own pair with ThemeProvider, and add tokens by subclassing:

from dataclasses import dataclass, replace

import pythonnative as pn


@dataclass(frozen=True, kw_only=True)
class BrandTheme(pn.Theme):
    accent: pn.Color = "#FF2D55"


LIGHT = BrandTheme(colors=replace(pn.LIGHT_THEME.colors, primary="#5B21B6"))
DARK = BrandTheme(dark=True, colors=replace(pn.DARK_THEME.colors, primary="#A78BFA"), accent="#FF6482")


@pn.component
def App() -> pn.Node:
    return pn.ThemeProvider(Home(), light=LIGHT, dark=DARK)


@pn.component
def Home() -> pn.Node:
    theme = pn.use_theme(BrandTheme)
    return pn.Text("Hello", style={"color": theme.accent})

Styles that depend on the theme are derived once per theme with use_styles.

Classes:

Name Description
Colors

A theme's color palette.

Typography

Named text styles; each is a Style to pass to pn.Text.

Spacing

A spacing scale in points, for padding, margins, and gap.

Radii

Corner radii in points; full makes a pill or circle.

Theme

Design tokens read through use_theme.

Functions:

Name Description
ThemeProvider

Provide light or dark to the subtree, following the effective color scheme.

use_theme

Return the active Theme and re-render when it changes.

use_styles

Return factory(theme), computed once per theme for this component.

Attributes:

Name Type Description
LIGHT_THEME

The built-in light theme, used when the system appearance is light.

DARK_THEME

The built-in dark theme, used when the system appearance is dark.

LIGHT_THEME module-attribute

LIGHT_THEME = Theme(
    colors=Colors(
        primary="#007AFF",
        background="#F2F2F7",
        surface="#FFFFFF",
        text="#000000",
        text_secondary="#6C6C70",
        border="#D1D1D6",
        error="#FF3B30",
        success="#34C759",
        warning="#FF9500",
    )
)

The built-in light theme, used when the system appearance is light.

DARK_THEME module-attribute

DARK_THEME = Theme(
    dark=True,
    colors=Colors(
        primary="#0A84FF",
        background="#000000",
        surface="#1C1C1E",
        text="#FFFFFF",
        text_secondary="#98989F",
        border="#38383A",
        error="#FF453A",
        success="#30D158",
        warning="#FF9F0A",
    ),
)

The built-in dark theme, used when the system appearance is dark.

Colors dataclass

Colors(
    *,
    primary: Color,
    background: Color,
    surface: Color,
    text: Color,
    text_secondary: Color,
    border: Color,
    error: Color,
    success: Color,
    warning: Color
)

A theme's color palette.

Attributes:

Name Type Description
primary Color

Accent for interactive elements. Navigators use it for the back button, header buttons, the selected tab, and the active drawer row.

background Color

Screen background.

surface Color

Raised surfaces such as cards, sheets, the navigation header, the tab bar, and the drawer panel.

text Color

Primary text, including header titles.

text_secondary Color

De-emphasized text.

border Color

Hairlines and dividers, including the ones between the header or tab bar and the content.

error Color

Destructive and error states, and tab badges.

success Color

Success states.

warning Color

Warning states.

Typography dataclass

Typography(
    *,
    title: Style = _text(font_size=28, font_weight="700"),
    heading: Style = _text(font_size=20, font_weight="600"),
    body: Style = _text(font_size=16),
    label: Style = _text(font_size=15, font_weight="500"),
    caption: Style = _text(font_size=13)
)

Named text styles; each is a Style to pass to pn.Text.

Attributes:

Name Type Description
title Style

Screen titles.

heading Style

Section headings.

body Style

Body text.

label Style

Labels on controls and list rows.

caption Style

Captions and footnotes.

Spacing dataclass

Spacing(
    *,
    xs: float = 4,
    sm: float = 8,
    md: float = 16,
    lg: float = 24,
    xl: float = 32
)

A spacing scale in points, for padding, margins, and gap.

Radii dataclass

Radii(
    *,
    sm: float = 4,
    md: float = 8,
    lg: float = 16,
    full: float = 9999
)

Corner radii in points; full makes a pill or circle.

Theme dataclass

Theme(
    *,
    dark: bool = False,
    colors: Colors,
    typography: Typography = Typography(),
    spacing: Spacing = Spacing(),
    radii: Radii = Radii()
)

Design tokens read through use_theme.

Subclass it (keeping frozen=True, kw_only=True) to add application tokens, and derive variations with dataclasses.replace.

Attributes:

Name Type Description
dark bool

Whether the theme is meant for a dark appearance.

colors Colors

The Colors palette.

typography Typography

Named Typography text styles.

spacing Spacing

The Spacing scale.

radii Radii

Corner Radii.

ThemeProvider

ThemeProvider(
    *children: Node,
    light: Theme = LIGHT_THEME,
    dark: Theme = DARK_THEME
) -> Element

Provide light or dark to the subtree, following the effective color scheme.

The effective scheme is the system appearance unless the app overrides it with appearance.set_color_scheme. Pass the same theme for both to pin one regardless of appearance.

Parameters:

Name Type Description Default
*children Node

The subtree that reads the theme.

()
light Theme

Theme used when the scheme is light.

LIGHT_THEME
dark Theme

Theme used when the scheme is dark.

DARK_THEME

use_theme

use_theme() -> Theme
use_theme(kind: type[ThemeT]) -> ThemeT
use_theme(kind: Optional[type[Theme]] = None) -> Theme

Return the active Theme and re-render when it changes.

The nearest ThemeProvider wins; without one, the built-in light or dark theme follows the effective color scheme.

Parameters:

Name Type Description Default
kind Optional[type[Theme]]

Optional Theme subclass. The return value is typed as that subclass, and a provided theme of another type raises.

None

Raises:

Type Description
TypeError

If kind is given and the active theme isn't an instance of it.

RuntimeError

If called outside a @component function.

Example
@pn.component
def Card(*children: pn.Node) -> pn.Node:
    theme = pn.use_theme()
    return pn.View(
        *children,
        style={"background_color": theme.colors.surface, "padding": theme.spacing.md},
    )

use_styles

use_styles(factory: Callable[[ThemeT], S]) -> S

Return factory(theme), computed once per theme for this component.

factory is usually a class whose __init__ takes the theme and assigns styles as attributes, so they're typed and misspelled names are static errors:

class CardStyles:
    def __init__(self, theme: pn.Theme) -> None:
        self.card = pn.style(background_color=theme.colors.surface, border_radius=theme.radii.md)
        self.title = theme.typography.heading


@pn.component
def Card(title: str) -> pn.Node:
    styles = pn.use_styles(CardStyles)
    return pn.View(pn.Text(title, style=styles.title), style=styles.card)

Parameters:

Name Type Description Default
factory Callable[[ThemeT], S]

A callable taking the active theme.

required

Returns:

Type Description
S

Whatever factory returns, with its type.

Next steps