diff --git a/readme.hints.md b/readme.hints.md index 4a3d410..f8faf34 100644 --- a/readme.hints.md +++ b/readme.hints.md @@ -513,4 +513,95 @@ Completed comprehensive refactoring to ensure clean, maintainable code with sepa The refactoring follows the principles in instructions.md: - Uses static templates with manual DOM operations - Maintains separated concerns in different classes -- Results in clean, concise, and manageable code \ No newline at end of file +- Results in clean, concise, and manageable code + +## Z-Index Management System (2025-12-24) + +A comprehensive z-index management system has been implemented to fix overlay stacking conflicts: + +### The Problem: +- Modals were hiding dropdown overlays +- Context menus appeared behind modals +- Inconsistent z-index values across components +- No clear hierarchy for overlay stacking + +### The Solution: + +#### 1. Central Z-Index Constants (`00zindex.ts`): +Created a centralized file defining all z-index layers: + +```typescript +export const zIndexLayers = { + // Base layer: Regular content + base: { + content: 'auto', + inputElements: 1, + }, + // Fixed UI elements + fixed: { + appBar: 10, + sideMenu: 10, + mobileNav: 250, + }, + // Overlay backdrops + backdrop: { + dropdown: 1999, + modal: 2999, + contextMenu: 3999, + }, + // Interactive overlays + overlay: { + dropdown: 2000, // Dropdowns and select menus + modal: 3000, // Modal dialogs + contextMenu: 4000, // Context menus and tooltips + toast: 5000, // Toast notifications + }, + // Special cases + modalDropdown: 3500, // Dropdowns inside modals + wysiwygMenus: 4500, // Editor formatting menus +} +``` + +#### 2. Updated Components: +- **dees-modal**: Changed from 2000 to 3000 +- **dees-windowlayer**: Changed from 200-201 to 1999-2000 (used by dropdowns) +- **dees-contextmenu**: Changed from 10000 to 4000 +- **dees-toast**: Changed from 10000 to 5000 +- **wysiwyg menus**: Changed from 10000 to 4500 +- **dees-appui-profiledropdown**: Uses new dropdown z-index (2000) + +#### 3. Stacking Order (bottom to top): +1. Regular page content (auto) +2. Fixed navigation elements (10-250) +3. Dropdown backdrop (1999) +4. Dropdown content (2000) +5. Modal backdrop (2999) +6. Modal content (3000) +7. Context menu (4000) +8. WYSIWYG menus (4500) +9. Toast notifications (5000) + +#### 4. Key Benefits: +- Dropdowns now appear above modals +- Context menus appear above dropdowns and modals +- Toast notifications always appear on top +- Consistent and predictable stacking behavior +- Easy to adjust hierarchy by modifying central constants + +#### 5. Testing: +Created `test-zindex.demo.ts` to verify stacking behavior with: +- Modal containing dropdown +- Context menu on modal +- Toast notifications +- Complex overlay combinations + +### Usage: +Import and use the z-index constants in any component: +```typescript +import { zIndexLayers } from './00zindex.js'; + +// In styles +z-index: ${zIndexLayers.overlay.modal}; +``` + +This system ensures proper stacking order for all overlay components and prevents z-index conflicts. \ No newline at end of file diff --git a/ts_web/elements/00zindex.ts b/ts_web/elements/00zindex.ts new file mode 100644 index 0000000..d357a74 --- /dev/null +++ b/ts_web/elements/00zindex.ts @@ -0,0 +1,59 @@ +/** + * Central z-index management for consistent stacking order + * Higher numbers appear on top of lower numbers + */ + +export const zIndexLayers = { + // Base layer: Regular content + base: { + content: 'auto', + inputElements: 1, + }, + + // Fixed UI elements + fixed: { + appBar: 10, + sideMenu: 10, + mobileNav: 250, + }, + + // Overlay backdrops (semi-transparent backgrounds) + backdrop: { + dropdown: 1999, // Below modals but above fixed elements + modal: 2999, // Below dropdowns on modals + contextMenu: 3999, // Below critical overlays + }, + + // Interactive overlays + overlay: { + dropdown: 2000, // Dropdowns and select menus + modal: 3000, // Modal dialogs + contextMenu: 4000, // Context menus and tooltips + toast: 5000, // Toast notifications (highest priority) + }, + + // Special cases for nested elements + modalDropdown: 3500, // Dropdowns inside modals + wysiwygMenus: 4500, // Editor formatting menus +} as const; + +// Helper function to get z-index value +export function getZIndex(category: keyof typeof zIndexLayers, subcategory?: string): number | string { + const categoryObj = zIndexLayers[category]; + if (typeof categoryObj === 'object' && subcategory) { + return categoryObj[subcategory as keyof typeof categoryObj] || 'auto'; + } + return typeof categoryObj === 'number' ? categoryObj : 'auto'; +} + +// Z-index assignments for components +export const componentZIndex = { + 'dees-modal': zIndexLayers.overlay.modal, + 'dees-windowlayer': zIndexLayers.overlay.dropdown, + 'dees-contextmenu': zIndexLayers.overlay.contextMenu, + 'dees-toast': zIndexLayers.overlay.toast, + 'dees-appui-mainmenu': zIndexLayers.fixed.appBar, + 'dees-mobilenavigation': zIndexLayers.fixed.mobileNav, + 'dees-slash-menu': zIndexLayers.wysiwygMenus, + 'dees-formatting-menu': zIndexLayers.wysiwygMenus, +} as const; \ No newline at end of file diff --git a/ts_web/elements/dees-appui-mainmenu.ts b/ts_web/elements/dees-appui-mainmenu.ts index 280f4d7..9863b84 100644 --- a/ts_web/elements/dees-appui-mainmenu.ts +++ b/ts_web/elements/dees-appui-mainmenu.ts @@ -1,5 +1,6 @@ import * as plugins from './00plugins.js'; import * as interfaces from './interfaces/index.js'; +import { zIndexLayers } from './00zindex.js'; import { DeesElement, @@ -46,7 +47,7 @@ export class DeesAppuiMainmenu extends DeesElement { .mainContainer { --menuSize: 60px; color: ${cssManager.bdTheme('#666', '#ccc')}; - z-index: 10; + z-index: ${zIndexLayers.fixed.appBar}; display: block; position: relative; width: var(--menuSize); diff --git a/ts_web/elements/dees-appui-profiledropdown.ts b/ts_web/elements/dees-appui-profiledropdown.ts index 65814cd..b7f6eea 100644 --- a/ts_web/elements/dees-appui-profiledropdown.ts +++ b/ts_web/elements/dees-appui-profiledropdown.ts @@ -1,4 +1,5 @@ import * as plugins from './00plugins.js'; +import { zIndexLayers } from './00zindex.js'; import { DeesElement, @@ -73,7 +74,7 @@ export class DeesAppuiProfileDropdown extends DeesElement { '0 4px 12px rgba(0, 0, 0, 0.15)', '0 4px 12px rgba(0, 0, 0, 0.3)' )}; - z-index: 1000; + z-index: ${zIndexLayers.overlay.dropdown}; opacity: 0; transform: scale(0.95) translateY(-10px); transition: opacity 0.2s, transform 0.2s; @@ -258,7 +259,7 @@ export class DeesAppuiProfileDropdown extends DeesElement { right: 0; bottom: 0; background: rgba(0, 0, 0, 0.3); - z-index: 999; + z-index: ${zIndexLayers.backdrop.dropdown}; opacity: 0; transition: opacity 0.2s; display: none; diff --git a/ts_web/elements/dees-contextmenu.ts b/ts_web/elements/dees-contextmenu.ts index 2706339..a62bd96 100644 --- a/ts_web/elements/dees-contextmenu.ts +++ b/ts_web/elements/dees-contextmenu.ts @@ -14,6 +14,7 @@ import { import * as domtools from '@design.estate/dees-domtools'; import { DeesWindowLayer } from './dees-windowlayer.js'; +import { zIndexLayers } from './00zindex.js'; import './dees-icon.js'; declare global { @@ -74,7 +75,7 @@ export class DeesContextmenu extends DeesElement { eventArg.stopPropagation(); const contextMenu = new DeesContextmenu(); contextMenu.style.position = 'fixed'; - contextMenu.style.zIndex = '10000'; + contextMenu.style.zIndex = String(zIndexLayers.overlay.contextMenu); contextMenu.style.opacity = '0'; contextMenu.style.transform = 'scale(0.95) translateY(-10px)'; contextMenu.menuItems = menuItemsArg; diff --git a/ts_web/elements/dees-mobilenavigation.ts b/ts_web/elements/dees-mobilenavigation.ts index 5190b41..b2804a9 100644 --- a/ts_web/elements/dees-mobilenavigation.ts +++ b/ts_web/elements/dees-mobilenavigation.ts @@ -1,4 +1,5 @@ import * as plugins from './00plugins.js'; +import { zIndexLayers } from './00zindex.js'; import { cssManager, css, @@ -83,7 +84,7 @@ export class DeesMobilenavigation extends DeesElement { min-width: 280px; transform: translateX(200px); color: ${cssManager.bdTheme('#333', '#fff')}; - z-index: 250; + z-index: ${zIndexLayers.fixed.mobileNav}; opacity: 0; padding: 16px 32px; right: 0px; diff --git a/ts_web/elements/dees-modal.ts b/ts_web/elements/dees-modal.ts index 04ede01..3f445fe 100644 --- a/ts_web/elements/dees-modal.ts +++ b/ts_web/elements/dees-modal.ts @@ -1,5 +1,6 @@ import * as colors from './00colors.js'; import * as plugins from './00plugins.js'; +import { zIndexLayers } from './00zindex.js'; import { demoFunc } from './dees-modal.demo.js'; import { @@ -85,7 +86,7 @@ export class DeesModal extends DeesElement { box-sizing: border-box; align-items: center; justify-content: center; - z-index: 2000; + z-index: ${zIndexLayers.overlay.modal}; } .modal { will-change: transform; diff --git a/ts_web/elements/dees-toast.ts b/ts_web/elements/dees-toast.ts index bc1d6db..66f2cb2 100644 --- a/ts_web/elements/dees-toast.ts +++ b/ts_web/elements/dees-toast.ts @@ -1,6 +1,7 @@ import { customElement, DeesElement, type TemplateResult, html, css, property, cssManager } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; +import { zIndexLayers } from './00zindex.js'; import { demoFunc } from './dees-toast.demo.js'; declare global { @@ -32,7 +33,7 @@ export class DeesToast extends DeesElement { container.className = `toast-container toast-container-${position}`; container.style.cssText = ` position: fixed; - z-index: 10000; + z-index: ${zIndexLayers.overlay.toast}; pointer-events: none; padding: 16px; display: flex; diff --git a/ts_web/elements/dees-windowlayer.ts b/ts_web/elements/dees-windowlayer.ts index 9e48480..540bb7a 100644 --- a/ts_web/elements/dees-windowlayer.ts +++ b/ts_web/elements/dees-windowlayer.ts @@ -1,4 +1,5 @@ import { customElement, DeesElement, domtools, type TemplateResult, html, property, type CSSResult, state, } from '@design.estate/dees-element'; +import { zIndexLayers } from './00zindex.js'; declare global { interface HTMLElementTagNameMap { @@ -62,7 +63,7 @@ export class DeesWindowLayer extends DeesElement { background: rgba(0, 0, 0, 0.0); backdrop-filter: brightness(1) ${this.options.blur ? 'blur(0px)' : ''}; pointer-events: none; - z-index: 200; + z-index: ${zIndexLayers.backdrop.dropdown}; } .slotContent { position: fixed; @@ -71,7 +72,7 @@ export class DeesWindowLayer extends DeesElement { display: flex; justify-content: center; align-items: center; - z-index: 201; + z-index: ${zIndexLayers.overlay.dropdown}; } .visible { diff --git a/ts_web/elements/index.ts b/ts_web/elements/index.ts index 90fad72..af856cb 100644 --- a/ts_web/elements/index.ts +++ b/ts_web/elements/index.ts @@ -1,3 +1,4 @@ +export * from './00zindex.js'; export * from './dees-appui-activitylog.js'; export * from './dees-appui-appbar.js'; export * from './dees-appui-base.js'; diff --git a/ts_web/elements/wysiwyg/dees-formatting-menu.ts b/ts_web/elements/wysiwyg/dees-formatting-menu.ts index 97f91c9..fda6170 100644 --- a/ts_web/elements/wysiwyg/dees-formatting-menu.ts +++ b/ts_web/elements/wysiwyg/dees-formatting-menu.ts @@ -7,6 +7,7 @@ import { css, state, } from '@design.estate/dees-element'; +import { zIndexLayers } from '../00zindex.js'; import { WysiwygFormatting } from './wysiwyg.formatting.js'; @@ -41,7 +42,7 @@ export class DeesFormattingMenu extends DeesElement { css` :host { position: fixed; - z-index: 10000; + z-index: ${zIndexLayers.wysiwygMenus}; pointer-events: none; } diff --git a/ts_web/elements/wysiwyg/dees-slash-menu.ts b/ts_web/elements/wysiwyg/dees-slash-menu.ts index 5865190..c735bea 100644 --- a/ts_web/elements/wysiwyg/dees-slash-menu.ts +++ b/ts_web/elements/wysiwyg/dees-slash-menu.ts @@ -8,6 +8,7 @@ import { css, state, } from '@design.estate/dees-element'; +import { zIndexLayers } from '../00zindex.js'; import { type ISlashMenuItem } from './wysiwyg.types.js'; import { WysiwygShortcuts } from './wysiwyg.shortcuts.js'; @@ -49,7 +50,7 @@ export class DeesSlashMenu extends DeesElement { css` :host { position: fixed; - z-index: 10000; + z-index: ${zIndexLayers.wysiwygMenus}; pointer-events: none; }