Bug: Navbar (z-50) overlaps AppHeader Export PES button and ExportDialog backdrop intercepts inner Export clicks — full GUI export flow is broken #53

Closed
opened 2026-09-01 17:05:55 +00:00 by Mark · 1 comment
Owner

Summary

Two distinct z-index / pointer-events issues in AppHeader.tsx and ExportDialog.tsx prevent users from actually exporting a design through the GUI:

  1. Navbar (sticky z-50) overlaps AppHeader Export PES button at the same z-index. Click on Export PES in the workspace header is intercepted by the navbar's "Open Workspace" CTA when the user has scrolled — in some viewports the navbar CTA sits over the AppHeader button. Playwright detects this as <a ... navbar ...> subtree intercepts pointer events.
  2. ExportDialog.tsx:113-117 — backdrop <div absolute inset-0> blocks clicks on inner "Export" button. The backdrop renders as a sibling of the panel and is on top of it; pointer events on its area never reach the Export button below it. Force-click is the only way through, and even then most clicks land on the backdrop (which calls onClose and dismisses the dialog).

This means clicking "Export PES" inside the dialog closes the dialog instead of running the export, on a fresh human click in the central portion of the dialog.

Steps to Reproduce

  1. Visit http://127.0.0.1:5173/workspace
  2. Upload any image (PNG/JPG/SVG), wait for Auto-Digitize to complete
  3. Click "Export PES" in the top-right AppHeader
  4. Click the big "Export" button inside the dialog

Expected

  • A .pes file downloads.
  • The Export button is the topmost interactive element inside the dialog.

Actual

First attempt: navbar CTA intercepts and stops click on Export PES.
Second attempt (after dispaching click directly): dialog opens; clicking the inner Export button dismisses the dialog instead of exporting, because the backdrop <div className="absolute inset-0"> is rendered as a sibling on top of the panel and intercepts the click.

The full Playwright tracebacks are:

- <a href="/workspace" data-discover="true" code-path="src/components/Navbar.tsx:68:11" ...>Open Workspace</a>
  from <nav code-path="src/components/Navbar.tsx:25:5" class="sticky top-0 z-50 h-16">
  subtree intercepts pointer events

and:

- <div class="absolute inset-0" code-path="src/components/workspace/ExportDialog.tsx:113:11"></div>
  from <div class="fixed inset-0 flex items-center justify-center" code-path="src/components/workspace/ExportDialog.tsx:104:9">
  subtree intercepts pointer events

Suggested Fix

For the navbar/AppHeader overlap:

  • One of the two z-50 layers is wrong. AppHeader at z-50 is inside the workspace content; the Navbar sticky is the global nav. Either reduce AppHeader's z to less than 50 (e.g. z-30), or make AppHeader non-sticky, or add pointer-events: none to the navbar at idle state.

For the ExportDialog backdrop:

  • Add onClick only to the backdrop and put the panel outside the absolute/inset wrapper, OR give the backdrop pointerEvents: 'auto' and the panel pointerEvents: 'auto'; z-index: 1; position: relative;. Cleanest fix: split layout so backdrop is parent (fixed inset-0) and the panel uses position: relative; z-index: 1 inside it. Or use Radix Dialog which handles this correctly out of the box.

Severity

High — the GUI export flow does not work for keyboard/mouse users. Only via Playwright force: true could we get past the dialog backdrop, and even then the navbar overlap required a DOM-dispatch workaround. The export path is the primary value of the product.

Shakedown Context

2026-09-01 BB monogram GUI shakedown. Same session that produced #52 (Settings/Help no-op). Repro: Playwright Python sync API against http://127.0.0.1:5173/workspace.

Environment

  • ThreadCraft frontend on localhost:5173
  • easystitch-service backend on localhost:8080
  • HEAD of Kimi_Agent_Brother_10_Digitizing_App repo at run time
