Access background color from dash_bootstrap_templates inside dash callback

I use ThemeChangerAIO to select themes to use in the dashboard. How I can access the currently configured background color inside the dash callback?

I found a post on stackoverflow where someone suggests using clientside callbacks like:

import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Output, Input
import dash_bootstrap_components as dbc

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div([dcc.Store(id="intermediate-store"), dcc.Store(id="color-store")])

app.clientside_callback(
    """
    function(val) {
        const bodyStyles = window.getComputedStyle(document.body);
        return {
            primary: bodyStyles.getPropertyValue("--primary"),
            secondary: bodyStyles.getPropertyValue("--secondary"),
            success: bodyStyles.getPropertyValue("--success"),
        };
    }
    """,
    Output("intermediate-store", "data"),
    Input("intermediate-store", "data"),
)


@app.callback(
    Output("color-store", "data"),
    Input("intermediate-store", "data"),
)
def update_output_div(data):
    # data equals: {'primary': '#007bff', 'secondary': '#6c757d', 'success': '#28a745'}
    return data


if __name__ == "__main__":
    app.run_server(debug=True)

but all colors are returned empty. I verified that bodyStyles doesn’t contain those colors. What’s wrong? I’m a JS novice…

@kiteme Can yo say what you plan to do once you get the colors? It may be possible to just use class names.

@AnnMarieW
I would like to use it in Heatmap - cell border - #3 by RenaudLN (to make the heatmap plotted over another heatmap have the same color as the background)
I’m also using dcc.Tabs and I would like to configure selected_style in dcc.Tab to use one of the main template colors

@kiteme
You can upadate figures in a callback with the Bootstrap theme using dash-bootstrap-templates. More information on that here: Dash Bootstrap Templates

