/** * THE ONE PLACE THE APP'S PAINT ORDER IS WRITTEN DOWN. * * Every overlay in this app portals into the SAME container (`#akashic-overlay-root`, see * `getAkashicPortalContainer.ts`), so every one of them is a SIBLING of every other. Siblings are ordered * by `z-index` first and DOM order only as a tiebreak - which means the relationship between two surfaces * is decided by two numbers written in two different files, and nothing makes those files agree. * * The defect this module exists to prevent * It already happened once. `FloatingWindow` took `z-index: 1000` after verifying that * `z-50` was the highest value anything else in the app used - correct, and it cleared the app CHROME as * intended. Nobody asked the INVERSE question: whether a MODAL must clear the WINDOW. `ui/dialog` and * `ui/alert-dialog` sat at `z-50`, so a confirmation dialog opened from INSIDE the meeting window (Delete * on a note, via `PatientNotes`) mounted BEHIND it. Radix's AlertDialog is modal, so it blocked pointer * events everywhere outside its own - invisible - content: a frozen meeting window and a dead Leave button * in the middle of a live, billed call. * * A magic number in each file is what let that happen. The relationship is stated ONCE, here, and pinned by * `src/components/ui/__tests__/stackingOrder.test.tsx`, which reads the numbers off the RENDERED elements * rather than off these constants - so a primitive that stops importing them fails too. * * The order, and why each primitive sits where it does * Z_APP_CHROME (50) - banners and sticky page furniture: `ActingAsBanner`, `AvailableMeetingBanner`, * `CallOverlay`'s failure notice, `ExportLegalRecordDialog`'s full-screen shell. * BELOW a floating window on purpose: a live, billed meeting must not be painted * over by a banner. These are plain `z-50` at their call sites and stay there - * this constant is the documented FLOOR, not something they import. * * Z_FLOATING_WINDOW (1000) - `ui/floating-window/FloatingWindow`. Above all chrome; below everything a * user can open FROM inside it. Instances raise themselves above each other from * this floor (click-to-front), which is why the layers above leave headroom. * * Z_MODAL (1100) - `ui/dialog`, `ui/alert-dialog`, `ui/sheet` (overlay AND content). * ABOVE a floating window. All three are Radix modal surfaces: they trap focus * and block pointer events outside themselves whether or not they are visible. * A modal that renders under a floating window is therefore never merely * "hidden" - it deadlocks the window it was opened from. `sheet` is literally * `@radix-ui/react-dialog` with a different animation, so splitting it from the * other two would be a distinction with no mechanism behind it. * * Z_POPOVER (1200) - `ui/dropdown-menu`, `ui/select` (content). * ABOVE modals, and therefore above floating windows too. A menu or a select * list is opened FROM a trigger, and that trigger can live inside a dialog OR * inside a floating window - so a popover has to clear both or it renders behind * the very surface that owns its trigger. It was already effectively above * modals (equal `z-50`, and its portal is appended later, so DOM order won); * this makes that ordering explicit instead of incidental. * * THE TRADE-OFF THIS ACCEPTS, STATED: a modal or popover opened from the page BEHIND a floating window * now paints OVER that window. That is the right way round. Radix modals already block interaction * app-wide while open, so the alternative is not "the window stays usable" - it is "the window is frozen by * something the user cannot see", which is precisely the shipped defect above. * * Why the class strings are literals * Tailwind's JIT scans source TEXT. A template-built class (`` `z-[${Z_MODAL}]` ``) generates NO CSS - * the utility simply would not exist and the element would fall back to `z-auto`. So the arbitrary-value * classes below are written out literally, in a file the `content` glob covers * (`./src/**\/*.{js,ts,jsx,tsx}`), and a test pins each string against its number so the pair cannot drift. */ /** Banners and sticky page furniture. The documented FLOOR - call sites write plain `z-50`. */ export const Z_APP_CHROME = 50; /** `FloatingWindow`'s base. Instances raise from here; see `takeTopZIndex` in `FloatingWindow.tsx`. */ export const Z_FLOATING_WINDOW = 1000; /** `ui/dialog`, `ui/alert-dialog`, `ui/sheet` - overlay and content alike. */ export const Z_MODAL = 1100; /** `ui/dropdown-menu`, `ui/select` - content. */ export const Z_POPOVER = 1200; /** A dialog opened from inside a popover (the call's roster asking before a removal): above its opener. */ export const Z_POPOVER_DIALOG = 1300; /** Literal on purpose - see the JIT note in this module's header. Pinned against `Z_MODAL` by test. */ export const Z_MODAL_CLASS = 'z-[1100]'; /** Literal on purpose - see the JIT note in this module's header. Pinned against `Z_POPOVER` by test. */ export const Z_POPOVER_CLASS = 'z-[1200]'; /** Literal on purpose - see the JIT note in this module's header. Pinned against `Z_POPOVER_DIALOG` by test. */ export const Z_POPOVER_DIALOG_CLASS = 'z-[1300]';