UNPKG

@sc4rfurryx/proteusjs

Version:

The Modern Web Development Framework for Accessible, Responsive, and High-Performance Applications. Intelligent container queries, fluid typography, WCAG compliance, and performance optimization.

195 lines (193 loc) 6.2 kB
/*! * ProteusJS v2.0.0 * Shape-shifting responsive design that adapts like the sea god himself * (c) 2025 sc4rfurry * Released under the MIT License */ /** * @sc4rfurryx/proteusjs/container * Container/Style Query helpers with visualization devtools * * @version 2.0.0 * @author sc4rfurry * @license MIT */ /** * Sugar on native container queries with dev visualization */ function defineContainer(target, name, opts = {}) { const targetEl = typeof target === 'string' ? document.querySelector(target) : target; if (!targetEl) { throw new Error('Target element not found'); } const { type = 'size', inlineSize: _inlineSize = true } = opts; const containerName = name || `container-${Math.random().toString(36).substring(2, 11)}`; // Apply container properties const element = targetEl; element.style.containerName = containerName; element.style.containerType = type; // Warn if containment settings are missing const computedStyle = getComputedStyle(element); if (!computedStyle.contain || computedStyle.contain === 'none') { console.warn(`Container "${containerName}" may need explicit containment settings for optimal performance`); } // Dev overlay (only in development) if (process.env['NODE_ENV'] === 'development' || window.__PROTEUS_DEV__) { createDevOverlay(element, containerName); } } /** * Create development overlay showing container bounds and breakpoints */ function createDevOverlay(element, name) { const overlay = document.createElement('div'); overlay.className = 'proteus-container-overlay'; overlay.style.cssText = ` position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; border: 2px dashed rgba(255, 0, 255, 0.5); background: rgba(255, 0, 255, 0.05); z-index: 9999; font-family: monospace; font-size: 12px; color: #ff00ff; `; const label = document.createElement('div'); label.style.cssText = ` position: absolute; top: -20px; left: 0; background: rgba(255, 0, 255, 0.9); color: white; padding: 2px 6px; border-radius: 3px; font-size: 10px; white-space: nowrap; `; label.textContent = `Container: ${name}`; const sizeInfo = document.createElement('div'); sizeInfo.style.cssText = ` position: absolute; bottom: 2px; right: 2px; background: rgba(0, 0, 0, 0.7); color: white; padding: 2px 4px; border-radius: 2px; font-size: 10px; `; overlay.appendChild(label); overlay.appendChild(sizeInfo); // Position overlay relative to container if (getComputedStyle(element).position === 'static') { element.style.position = 'relative'; } element.appendChild(overlay); // Update size info const updateSizeInfo = () => { const rect = element.getBoundingClientRect(); sizeInfo.textContent = `${Math.round(rect.width)}×${Math.round(rect.height)}`; }; updateSizeInfo(); // Update on resize if ('ResizeObserver' in window) { const resizeObserver = new ResizeObserver(updateSizeInfo); resizeObserver.observe(element); } // Store cleanup function element._proteusContainerCleanup = () => { overlay.remove(); }; } /** * Helper to create container query CSS rules */ function createContainerQuery(containerName, condition, styles) { const cssRules = Object.entries(styles) .map(([property, value]) => ` ${property}: ${value};`) .join('\n'); return `@container ${containerName} (${condition}) {\n${cssRules}\n}`; } /** * Apply container query styles dynamically */ function applyContainerQuery(containerName, condition, styles) { const css = createContainerQuery(containerName, condition, styles); const styleElement = document.createElement('style'); styleElement.textContent = css; styleElement.setAttribute('data-proteus-container', containerName); document.head.appendChild(styleElement); } /** * Remove container query styles */ function removeContainerQuery(containerName) { const styleElements = document.querySelectorAll(`style[data-proteus-container="${containerName}"]`); styleElements.forEach(element => element.remove()); } /** * Get container size information */ function getContainerSize(target) { const targetEl = typeof target === 'string' ? document.querySelector(target) : target; if (!targetEl) { throw new Error('Target element not found'); } const rect = targetEl.getBoundingClientRect(); return { width: rect.width, height: rect.height }; } /** * Check if container queries are supported */ function isSupported() { return CSS.supports('container-type', 'size'); } /** * Cleanup container overlays and observers */ function cleanup(target) { const targetEl = typeof target === 'string' ? document.querySelector(target) : target; if (!targetEl) return; // Call stored cleanup function if it exists const elementWithCleanup = targetEl; if (elementWithCleanup._proteusContainerCleanup) { elementWithCleanup._proteusContainerCleanup(); delete elementWithCleanup._proteusContainerCleanup; } } /** * Toggle dev overlay visibility */ function toggleDevOverlay(visible) { const overlays = document.querySelectorAll('.proteus-container-overlay'); overlays.forEach(overlay => { const element = overlay; if (visible !== undefined) { element.style.display = visible ? 'block' : 'none'; } else { element.style.display = element.style.display === 'none' ? 'block' : 'none'; } }); } // Export default object for convenience var index = { defineContainer, createContainerQuery, applyContainerQuery, removeContainerQuery, getContainerSize, isSupported, cleanup, toggleDevOverlay }; export { applyContainerQuery, cleanup, createContainerQuery, index as default, defineContainer, getContainerSize, isSupported, removeContainerQuery, toggleDevOverlay }; //# sourceMappingURL=container.esm.js.map