Hello all! Im facing an issue where two drawers that are being opened concurrently have fixed orders, where one is always layered on top of the other, regardless of the order in which they were opened. I tried to write a callback that would manually reassign zIndex values to the drawers on click, but it flat out refuses to work. Any advice as to how I could make the most recently clicked drawer sit on top of the earlier called drawer would be appreciated!
Here is the screenshot of overlapping drawers, where no matter what order I press the buttons in, the event log drawer is in the front.
Here are my versions: dash version: 3.1.1, dcc version: 3.1.0, html version: 3.0.3, dash table version: 6.0.3
import dash
from dash import State, callback, dcc, html, Dash, Input, Output, dash_table
from dash_iconify import DashIconify
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
import dash_mantine_components as dmc
from plotly.graph_objs import Figure
from plotly.subplots import make_subplots
import plotly.graph_objs as go
import plotly.io as pio
app = Dash(__name__, suppress_callback_exceptions=True)
app.layout = dmc.MantineProvider(
html.Div([
html.Div([
html.Button(
id="warning-button",
className="tdesign--error-triangle", # Default icon
n_clicks=0, # Needed for callback
style={
"position": "fixed",
"top": "10px",
"left": "10px",
"zIndex": 9999,
"backgroundColor": "black",
"padding": "10px",
"borderRadius": "5px",
"border": "white",
"boxShadow": "0 0 10px rgba(0,0,0,0.3)",
"width": "15vh",
"height": "15vh",
"cursor": "pointer"
}
)
], id="button-wrap", hidden=False),
html.Button(
id="event-button",
className="mingcute--paper-line",
n_clicks=0, # Needed for callback
style={
"position": "fixed",
"bottom": "0px",
"left": "0px",
"zIndex": 9999,
"backgroundColor": "black",
"padding": "10px",
"borderRadius": "5px",
"border": "white",
"boxShadow": "0 0 10px rgba(0,0,0,0.3)",
"width": "7.5vh",
"height": "7.5vh",
"cursor": "pointer"
}
),
dmc.Drawer( # this thing is to show all the error info.
title="Error Page",
id="error-page",
padding="md",
opened=False,
size="50%",
position="bottom",
# style={"position": "fixed"},
children=[
html.Div(id="error_check"),
]
),
dmc.Drawer( # this to display the event log
title="Event Log",
id="event-page",
padding="md",
opened=False,
size="30%",
position="right",
# style={"position": "fixed"},
children=[
html.Div(id="event_table"),
]
),
dcc.Interval(
id="tab-interval",
interval=1 * 60 * 1000, # 5 minutes
n_intervals=0,
),
dcc.Interval(
id="flash-interval",
interval=0.2 * 1000,
n_intervals=0,
)
])
)
app.clientside_callback( #asks for window width every interval
"""
function(n_intervals) {
if(typeof window !== 'undefined' && window.innerWidth) {
return window.innerWidth;
} else {
return 1000; // fallback width
}
}
""",
Output('window-size', 'data'),
Input('interval-component', 'n_intervals')
)
power_offline = True #so that the error button shows up
#ERROR BUTTON APPEARANCE
@callback(
Output("button-wrap", "hidden"),
Input("flash-interval", "n_intervals"),
prevent_initial_call=False
)
def show_error_icon(n_intervals):
print("Offline status check:", power_offline, current_offline, powfact_offline)
return False
#return not (power_offline or current_offline or powfact_offline)
#ERROR PAGE (DRAWER)
@callback(
Output("error-page", "opened"),
Input("warning-button", "n_clicks"),
State("error-page", "opened"),
prevent_initial_call=True
)
def toggle_drawer(n_clicks, is_open):
if n_clicks is None:
raise dash.exceptions.PreventUpdate
return not is_open
#updates the error info that needs to be shown
POWmissing_boards = ["Waiting..."]
CURmissing_boards = ["Waiting..."]
PFmissing_boards = ["Waiting..."]
power_offline = False
current_offline = False
powfact_offline = False
danger_current = ["No dangerous level of current"]
@callback(
Output("error_check", "children"),
Input("error-page", "opened"),
prevent_initial_call=True
)
def update_drawer_content(is_open):
if not is_open:
raise dash.exceptions.PreventUpdate
return html.Div([
html.H4("System Error Information:"),
html.P(f"Power Data Offline for Boards {POWmissing_boards}"),
html.P(f"Current Data Offline for Boards {CURmissing_boards}"),
html.P(f"Power Factor Data Offline for Boards {PFmissing_boards}"),
html.P(f"The current level is {danger_current} amps"),
])
#EVENT LOG (Drawer) switch
@callback(
Output("event-page", "opened"),
Input("event-button", "n_clicks"),
State("event-page", "opened"),
prevent_initial_call=True
)
def toggle_log(n_clicks, is_open):
if n_clicks is None:
raise dash.exceptions.PreventUpdate
return not is_open
def generate_logs(data, columns):
return html.Table([
html.Thead(
html.Tr([html.Th(col) for col in columns])
),
html.Tbody([
html.Tr([
html.Td(row[col]) for col in columns
]) for row in data
])
], style={'width': '100%', 'border': '1px solid black', 'borderCollapse': 'collapse', 'padding': '8px'})
@callback(
Output("event_table", "children"),
Input("event-page", "opened"),
prevent_initial_call=True
)
def update_log_content(is_open):
if not is_open:
raise dash.exceptions.PreventUpdate
data = [
{'Board': 'Board 1', 'Status': 'Offline', 'Last Update': '2025-07-17 10:00'},
{'Board': 'Board 2', 'Status': 'Online', 'Last Update': '2025-07-17 10:05'},
{'Board': 'Board 3', 'Status': 'Offline', 'Last Update': '2025-07-17 09:50'},
]
columns = ['Board', 'Status', 'Last Update']
return html.Div([
html.H4("System Error Information:"),
generate_logs(data, columns),
# You can add more info below if you want
])
#table with the timestamp, an ID of what happened, and what that ID means (abnormal value, returns value, null, 0,)
#MAK THE MOST RECENT CLICKED DRAWER SHOW UP IN FRONT
@callback(
Output("error-page", "style"),
Output("event-page", "style"),
Input("error-page", "opened"),
Input("event-page", "opened"),
prevent_initial_call=True
)
def prioritize_drawers(error_open, event_open):
if error_open and not event_open:
return {"zIndex": 1001}, {"zIndex": 1000}
elif event_open and not error_open:
return {"zIndex": 1000}, {"zIndex": 1001}
elif error_open and event_open:
return {"zIndex": 1000}, {"zIndex": 1001} # Or swap depending on desired stacking
return {"zIndex": 1000}, {"zIndex": 1000}
if __name__ == '__main__':
print("dash version:", dash.__version__), print("dcc version:", dcc.__version__), print("html version:", html.__version__), print("dash table version:", dash_table.__version__),
app.run(port=8055, debug=True, use_reloader=False)
And while I doubt it has any effect on the buttons, just in case, here’s the css file for icons.
.tdesign--error-triangle {
display: flex;
align-items: center;
justify-content: center;
/* width: 40px;
height: 40px; */
background-repeat: no-repeat;
background-size: 80% 80%; /* can be % or pixels */
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fcf000' d='m12 1l11.951 20.7H.05zM3.513 19.7h16.974L12 5zM13 9.5V15h-2V9.5zm-2 7h2.004v2.004H11z' stroke-width='0.5' stroke='%23ff0909'/%3E%3C/svg%3E");
}
.tdesign--error-triangle-FLASH {
display: flex;
align-items: center;
justify-content: center;
/* width: 40px;
height: 40px; */
background-repeat: no-repeat;
background-size: 80% 80%; /* can be % or pixels */
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23ff0909' d='m12 1l11.951 20.7H.05zM3.513 19.7h16.974L12 5zM13 9.5V15h-2V9.5zm-2 7h2.004v2.004H11z' stroke-width='0.5' stroke='%23fcf000'/%3E%3C/svg%3E");
}
.mingcute--paper-line {
display: flex;
align-items: center;
justify-content: center;
/* width: 40px;
height: 40px; */
background-repeat: no-repeat;
background-size: 80% 80%; /* can be % or pixels */
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none'%3E%3Cpath d='m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z'/%3E%3Cpath fill='%23e6feff' d='M16 3a3 3 0 0 1 2.995 2.824L19 6v10h.75c.647 0 1.18.492 1.244 1.122l.006.128V19a3 3 0 0 1-2.824 2.995L18 22H8a3 3 0 0 1-2.995-2.824L5 19V9H3.25a1.25 1.25 0 0 1-1.244-1.122L2 7.75V6a3 3 0 0 1 2.824-2.995L5 3zm0 2H7v14a1 1 0 1 0 2 0v-1.75c0-.69.56-1.25 1.25-1.25H17V6a1 1 0 0 0-1-1m3 13h-8v1c0 .35-.06.687-.17 1H18a1 1 0 0 0 1-1zm-7-6a1 1 0 1 1 0 2h-2a1 1 0 1 1 0-2zm2-4a1 1 0 1 1 0 2h-4a1 1 0 0 1 0-2zM5 5a1 1 0 0 0-.993.883L4 6v1h1z' stroke-width='0.5' stroke='%2300fff6'/%3E%3C/g%3E%3C/svg%3E");
}