If you use dbc.Tabs, the theme color will update automatically for you. For dcc.Tabs, you can also use the stylesheet from dash-bootstrap-templates to automatically update dash core components with the theme. See more info here: Dash Bootstrap Theme Explorer. You can also set the color in dcc.Tabs with className propery ie `selected_className=“primary”

If it’s for some other reason, the clientside callback you showed works for dash-bootstrap-components <= V0.13.0 which uses Bootstrap 4.0. However, dbc version>=1.0.0 uses Bootstrap 5, and Bootstrap changed the propery names of the colors in V5.

Here’s an updated clientside callback:

from dash import Dash, dcc, html, Output, Input

import dash_bootstrap_components as dbc

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div([dcc.Store(id="intermediate-store"), dcc.Store(id="color-store")])

app.clientside_callback(
    """
    function(val) {
        const bodyStyles = window.getComputedStyle(document.body);
        return {
            primary: bodyStyles.getPropertyValue("--bs-primary"),
            secondary: bodyStyles.getPropertyValue("--bs-secondary"),
            success: bodyStyles.getPropertyValue("--bs-success"),
        };
    }
    """,
    Output("intermediate-store", "data"),
    Input("intermediate-store", "data"),
)


@app.callback(
    Output("color-store", "data"),
    Input("intermediate-store", "data"),
)
def update_output_div(data):
    # data equals: {'primary': '#007bff', 'secondary': '#6c757d', 'success': '#28a745'}
    print(data)
    return data


if __name__ == "__main__":
    app.run_server(debug=True)


Note an easy way to find all the color names is by inspecting the browser:

@AnnMarieW thank you - the updated callback works great!
Could you please just clarify for me re dcc.Tab - I want to use e.g. primary color as the background of the selected tab, like in the example from Tab | Dash for Python Documentation | Plotly :

from dash import Dash, dcc, html
from dash.dependencies import Input, Output

app = Dash(__name__)

tabs_styles = {
    'height': '44px'
}
tab_style = {
    'borderBottom': '1px solid #d6d6d6',
    'padding': '6px',
    'fontWeight': 'bold'
}

tab_selected_style = {
    'borderTop': '1px solid #d6d6d6',
    'borderBottom': '1px solid #d6d6d6',
    'backgroundColor': '#119DFF',
    'color': 'white',
    'padding': '6px'
}

app.layout = html.Div([
    dcc.Tabs(id="tabs-inline", value='tab-1', children=[
        dcc.Tab(label='Tab 1', value='tab-1', style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label='Tab 2', value='tab-2', style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label='Tab 3', value='tab-3', style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label='Tab 4', value='tab-4', style=tab_style, selected_style=tab_selected_style),
    ], style=tabs_styles),
    html.Div(id='tabs-content-inline-3')
])

@app.callback(Output('tabs-content-inline-3', 'children'),
              Input('tabs-inline', 'value'))
def render_content(tab):
    if tab == 'tab-1':
        return html.Div([
            html.H3('Tab content 1')
        ])
    elif tab == 'tab-2':
        return html.Div([
            html.H3('Tab content 2')
        ])
    elif tab == 'tab-3':
        return html.Div([
            html.H3('Tab content 3')
        ])
    elif tab == 'tab-4':
        return html.Div([
            html.H3('Tab content 4')
        ])

if __name__ == '__main__':
    app.run_server(debug=True)

where selected tab background color is set in ‘backgroundColor’ in tab_selected_style

@kiteme

Here are two examples - the first is simpler and just uses Bootstrap classes. The second uses variable names with css.

Example 1 Using Boostrap classes

from dash import Dash, dcc, html, Output, Input
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div([
    dcc.Tabs(id="tabs-inline", value='tab-1', children=[
        dcc.Tab(label='Tab 1', value='tab-1',  selected_className="bg-primary text-light"),
        dcc.Tab(label='Tab 2', value='tab-2',  selected_className="bg-primary text-light"),
        dcc.Tab(label='Tab 3', value='tab-3', selected_className="bg-primary text-light"),
        dcc.Tab(label='Tab 4', value='tab-4',  selected_className="bg-primary text-light"),
    ], ),
    html.Div(id='tabs-content-inline-3')
])

@app.callback(Output('tabs-content-inline-3', 'children'),
              Input('tabs-inline', 'value'))
def render_content(tab):
    if tab == 'tab-1':
        return html.Div([
            html.H3('Tab content 1')
        ])
    elif tab == 'tab-2':
        return html.Div([
            html.H3('Tab content 2')
        ])
    elif tab == 'tab-3':
        return html.Div([
            html.H3('Tab content 3')
        ])
    elif tab == 'tab-4':
        return html.Div([
            html.H3('Tab content 4')
        ])

if __name__ == '__main__':
    app.run_server(debug=True)



Example 2 Using CSS Variables

If you would like to fine-tune the style, you can also use css variables so that it will automatically update based on the theme:

 'backgroundColor': 'var(--bs-primary)',

Here’s a complete example:


from dash import Dash, dcc, html, Output, Input

import dash_bootstrap_components as dbc

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
from dash import Dash, dcc, html
from dash.dependencies import Input, Output



tabs_styles = {
    'height': '44px'
}
tab_style = {
    'borderBottom': '1px solid var(--bs-gray-300)',
    'padding': '6px',
    'fontWeight': 'bold'
}

tab_selected_style = {
    'borderTop': '1px solid var(--bs-gray-300)',
    'borderBottom': '1px solid var(--bs-gray-300)',
    'backgroundColor': 'var(--bs-primary)',
    'color': 'white',
    'padding': '6px'
}

app.layout = html.Div([
    dcc.Tabs(id="tabs-inline", value='tab-1', children=[
        dcc.Tab(label='Tab 1', value='tab-1', style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label='Tab 2', value='tab-2', style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label='Tab 3', value='tab-3', style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label='Tab 4', value='tab-4', style=tab_style, selected_style=tab_selected_style),
    ], ),
    html.Div(id='tabs-content-inline-3')
])

@app.callback(Output('tabs-content-inline-3', 'children'),
              Input('tabs-inline', 'value'))
def render_content(tab):
    if tab == 'tab-1':
        return html.Div([
            html.H3('Tab content 1')
        ])
    elif tab == 'tab-2':
        return html.Div([
            html.H3('Tab content 2')
        ])
    elif tab == 'tab-3':
        return html.Div([
            html.H3('Tab content 3')
        ])
    elif tab == 'tab-4':
        return html.Div([
            html.H3('Tab content 4')
        ])

if __name__ == '__main__':
    app.run_server(debug=True)

Both work well. Thank you!

@AnnMarieW
I’m sorry but I have one more question about the app.clientside_callback. I tried to combine it with ThemeChangerAIO and trigger this clientside callback with event Input(ThemeChangerAIO.ids.radio(“theme”), “value”), but this event seems to be triggered when the new theme is selected, not once it’s loaded - so consequently I always get background color for the previous theme as the new one is still loading. Is there another event I could use to trigger the clientside callback on theme refresh?

@kiteme

oh yes, I could replicate the delay in the stored theme colors that you reported.

Here’s one solution that seems to get around the timing difference. When the theme changes, it updates an intermediate store, which is then used as the Input to trigger the clientside callback.
I’m sure there is a better way, but it seems to work:

"""
This is a minimal example of changing themes with the ThemeChangerAIO component and 
accessing the theme colors numbers in a callback.
Note - this requires dash-bootstrap-components>=1.0.0 and dash>=2.0 dash-bootstrap-templates>=1.0.4
"""

from dash import Dash, dcc, html, Input, Output, State
import plotly.express as px
import dash_bootstrap_components as dbc
from dash_bootstrap_templates import ThemeChangerAIO, template_from_url

df = px.data.gapminder()

dbc_css = (
    "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.2/dbc.min.css"
)
app = Dash(__name__, external_stylesheets=[dbc.themes.FLATLY, dbc_css])

header = html.H4(
    "ThemeChangerAIO Demo", className="bg-primary text-white p-4 mb-2 text-center"
)
buttons = html.Div(
    [
        dbc.Button("Primary", color="primary"),
        dbc.Button("Secondary", color="secondary"),
        dbc.Button("Success", color="success"),
        dbc.Button("Warning", color="warning"),
        dbc.Button("Danger", color="danger"),
        dbc.Button("Info", color="info"),
        dbc.Button("Light", color="light"),
        dbc.Button("Dark", color="dark"),
        dbc.Button("Link", color="link"),
    ],
    className="m-4",
)

graph = html.Div(dcc.Graph(id="graph"), className="m-4")
stores = html.Div(
    [dcc.Store(id="intermediate-store", data={}), dcc.Store(id="color-store", data={})]
)

app.layout = dbc.Container(
    [
        header,
        dbc.Row(
            [
                dbc.Col(
                    ThemeChangerAIO(
                        aio_id="theme", radio_props={"value": dbc.themes.FLATLY}
                    ),
                    width=2,
                ),
                dbc.Col([buttons, graph], width=10),
            ],
        ),
        dbc.Row(dbc.Col([html.Div(id="output"), stores])),
    ],
    className="m-4 dbc",
    fluid=True,
)


@app.callback(
    Output("graph", "figure"),
    Output("intermediate-store", "data"),
    Input(ThemeChangerAIO.ids.radio("theme"), "value"),
)
def update_graph_theme(theme):
    template = template_from_url(theme)
    graph = px.scatter(
        df.query("year==2007"),
        x="gdpPercap",
        y="lifeExp",
        size="pop",
        color="continent",
        log_x=True,
        size_max=60,
        template=template,
        title="Gapminder 2007: '%s' theme" % template,
    )
    return graph, "theme"


app.clientside_callback(
    """
    function(val) {
        const bodyStyles = window.getComputedStyle(document.body);
        return {
            primary: bodyStyles.getPropertyValue("--bs-primary"),
            secondary: bodyStyles.getPropertyValue("--bs-secondary"),
            success: bodyStyles.getPropertyValue("--bs-success"),
        };
    }
    """,
    Output("color-store", "data"),
    Input("intermediate-store", "data"),
)


@app.callback(Output("output", "children"), Input("color-store", "data"))
def update_output_div(colors):
    return f"colors: {colors}"


if __name__ == "__main__":
    app.run_server(debug=True)


@AnnMarieW thank you!