Fix transient UI layering over modals
Raise notification toasts and Bootstrap tooltips above normal and legacy modal overlays while keeping confirmation dialogs on top. Remove the duplicate tooltip CSS block and add a focused regression for the layer ordering contract.
Alessandro committed
Jul 8, 2026 at 15:37 UTC
4f80ded5318158b42ac18d2214a3faa9778dda55
5 files changed
+28
-109
tests/test_browser_agent_regressions.py
+23
@@ -969,6 +969,29 @@ def test_surface_buttons_keep_modal_and_canvas_entry_points_separate():
969
assert "/plugins/_desktop/webui/main.html" in surfaces_js
970
971
972
+def test_transient_ui_layers_above_modals_below_confirm_dialogs():
973
+ index_css = (PROJECT_ROOT / "webui" / "index.css").read_text(encoding="utf-8")
974
+ modals_js = (PROJECT_ROOT / "webui" / "js" / "modals.js").read_text(encoding="utf-8")
975
+ modals_css = (PROJECT_ROOT / "webui" / "css" / "modals.css").read_text(encoding="utf-8")
976
+ toast_html = (
977
+ PROJECT_ROOT / "webui" / "components" / "notifications" / "notification-toast-stack.html"
978
+ ).read_text(encoding="utf-8")
979
+
980
+ modal_base_z = int(re.search(r"const baseZIndex = (\d+);", modals_js).group(1))
981
+ legacy_overlay_z = int(re.search(r"\.modal-overlay \{[^}]*z-index: (\d+);", modals_css, re.S).group(1))
982
+ confirm_z = int(re.search(r"\.confirm-dialog-backdrop \{[^}]*z-index: (\d+);", modals_css, re.S).group(1))
983
+ toast_z = int(re.search(r"\.toast-stack-container \{[^}]*z-index: (\d+);", toast_html, re.S).group(1))
984
+ tooltip_z_indexes = [
985
+ int(z)
986
+ for z in re.findall(r"\.tooltip \{[^}]*z-index: (\d+);", index_css, re.S)
987
+ ]
988
+
989
+ assert tooltip_z_indexes
990
+ assert toast_z > max(modal_base_z + 20, legacy_overlay_z)
991
+ assert all(z > toast_z for z in tooltip_z_indexes)
992
+ assert all(z < confirm_z for z in [toast_z, *tooltip_z_indexes])
993
+
994
+
995
def test_browser_tool_does_not_auto_open_canvas_policy_is_documented():
996
prompt = (
997
PROJECT_ROOT / "plugins" / "_browser" / "prompts" / "agent.system.tool.browser.md"
webui/AGENTS.md
+1
@@ -28,6 +28,7 @@
28
- Startup scripts in `index.html` must be local and non-parser-blocking: use ES modules, `defer`, or `async` for every script with `src`.
29
- Rubik (`--font-family-main`) is the default WebUI text and control font; use the code/mono font tokens only for code, logs, paths, and fixed-width data.
30
- Hover, focus, and active border treatments should follow existing neutral border/background patterns; avoid hard-coded blue border highlights unless matching an established specialized surface.
31
+- Keep transient UI affordances such as Bootstrap tooltips and notification toasts above normal and legacy modal layers while confirmation dialogs remain on top.
32
33
## Work Guidance
34
webui/components/notifications/AGENTS.md
+1
@@ -15,6 +15,7 @@
15
16
- Use the notification system for user-facing success, warning, info, and error feedback.
17
- Keep public helper names stable for core and plugin callers.
18
+- Keep the toast stack visible above normal and legacy modal overlays while staying below confirmation dialogs.
19
- Avoid exposing secrets or raw auth payloads in notification text.
20
21
## Work Guidance
webui/components/notifications/notification-toast-stack.html
+1
-1
@@ -58,7 +58,7 @@
58
position: absolute;
59
bottom: 5px; /* Spacing from the bottom of the zero-height container */
60
padding-right: 5px;
61
- z-index: 4000;
61
+ z-index: 8900;
62
display: flex;
63
flex-direction: column-reverse; /* Stack toasts upwards */
64
gap: 8px;
webui/index.css
+2
-108
@@ -189,114 +189,8 @@ body,
189
/* Bootstrap-like tooltips */
190
.tooltip {
191
position: absolute;
192
- /* Above custom modals (base z-index: 3000 in `webui/js/modals.js`) */
193
- z-index: 5000;
194
- display: block;
195
- margin: 0;
196
- font-family: var(--font-family-main);
197
- font-style: normal;
198
- font-weight: 400;
199
- line-height: 1.2;
200
- text-align: left;
201
- text-decoration: none;
202
- text-shadow: none;
203
- text-transform: none;
204
- letter-spacing: normal;
205
- word-break: normal;
206
- white-space: normal;
207
- opacity: 0;
208
- pointer-events: none;
209
- transition: opacity 0.08s ease-in-out;
210
-}
211
-
212
-.tooltip.show {
213
- opacity: 1;
214
-}
215
-
216
-.tooltip .tooltip-inner {
217
- max-width: 240px;
218
- padding: 0.35rem 0.6rem;
219
- color: var(--color-text);
220
- background-color: var(--color-secondary);
221
- border-radius: 0.375rem;
222
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
223
- font-size: 0.75rem;
224
-}
225
-
226
-.tooltip .tooltip-arrow {
227
- position: absolute;
228
- width: 0.8rem;
229
- height: 0.4rem;
230
-}
231
-
232
-.tooltip .tooltip-arrow::before {
233
- content: "";
234
- position: absolute;
235
- border-color: transparent;
236
- border-style: solid;
237
-}
238
-
239
-.bs-tooltip-top .tooltip-arrow {
240
- bottom: 0;
241
- left: 50%;
242
- transform: translateX(-50%);
243
-}
244
-
245
-.bs-tooltip-top .tooltip-arrow::before {
246
- border-width: 0.4rem 0.4rem 0;
247
- border-top-color: var(--color-secondary);
248
- top: 0;
249
-}
250
-
251
-.bs-tooltip-bottom .tooltip-arrow {
252
- top: 0;
253
- left: 50%;
254
- transform: translateX(-50%);
255
-}
256
-
257
-.bs-tooltip-bottom .tooltip-arrow::before {
258
- border-width: 0 0.4rem 0.4rem;
259
- border-bottom-color: var(--color-secondary);
260
- top: 0;
261
-}
262
-
263
-.bs-tooltip-start .tooltip-arrow,
264
-.bs-tooltip-left .tooltip-arrow {
265
- right: 0;
266
- top: 50%;
267
- width: 0.4rem;
268
- height: 0.8rem;
269
- transform: translateY(-50%);
270
-}
271
-
272
-.bs-tooltip-start .tooltip-arrow::before,
273
-.bs-tooltip-left .tooltip-arrow::before {
274
- border-width: 0.4rem 0 0.4rem 0.4rem;
275
- border-left-color: var(--color-secondary);
276
- left: 0;
277
-}
278
-
279
-.bs-tooltip-end .tooltip-arrow,
280
-.bs-tooltip-right .tooltip-arrow {
281
- left: 0;
282
- top: 50%;
283
- width: 0.4rem;
284
- height: 0.8rem;
285
- transform: translateY(-50%);
286
-}
287
-
288
-.bs-tooltip-end .tooltip-arrow::before,
289
-.bs-tooltip-right .tooltip-arrow::before {
290
- border-width: 0.4rem 0.4rem 0.4rem 0;
291
- border-right-color: var(--color-secondary);
292
- right: 0;
293
-}
294
-
295
-/* Bootstrap-like tooltips */
296
-.tooltip {
297
- position: absolute;
298
- /* Above custom modals (base z-index: 3000 in `webui/js/modals.js`) */
299
- z-index: 5000;
192
+ /* Above custom modals and toasts, below confirm dialogs. */
193
+ z-index: 9000;
194
display: block;
195
margin: 0;
196
font-family: var(--font-family-main);