## Summary Two distinct z-index / pointer-events issues in `AppHeader.tsx` and `ExportDialog.tsx` prevent users from actually exporting a design through the GUI: 1. **Navbar (sticky `z-50`) overlaps AppHeader Export PES button** at the same z-index. Click on Export PES in the workspace header is intercepted by the navbar's "Open Workspace" CTA when the user has scrolled — in some viewports the navbar CTA sits over the AppHeader button. Playwright detects this as `<a ... navbar ...> subtree intercepts pointer events`. 2. **`ExportDialog.tsx:113-117` — backdrop `<div absolute inset-0>` blocks clicks on inner "Export" button.** The backdrop renders as a sibling of the panel and is on top of it; pointer events on its area never reach the Export button below it. Force-click is the only way through, and even then most clicks land on the backdrop (which calls `onClose` and dismisses the dialog). This means clicking "Export PES" inside the dialog *closes the dialog* instead of running the export, on a fresh human click in the central portion of the dialog. ## Steps to Reproduce 1. Visit `http://127.0.0.1:5173/workspace` 2. Upload any image (PNG/JPG/SVG), wait for Auto-Digitize to complete 3. Click "Export PES" in the top-right AppHeader 4. Click the big "Export" button inside the dialog ## Expected - A `.pes` file downloads. - The Export button is the topmost interactive element inside the dialog. ## Actual First attempt: navbar CTA intercepts and stops click on Export PES. Second attempt (after dispaching click directly): dialog opens; clicking the inner Export button dismisses the dialog instead of exporting, because the backdrop `<div className="absolute inset-0">` is rendered as a sibling on top of the panel and intercepts the click. The full Playwright tracebacks are: ``` - <a href="/workspace" data-discover="true" code-path="src/components/Navbar.tsx:68:11" ...>Open Workspace</a> from <nav code-path="src/components/Navbar.tsx:25:5" class="sticky top-0 z-50 h-16"> subtree intercepts pointer events ``` and: ``` - <div class="absolute inset-0" code-path="src/components/workspace/ExportDialog.tsx:113:11"></div> from <div class="fixed inset-0 flex items-center justify-center" code-path="src/components/workspace/ExportDialog.tsx:104:9"> subtree intercepts pointer events ``` ## Suggested Fix For the navbar/AppHeader overlap: - One of the two `z-50` layers is wrong. AppHeader at `z-50` is inside the workspace content; the Navbar sticky is the global nav. Either reduce AppHeader's z to less than 50 (e.g. `z-30`), or make AppHeader non-sticky, or add `pointer-events: none` to the navbar at idle state. For the ExportDialog backdrop: - Add `onClick` only to the backdrop and put the panel *outside* the absolute/inset wrapper, OR give the backdrop `pointerEvents: 'auto'` and the panel `pointerEvents: 'auto'; z-index: 1; position: relative;`. Cleanest fix: split layout so backdrop is parent (`fixed inset-0`) and the panel uses `position: relative; z-index: 1` inside it. Or use Radix Dialog which handles this correctly out of the box. ## Severity **High** — the GUI export flow does not work for keyboard/mouse users. Only via Playwright `force: true` could we get past the dialog backdrop, and even then the navbar overlap required a DOM-dispatch workaround. The export path is the *primary* value of the product. ## Shakedown Context 2026-09-01 BB monogram GUI shakedown. Same session that produced #52 (Settings/Help no-op). Repro: Playwright Python sync API against http://127.0.0.1:5173/workspace. ## Environment - ThreadCraft frontend on `localhost:5173` - easystitch-service backend on `localhost:8080` - HEAD of `Kimi_Agent_Brother_10_Digitizing_App` repo at run time
Mark closed this issue 2026-09-04 14:46:47 +00:00
Author
Owner

Fixed and pushed to main (commits ffe240b + 21c915c).

Bug #1 — Navbar overlap: AppHeader was at z-50 with no explicit position, same as the sticky Navbar. The Navbar's sticky positioning created its own stacking context that won on scroll, intercepting clicks on the Export PES button. Fix: AppHeader.tsx position: relative, zIndex: 60.

Bug #2 — ExportDialog backdrop: The backdrop <div absolute inset-0 onClick={onClose}> was a sibling of the relative-positioned panel. In a flex container the absolute sibling sits on top of the relative sibling without an explicit z-index, so the backdrop intercepted clicks anywhere inside the dialog and dismissed the dialog instead of triggering export. Fix: ExportDialog.tsx zIndex: 1 on the panel.

Regression tests added in ExportDialog.test.tsx:

  • Export button click does not call onClose (panel above backdrop)
  • Backdrop click does call onClose (close-on-backdrop still works)

Note: I dispatched quickfix on this issue earlier; it ran the model probe and kilo, but kilo exited 0 without committing any code changes (likely saw passing tests for the render and called it good). I then made the fixes manually, ran gates (lint clean, 212/212 tests pass), committed, and pushed. The retry counter was reset, so if quickfix re-picks this it will see the fix already on main and skip the model probe.

Fixed and pushed to main (commits `ffe240b` + `21c915c`). **Bug #1 — Navbar overlap:** AppHeader was at `z-50` with no explicit position, same as the sticky Navbar. The Navbar's sticky positioning created its own stacking context that won on scroll, intercepting clicks on the Export PES button. Fix: `AppHeader.tsx` `position: relative, zIndex: 60`. **Bug #2 — ExportDialog backdrop:** The backdrop `<div absolute inset-0 onClick={onClose}>` was a sibling of the relative-positioned panel. In a flex container the absolute sibling sits on top of the relative sibling without an explicit z-index, so the backdrop intercepted clicks anywhere inside the dialog and dismissed the dialog instead of triggering export. Fix: `ExportDialog.tsx` `zIndex: 1` on the panel. Regression tests added in `ExportDialog.test.tsx`: - Export button click does not call `onClose` (panel above backdrop) - Backdrop click does call `onClose` (close-on-backdrop still works) Note: I dispatched quickfix on this issue earlier; it ran the model probe and kilo, but kilo exited 0 without committing any code changes (likely saw passing tests for the render and called it good). I then made the fixes manually, ran gates (lint clean, 212/212 tests pass), committed, and pushed. The retry counter was reset, so if quickfix re-picks this it will see the fix already on main and skip the model probe.
Sign in to join this conversation.
No description provided.