Hi all,
I have a big interactive dashboard with lots of moving parts and I recently added a dark mode toggle, which toggles a dark mode CSS class using CustomJS. However, switching to dark mode doesn’t work exactly as expected.
Initial load (light mode)
After clicking dark mode toggle, you can see that
- The background changed colors (class now contains “dark-background”)
- The data toggle changed colors (class now contains “dark-button”)
- The dark mode toggle did not change colors (class doesn’t contain “dark-button”)
After clicking another button (data toggle in this example), you can see that
- The data toggle reverted back to the original color
- “dark-button” is no longer in the class list
Some other weird stuff happens if you keep clicking the buttons, but this is the gist of it. I’ve included all of the code for this example below. Let me know if you need any more information. If anyone could help with this I’d really appreciate it.
Folder structure
project /
__main.py
__templates /
____index.html
____styles.css
Python
#!/usr/bin/env python3
import os
import numpy as np
import pandas as pd
from time import time
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Toggle
pd.options.mode.chained_assignment = None
def make_data():
d = {
'x': np.random.random(100),
'y': np.random.random(100)
}
return ColumnDataSource(d)
def make_plot(source):
plot_height = 200
plot_width = 600
color = '#987987'
p = figure(plot_width=plot_width, plot_height=plot_height, css_classes=['plot'], sizing_mode='stretch_both',
name='plot', tools='tap,hover', toolbar_location=None, x_range=(0, 1), y_range=(0, 1))
p.circle('x', 'y', size=20, color=color, source=source)
p.background_fill_color = None
p.border_fill_color = None
return p
def change_data(attr, old, new):
if new:
d.data.update({'y': d.data['y'] / 2})
else:
d.data.update({'y': d.data['y'] * 2})
d = make_data()
p = make_plot(d)
w0 = Toggle(label='data toggle', height=30, css_classes=['toggle'], width=300, width_policy='max', name='toggle_data')
w0.on_change('active', change_data)
w1 = Toggle(label='dark mode toggle', height=30, css_classes=['toggle'], width=300, width_policy='max', name='toggle_color')
def change_colors_js():
return CustomJS(args=dict(tgl=w1), code="""
console.log(tgl.active);
// body
var el = document.body;
el.classList.toggle("dark-background");
// spinners
var els = document.getElementsByClassName("bk-btn");
for (el of els) {
el.classList.toggle("dark-button");
}
""")
w1.js_on_change('active', change_colors_js())
curdoc().add_root(w0)
curdoc().add_root(w1)
curdoc().add_root(p)
curdoc().title = 'Test'
HTML
{% from macros import embed %}
<!DOCTYPE html>
<html>
<head>
<title>Waze Live Dashboard</title>
<meta charset="UTF-8" />
{{ bokeh_css }}
{{ bokeh_js }}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato|Montserrat|Raleway|Oswald|Open+Sans"/>
<style type="text/css"> {% include 'styles.css' %} </style>
</head>
<body>
<div id="container">
<div id="button-holder" class="row">
<div class="button">
{{ embed(roots.toggle_data) }}
</div>
<div class="button">
{{ embed(roots.toggle_color) }}
</div>
</div>
<div id="plot-holder">
{{ embed(roots.plot) }}
</div>
</div>
</body>
<!-- PLOT SCRIPT -->
{{ plot_script }}
</html>
CSS
/* GENERAL */
.row {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
}
html, body {
margin: 0;
padding: 0;
overflow: hidden;
}
#container {
max-height: 100vh;
min-height: 100vh;
max-width: 100vw;
min-width: 100vw;
}
#button-holder {
max-width: 100vw;
min-width: 100vw;
}
#plot-holder {
max-height: calc(100vh - 40px);
min-height: calc(100vh - 40px);
max-width: 100vw;
min-width: 100vw;
}
.button {
max-width: 49.5vw;
min-width: 49.5vw;
}
/* DARK MODE */
.dark-background {
background: #262626;
color: #fff;
}
.dark-button {
background: #404040!important;
color: #fff!important;
border-color: #000!important;
}