# Resize Handle Overlay Issue - Investigation Report ## Problem Summary The resize handles in edit mode are being rendered **INSIDE the widget container DOM**, causing: - Widgets to stretch and overflow their grid bounds - Scrollbars to appear unexpectedly - Edit/delete buttons to be hidden or inconsistently visible - Layout overflow issues The handles use negative positioning (`top: -6px`, `left: -3px`) to extend outside widget bounds, but being children of the widget element causes them to contribute to the widget's `offsetHeight` and `offsetWidth`, which creates unwanted scrollbars and overflow. --- ## Investigation Findings ### 1. Where Resize Handles Are Created and Appended **File:** `src/systems/dashboard/resizeHandler.js` **Key Code (Lines 172-215):** ```javascript createResizeHandles() { const container = document.createElement('div'); container.className = 'resize-handles'; container.style.position = 'absolute'; container.style.inset = '0'; container.style.pointerEvents = 'none'; // Create 8 handles (4 corners + 4 edges) Object.entries(this.handleTypes).forEach(([handleType, cursor]) => { const handle = document.createElement('div'); handle.className = `resize-handle resize-handle-${handleType}`; // ... positioning ... handle.style.top = '-6px'; // Negative positioning handle.style.left = '-3px'; // Negative positioning handle.style.zIndex = '100'; container.appendChild(handle); }); return container; } ``` **Appended At (Line 77):** ```javascript initWidget(element, widget, onResizeEnd, constraints = {}) { const handles = this.createResizeHandles(); element.appendChild(handles); // <-- APPENDED INSIDE WIDGET // ... } ``` **Problem:** The handles container is appended directly to the widget element (`element.appendChild(handles)`), making it a child of `.rpg-widget`. --- ### 2. Where Edit/Delete Buttons Are Created **File:** `src/systems/dashboard/editModeManager.js` **Key Code (Lines 325-373):** ```javascript addWidgetControls(element, widgetId) { const controls = document.createElement('div'); controls.className = 'widget-edit-controls'; controls.style.position = 'absolute'; controls.style.top = '4px'; controls.style.right = '4px'; controls.style.display = 'flex'; controls.style.gap = '4px'; controls.style.zIndex = '100'; controls.style.opacity = '0'; controls.style.transition = 'opacity 0.2s'; // Create settings and delete buttons const settingsBtn = this.createControlButton('⚙', 'Settings'); const deleteBtn = this.createControlButton('×', 'Delete'); controls.appendChild(settingsBtn); controls.appendChild(deleteBtn); element.appendChild(controls); // <-- APPENDED INSIDE WIDGET // ... } ``` **Problem:** Like the resize handles, the edit controls are appended inside the widget element as a child. --- ### 3. Current DOM Structure ```
``` **Why This Causes Issues:** - Even though handles have `position: absolute`, they're still part of the DOM flow calculation - Negative positioning extends them outside the widget visually, but the browser still includes them in overflow calculations - This causes scrollbars when the widget container has `overflow: auto` or `overflow: scroll` - The controls at `top: 4px; right: 4px` with `z-index: 100` can be covered or hidden by other elements --- ### 4. CSS Widget Styling **File:** `style.css` **Key Widget CSS:** ```css .rpg-widget { box-sizing: border-box; overflow: visible; /* Allow resize handles to extend beyond widget bounds */ display: flex; flex-direction: column; max-height: 100%; /* Prevent content from overflowing grid cell */ /* ... other styles ... */ } /* Hide resize handles by default */ .resize-handles { opacity: 0; pointer-events: none; transition: opacity 0.2s; } /* Show resize handles in edit mode */ .edit-mode .resize-handles { opacity: 1; pointer-events: auto; } /* Hide resize handles when widgets are locked */ .widgets-locked .resize-handles { opacity: 0 !important; pointer-events: none !important; } ``` **Current State:** - Widget has `overflow: visible` - correct for allowing handles to show - But the negative positioning of handles inside the widget still causes layout issues - The `max-height: 100%` on flex column can cause scrollbars if child heights exceed parent --- ### 5. Why Buttons Are Inconsistently Visible The edit/delete buttons are positioned inside the widget at `top: 4px; right: 4px;` with `z-index: 100`. Issues arise: 1. **Scrollbars Overlap:** If the widget develops a scrollbar, the buttons are positioned relative to the widget's content box, not the visible area, so they can be hidden by the scrollbar. 2. **Parent Stacking Context:** The widget element's positioning and z-index hierarchy may cause the buttons to be layered differently depending on scroll state. 3. **Hover State Lost:** When scrollbars appear, the widget's visual bounds change, and hover detection may fail to show/hide buttons consistently. 4. **Absolute Positioning Within Scrollable Parent:** Buttons positioned absolutely within a widget that can scroll create unpredictable rendering. --- ## Recommended Approach: Make Handles & Buttons True Overlays ### Strategy **Move resize handles and edit controls outside the widget DOM to a shared overlay container at the dashboard/grid level.** **Current (Problematic) Structure:** ``` ``` **Target (Fixed) Structure:** ```