✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🏦 Standard & Poor's chooses Dash Enterprise for ESG analysis. Learn why, sign up for the June 23 Webinar here!

Updating external_stylesheets via callback?

So usually you would do something like:

external_stylesheets = [‘https://codepen.io/chriddyp/pen/bWLwgP.css’]

app = dash.Dash(name, external_stylesheets=external_stylesheets)

Suppose I’d like to give users the option to pick a theme that updates the external_stylesheets, for example between dash bootstrap components dbc.themes.LUX and dbc.themes.YETI (but not both). So I’d like to be able to replace the external_stylesheets using radio item callbacks, is this possible?

1 Like

Has anyone found a solution for this? I also want to toggle between dbc themes in my dash app.

Take a look at cool theme explorer by @AnnMarieW,

1 Like

Hey @atharvakatre

Here is a minimal example that shows the clientside callback used to switch the external stylesheets.
And thanks to @Emil who helped improve it - He had the great idea of using two stylsheets with a delay to help reduce the flicker when the stylesheets change.

"""
This is a minimal example of the theme switcher clientside callback.
See the full app at https://hellodash.pythonanywhere.com/

"""


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

import dash_bootstrap_components as dbc

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


dbc_themes_url = {
    "BOOTSTRAP": dbc.themes.BOOTSTRAP,
    "CERULEAN": dbc.themes.CERULEAN,
    "COSMO": dbc.themes.COSMO,
    "FLATLY": dbc.themes.FLATLY,
    "JOURNAL": dbc.themes.JOURNAL,
    "LITERA": dbc.themes.LITERA,
    "LUMEN": dbc.themes.LUMEN,
    "LUX": dbc.themes.LUX,
    "MATERIA": dbc.themes.MATERIA,
    "MINTY": dbc.themes.MINTY,
    "PULSE": dbc.themes.PULSE,
    "SANDSTONE": dbc.themes.SANDSTONE,
    "SIMPLEX": dbc.themes.SIMPLEX,
    "SKETCHY": dbc.themes.SKETCHY,
    "SPACELAB": dbc.themes.SPACELAB,
    "UNITED": dbc.themes.UNITED,
    "YETI": dbc.themes.YETI,
    "CYBORG": dbc.themes.CYBORG,
    "DARKLY": dbc.themes.DARKLY,
    "SLATE": dbc.themes.SLATE,
    "SOLAR": dbc.themes.SOLAR,
    "SUPERHERO": dbc.themes.SUPERHERO,
}

dropdown = dcc.Dropdown(
    id="themes",
    options=[{"label": str(i), "value": dbc_themes_url[i]} for i in dbc_themes_url],
    value=dbc_themes_url["BOOTSTRAP"],
    clearable=False,
)

buttons = html.Div(
    [
        dbc.Button("Primary", color="primary", className="mr-1"),
        dbc.Button("Secondary", color="secondary", className="mr-1"),
        dbc.Button("Success", color="success", className="mr-1"),
        dbc.Button("Warning", color="warning", className="mr-1"),
        dbc.Button("Danger", color="danger", className="mr-1"),
        dbc.Button("Info", color="info", className="mr-1"),
        dbc.Button("Light", color="light", className="mr-1"),
        dbc.Button("Dark", color="dark", className="mr-1"),
        dbc.Button("Link", color="link"),
    ],className='m-4'
)

alerts = html.Div(
    [
        dbc.Alert("This is a primary alert", color="primary"),
        dbc.Alert("This is a secondary alert", color="secondary"),
        dbc.Alert("This is a success alert! Well done!", color="success"),
        dbc.Alert("This is a warning alert... be careful...", color="warning"),
    ]
)

"""
===============================================================================
Layout
"""
app.layout = dbc.Container(
    dbc.Row(
        [
            dbc.Col(["Select Theme", dropdown], width=3),
            dbc.Col([buttons, alerts]),
            html.Div(id="blank_output"),
        ],
    ),
    className="m-4",
    fluid=True,
)


# Using 2 stylesheets with the delay reduces the annoying flicker when the theme changes
app.clientside_callback(
    """
    function(url) {
        // Select the FIRST stylesheet only.
        var stylesheets = document.querySelectorAll('link[rel=stylesheet][href^="https://stackpath"]')
        // Update the url of the main stylesheet.
        stylesheets[stylesheets.length - 1].href = url
        // Delay update of the url of the buffer stylesheet.
        setTimeout(function() {stylesheets[0].href = url;}, 100);
    }
    """,
    Output("blank_output", "children"),
    Input("themes", "value"),
)


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

@AnnMarieW @Emil Thank you guys!! This is exactly what I was looking for :slightly_smiling_face:

2 Likes

@AnnMarieW sorry to bother you again but is it possible to replace the dropdown for themes with a boolean toggle switch? Like I want to have to a light and a dark theme in my app and instead of having a dropdown or a radio button for both light and dark options I only want to have a single toggle component for the dark theme. But not sure how the values can manipulate the themes as the dbc checklist with a switch returns a True or False value.

Hi @atharvakatre

I’m happy to help :slight_smile:

Actually using a boolean switch is a slightly different process. In the clientside callback you have to use the actual URL for the theme instead of what the dropdown uses - the nice shortcut provided by the dbc library: dbc.themes.MINTY. An easy way to find the URL is with print(dbc.themes.MINTY) (or whatever theme you want to use)

Note that the dbc.Checklist with switch=True is not actually a boolean. It returns either [] or [value], where value is the value as defined in options. In this example, it will return either [] or [1]

options=[{"label": "dark theme", "value": 1}],

If you are using an actual boolean like the dbc.Checkbox, you just need to change the callback slightly. Let me know if you would like to see that example instead.

Here is the example with a single dbc.Checklist and it switches between Minty and Cyborg:


import dash
import dash_html_components as html
from dash.dependencies import Input, Output

import dash_bootstrap_components as dbc

app = dash.Dash(external_stylesheets=[dbc.themes.CYBORG, dbc.themes.MINTY])

# Use this to find the url to use in the clientside callback
print(dbc.themes.CYBORG)
print(dbc.themes.MINTY)


switch = dbc.FormGroup(
    [
        dbc.Label("Select theme"),
        dbc.Checklist(
            options=[{"label": "dark theme", "value": 1},],
            value=[],
            id="switch",
            switch=True,
        ),
    ]
)

buttons = html.Div(
    [
        dbc.Button("Primary", color="primary", className="mr-1"),
        dbc.Button("Secondary", color="secondary", className="mr-1"),
        dbc.Button("Success", color="success", className="mr-1"),
        dbc.Button("Warning", color="warning", className="mr-1"),
        dbc.Button("Danger", color="danger", className="mr-1"),
        dbc.Button("Info", color="info", className="mr-1"),
        dbc.Button("Light", color="light", className="mr-1"),
        dbc.Button("Dark", color="dark", className="mr-1"),
        dbc.Button("Link", color="link"),
    ],
    className="m-4",
)

alerts = html.Div(
    [
        dbc.Alert("This is a primary alert", color="primary"),
        dbc.Alert("This is a secondary alert", color="secondary"),
        dbc.Alert("This is a success alert! Well done!", color="success"),
        dbc.Alert("This is a warning alert... be careful...", color="warning"),
    ]
)

"""
===============================================================================
Layout
"""
app.layout = dbc.Container(
    dbc.Row(
        [
            dbc.Col(switch, width=3),
            dbc.Col([buttons, alerts]),
            html.Div(id="blank_output"),
        ],
    ),
    className="m-4",
    fluid=True,
)


# Using 2 stylesheets with the delay reduces the annoying flicker when the theme changes
app.clientside_callback(
    """
    function(selected) {

        let url = "https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/minty/bootstrap.min.css"
        if (selected.length > 0) {
            url= "https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/cyborg/bootstrap.min.css"
        }

        // Select the theme stylesheets .
        var stylesheets = document.querySelectorAll('link[rel=stylesheet][href^="https://stackpath"]')
        // Update the url of the main stylesheet.
        stylesheets[stylesheets.length - 1].href = url
        // Delay update of the url of the buffer stylesheet.
        setTimeout(function() {stylesheets[0].href = url;}, 100);
    }
    """,
    Output("blank_output", "children"),
    Input("switch", "value"),
)

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

@AnnMarieW Thank you, yes I was referring to the dbc.Checlist your example seems perfect for my use case. Looks like it was a pretty easy logic my JS skills are rusty :sweat_smile: