☰Read-only archive⌕
Looking for help with Bokeh?

This is a read-only archive. Ask questions and join the conversation on GitHub.

Visit Community Support on GitHub →
Archived discussions · Captured October 28, 2026

Fixed top bar for widget?

Community Support
RobM
RobM

Hello,
I have a column of plots that are all updated from a slider widget. It would be really handy if the slider was positioned in a fixed top bar so it can be adjusted when viewing plots at the bottom of the page. I think this could be handled by some CSS code e.g. How To Create a Fixed Menu
I’ve seen CSS can’t be used with Bokeh but another option might be using some sort of template. Is this the only way to replicate this type of styling?
Thanks.

⌁   Link to post
p-himik
p-himik

I’ve seen CSS can’t be used with Bokeh

That’s not true, it absolutely can be used. But not to its fullest probably, because Bokeh has an internal layout mechanism.
With that being said, it should absolutely be possible to achieve what you want with CSS.

⌁   Link to post
RobM
RobM

Ah ok, I must have misunterstood what I read (I have no prior experience with html, JS, CSS !)
I’ll give it a go although looking through the source code of my html page is still a bit confusing.

⌁   Link to post
RobM
RobM

Using the style editor in my browser I can add the following to .bk-root .bk-input-group;

position:fixed;
z-index:1;
width:340px;
margin-left: 25px; 

this fixes the slider to the top, however the bokeh tools e.g. scroll zoom, no longer work. I haven’t figured a way have whatever is under the cursor to be the focus of a pointer event regardless of z-index.

Is there a way to set this css style from my python script?

⌁   Link to post
RobM
RobM

ok, I’ve tried pass the updates to the css style using bokeh.resources.Resources but so far with no noticeable change to the page. I thought the code below should work but I may have misunderstood the documentation.

topbar = """
.bk-root .bk-input-group {
    position:fixed;
    z-index:1;
    width:365px;
    height:50px;
    margin-left: 25px; 
}
"""
res = Resources() 
res.css_raw.append(topbar) 
save(layout, resources=res)
⌁   Link to post
RobM
RobM

I have tried another approach using Div

topbar = Div(text = """
<style>
.bk-root .bk-input-group {
      position:fixed;
      z-index:1;
      width:365px;
      height:50px;
      margin-left: 25px; }
</style> """)


save(layout, css_classes = ['topbar'])

but this is also doesn’t work.

Are there any examples showing how to add CSS as strings when saving?
Thanks.

⌁   Link to post
RobM
RobM

I think I have now managed what I was hoping for.
In case anyone else would like implement something similar, this is a simple example:

import numpy as np
from bokeh.layouts import column, row, gridplot
from bokeh.models import CustomJS, Slider, Div
from bokeh.plotting import ColumnDataSource, figure, output_file, show, save
from bokeh.util.browser import view

output_file("slider_top_bar.html", 
            title="slider_top_bar.py example")

template = """
{% from macros import embed %}

<!DOCTYPE html>
<html lang="en">
  {% block head %}
  <head> 
    {% block inner_head %}
      <meta charset="utf-8">
      <title>{% block title %}{{ title | e if title else "Bokeh Plot" }}{% endblock %}</title>
      {% block preamble %}{% endblock %}
      {% block resources %}
        {% block css_resources %}
          {{ bokeh_css | indent(8) if bokeh_css }}
        {% block js_resources %}
          {{ bokeh_js | indent(8) if bokeh_js }}
        {% endblock %}
      {% endblock %}
      {% block postamble %}
      <style>
        .bk-root .bk-input-group {
              position:fixed;
              z-index:1;
              width:365px;
              height:50px;
              margin-left: 25px; }
        .bk-root .bk-toolbar {
              position:fixed;
              z-index:1;
              width:250px;
              height:30px;
              margin-left: 50px;
        }
      </style> 
      {% endblock %}
      {% endblock %}
    {% endblock %}
  </head>
  {% endblock %}
  {% block body %}
  <body>
    {% block inner_body %}
      {% block contents %}
        {% for doc in docs %}
          {{ embed(doc) if doc.elementid }}
          {% for root in doc.roots %}
            {% block root scoped %}
              {{ embed(root) | indent(10) }}
            {% endblock %}
          {% endfor %}
        {% endfor %}
      {% endblock %}
      {{ plot_script | indent(8) }}
    {% endblock %}
  </body>
  {% endblock %}
</html>

"""

x = np.linspace(0, 10, 500)
y = np.sin(x)

source = ColumnDataSource(data=dict(x=x, y=y))

tools = "pan,box_zoom,wheel_zoom,reset,hover"
plot = figure(y_range=(-10, 10), plot_width=400, plot_height=400, tools=tools)

plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

amp_slider = Slider(start=0.1, end=10, value=1, step=.1, title="Amplitude")

callback = CustomJS(args=dict(source=source, amp=amp_slider),
                    code="""
    const data = source.data;
    const A = amp.value;
    const x = data['x']
    const y = data['y']
    for (var i = 0; i < x.length; i++) {
        y[i] = A*Math.sin(x[i]);
    }
    source.change.emit();
""")

amp_slider.js_on_change('value', callback)
spacer = Div(width=400, height=40)
layout = gridplot([[amp_slider], [spacer], [plot]])  #, toolbar_location=None
save(layout, template=template)
view("/slider_top_bar.html")
⌁   Link to post

End of archived discussion · Replies are disabled