Z-Index Stacking#
Blinko has no shared z-index constants file. Values are hardcoded independently across Tailwind utility classes (z-[N]), inline style props, and CSS rules, creating an implicit layering hierarchy that is prone to stacking order bugs when new overlays are introduced .
De Facto Layer Hierarchy#
| Z-Index | Tier | Component(s) |
|---|---|---|
10000 | Image viewer | .PhotoView-Portal in globals.css |
9999 | Fullscreen editor | FullscreenEditor β fixed inset-0 z-[9999], rendered via createPortal |
3000 / -99 | Floating popover | PopoverFloat β animates to 3000 on enter, collapses to -99 on exit |
2002 | Dialog close button | Dialog/Provider and DialogStandalone/Provider |
2001 | Dialog container | Dialog/Provider and DialogStandalone/Provider |
2000 | Dialog / modal backdrop | Dialog/Provider (iOS), DialogStandalone/Provider β overrides HeroUI <Modal> default |
1200 | Camera dialog overlay | CameraDialog black-screen overlay |
1031 | NProgress bar | nprogress.css |
999 | Resizable handle | ResizableWrapper |
50 | Fixed UI chrome | Add button FAB, music player, multi-select toolbar, mobile nav bar |
20 | Expanded card (iOS) | expandContainer |
11 | Mobile header | Layout/index.tsx |
Key Components#
FullscreenEditor (z-[9999])#
FullscreenEditor.tsx renders a full-viewport div at z-[9999] via createPortal to document.body . It explicitly checks for a .PhotoView-Portal element to yield ESC key handling to the image viewer above it .
Dialog & DialogStandalone Providers (z-2000β2002)#
Two near-identical providers β Dialog/Provider.tsx and DialogStandalone/Provider.tsx β both override HeroUI's <Modal> with style={{ zIndex: 2000 }}. On iOS they bypass HeroUI entirely, using plain div overlays at z-[2000], a motion container at z-[2001], and a close button at z-[2002] .
PopoverFloat (z-3000 / z--99)#
PopoverFloat/index.tsx uses Framer Motion to animate between zIndex: 3000 (visible) and zIndex: -99 (hidden). At 3000 it sits above dialogs β intentional for tag/reference dropdowns inside modals β but can conflict if open while the fullscreen editor is launching .
Known Bugs & Root Causes#
Reference modal trapped behind FullscreenEditor#
Opening a note in fullscreen and then clicking the Reference button yields no visible modal . The root cause is a stacking context mismatch:
FullscreenEditorsetsz-index: 9999on its portal root.- HeroUI's
<Modal>portal wrapper defaults toz-index: 50on its outermostdiv; even though the inner<section>targetsz-index: 2000, the entire stacking context is capped at50β placing the modal beneath the fullscreen overlay .
The issue reporter also noted an intermittent dnd-kit crash (TypeError: Cannot use 'in' operator to search for 'x' in undefined) thrown during modal mounting, caused by a missing null check in dnd-kit's coordinate delta helper .
Suggested fixes (from the issue):
- Lower
FullscreenEditortoz-40so it stays below HeroUI'sz-50modal wrapper. - Or raise the HeroUI modal portal container above
9999(e.g.,z-[10000]).
Root Structural Problem: No Shared Constants#
There is no zIndex constants file . Consequences:
- The same logical levels (
2000,2001,2002) are duplicated independently in bothDialog/Provider.tsxandDialogStandalone/Provider.tsx. - HeroUI
<Modal>has its own internal stacking; thestyle={{ zIndex: 2000 }}override is a workaround rather than a contract. - Any third-party component defaulting to a value between
50and2000would silently break the layering. - Adding a new overlay requires manually auditing all existing values scattered across CSS files, Tailwind classes, and inline styles.
Source Files at a Glance#
| File | Role |
|---|---|
FullscreenEditor.tsx | z-[9999] full-screen overlay |
Dialog/Provider.tsx | Dialog modal, z-2000β2002 |
DialogStandalone/Provider.tsx | Standalone dialog, same z-2000β2002 |
PopoverFloat/index.tsx | Tag/reference popover, z-3000 β z--99 |
globals.css | .PhotoView-Portal { z-index: 10000 } |
nprogress.css | Progress bar at z-1031 |