Search code examples
pythoncssplotlyplotly-dash

How do I change the color scheme of a whole page from a dash daq Booleanswitch?


Sorry if I missed something as I'm fairly new to dash and on how callbacks work. So basically I'm working on a dashboard application using dash and python. My ultimate aim is to implement a dark mode option, which will be through the dash daq BooleanSwitch.

My Approach

The way I have implemented this is that I have two dictionaries that represent two color schemes: dark and light, with the exact same keys, but different values for color schemes. I currently am using the light dictionary to manually define the background color, font color, etc. into the styling options for each element inside my app layout. Here's what they look like:

light_theme = {
    'main-background': '#ffe7a6',
    'header-text': '#376e00',
    'sub-text': '#0c5703'
}

dark_theme = {
    'main-background': '#000000',
    'header-text': '#ff7575',
    'sub-text': '#ffd175'
}

I then call the light_theme dict into my main layout code here:

app.layout = html.Div(children=[
    daq.BooleanSwitch(on=False, id='bool-switch-input'),
    html.Div(id='bool-switch-output'),
    html.H1(children="This is a heading text", id="head-txt", style={
        'color': light_theme['header-text']
    }),
    html.H2(children="This is a subtext", id='sub-txt', style={
        'color': light_theme['sub-text']
    }),
], style={
    'backgroundColor': light_theme['main-background']
})

Finally, I use the callback function to get the value from the switch on whether it is on or off:

@app.callback(Output('bool-switch-output', 'children'),
              Input('bool-switch-input', 'on'))
def update_output(on):
    return on

Now what I'm having trouble is making it so that whenever this switch is on (which is dark mode), all the html elements inside the layout use the dark_theme instead of light_theme dictionary. I was under the impression that by simply swapping the variables would be work but I'm not sure if that is possible from the callback function while the app is running. So, is there a better way to do this?

What I have already tried:

  • Making a custom javascript to set onclick property to switch and then accordingly changing the css layout. Unfortunately didn't work because according to the other forums, the js always gets fired first which results in a null value everytime.

  • Using clientside_callbacks, but I'm honestly lost on how to get that working.

If there is a different, better approach to this someone could recommend, I'd appreciate it.


Solution

    • there are 3 html elements that you are styling. Need to ensure they all have an id
    • callback can now return style for each of the html elements with style
    import dash
    from dash import Dash, dcc, html, Input, Output
    from dash.exceptions import PreventUpdate
    import dash_daq as daq
    import plotly.express as px
    import pandas as pd
    from jupyter_dash import JupyterDash
    
    app = JupyterDash(__name__)
    
    light_theme = {
        "main-background": "#ffe7a6",
        "header-text": "#376e00",
        "sub-text": "#0c5703",
    }
    
    dark_theme = {
        "main-background": "#000000",
        "header-text": "#ff7575",
        "sub-text": "#ffd175",
    }
    
    app.layout = html.Div(
        id="parent_div",
        children=[
            daq.BooleanSwitch(on=False, id="bool-switch-input"),
            html.Div(id="bool-switch-output"),
            html.H1(
                children="This is a heading text",
                id="head-txt",
                style={"color": light_theme["header-text"]},
            ),
            html.H2(
                children="This is a subtext",
                id="sub-txt",
                style={"color": light_theme["sub-text"]},
            ),
        ],
        style={"backgroundColor": light_theme["main-background"]},
    )
    
    
    @app.callback(
        [
            Output("parent_div", "style"),
            Output("head-txt", "style"),
            Output("sub-txt", "style"),
        ],
        Input("bool-switch-input", "on"),
    )
    def update_output(on):
        theme = dark_theme if on else light_theme
    
        return (
            {"backgroundColor": theme["main-background"]},
            {"color": theme["header-text"]},
            {"color": theme["sub-text"]},
        )
    
    
    if __name__ == "__main__":
        app.run_server(mode="inline")