@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
JavaScript
/*!
* 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