Displaying a dark or light mode plot

I am trying to embed a json plot that could have either “dark” or “light” flavors.

My simplifed example code is:

def dummy_plot(dark_mode: bool) -> Dict[str,Any]:
    
    xs = [1,2,3,4,5,6]
    ys = [3,6,5,4,7,1]
    data = ColumnDataSource( {
        "x": xs,
        "y": ys
    })
    
    
    p = figure(title="Simple Plot Example", x_axis_label="x", y_axis_label="y")
    line = p.line(x="x", y="y", source=data, legend_label="A line!", line_width=2)
    
    if dark_mode:
        theme = DARK_MINIMAL 
    else: 
        theme = LIGHT_MINIMAL
        
    plot_json = json_item(p, target=None, theme=theme)
    plot_string = ujson.dumps(plot_json)
        
    return {
            "bokeh_css": CDN.render_css(),
            "bokeh_js": CDN.render_js(),
            "bokeh_plot": plot_string,
    }

The returned dictionary goes to a jinja template that generates the final web page, but I don’t believe that part is the problem here, since the plot does show up. However, regardless of whether I pass in true or false for dark_mode, the plot background is always white.

What did I miss?

It’s possible there is just a bug. I’d like to investigate directly, but for that I would need a complete Minimal Reproducible Example that can be copy-pasted and then immediately run without changes.

OK, I was trying not to have to create a minimal template and server example code but I can work on this a bit more

I got the webserver code out of the way by just writing two .html files. Both plots look light when I open them with my browser.

import json
from pathlib import Path
from typing import Dict, Any

from bokeh.plotting import figure
from bokeh.embed import json_item
from bokeh.resources import CDN
from bokeh.themes import LIGHT_MINIMAL, DARK_MINIMAL
from bokeh.models import ColumnDataSource


def really_simple_template(title, css, js, plot):

    return f"""
<!DOCTYPE html>
<html lang="en">

<head>
    <link rel="stylesheet" href="https://unpkg.com/mvp.css">

    {css}
    {js}

    <meta charset="utf-8">
    <meta name="description" content="My description">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script>
    function insertPlot() {{
        Bokeh.embed.embed_item({plot}, "a_spot_for_my_plot")
    }}
    window.onload = insertPlot;
    </script>
</head>

<body>
    <header>
	    <h2>{title}</h2>
    </header>
    <main>
	    <div id="a_spot_for_my_plot"></div>
    </main>
    <footer>
    </footer>
</body>
</html>
    """

def dummy_plot(dark_mode: bool) -> Dict[str, Any]:

    xs = [1, 2, 3, 4, 5, 6]
    ys = [3, 6, 5, 4, 7, 1]
    data = ColumnDataSource({"x": xs, "y": ys})

    p = figure(title="Simple Plot Example", x_axis_label="x", y_axis_label="y")
    line = p.line(x="x", y="y", source=data, legend_label="A line!", line_width=2)

    if dark_mode:
        theme = DARK_MINIMAL
    else:
        theme = LIGHT_MINIMAL

    plot_json = json_item(p, target=None, theme=theme)
    plot_string = json.dumps(plot_json)

    return {
        "bokeh_css": CDN.render_css(),
        "bokeh_js": CDN.render_js(),
        "bokeh_plot": plot_string,
    }

def main():
    dark = dummy_plot(True)
    dark_html = really_simple_template(
        "Dark plot", dark["bokeh_css"], dark["bokeh_js"], dark["bokeh_plot"]
    )
    Path("dark.html").write_text(dark_html)

    light = dummy_plot(False)
    light_html = really_simple_template(
        "Light plot", light["bokeh_css"], light["bokeh_js"], light["bokeh_plot"]
    )
    Path("light.html").write_text(light_html)


if __name__ == "__main__":
    main()

Themes are working with other output methods, so I guess there must simply be a bug specific to the combination of json_item and themes. Please submit a bug report with all this information as a GitHub Issue.

In the mean, if you require json_item my only suggestion is to apply the relevant property changes programmatically.

“By apply the property changes programmatically” – do you just mean explicity set the color of everything myself?

Yes, if you have lots of properties on multiple objects to update, it would probably make sense to define a small function for that purpose.

I found my bug: DARK_MINIMAL is just the string 'dark_minimal'

To apply the theme, you have to specify built_in_themes[DARK_MINIMAL]

Well, it is intended that the CAP_NAMES values in bokeh.themes be usable most anywhere a theme is expected, so I do still consider it a bug to fix.