@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.
115 lines (113 loc) • 3.9 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/a11y-primitives
* Lightweight accessibility patterns
*
* @version 2.0.0
* @author sc4rfurry
* @license MIT
*/
function dialog(root, opts = {}) {
const el = typeof root === 'string' ? document.querySelector(root) : root;
if (!el)
throw new Error('Dialog element not found');
const { modal = true, restoreFocus = true } = opts;
const close = () => {
};
return { destroy: () => close() };
}
function tooltip(trigger, content, opts = {}) {
const { delay = 300 } = opts;
let timeout;
const show = () => {
clearTimeout(timeout);
timeout = window.setTimeout(() => {
content.setAttribute('role', 'tooltip');
trigger.setAttribute('aria-describedby', content.id || 'tooltip');
content.style.display = 'block';
}, delay);
};
const hide = () => {
clearTimeout(timeout);
content.style.display = 'none';
trigger.removeAttribute('aria-describedby');
};
trigger.addEventListener('mouseenter', show);
trigger.addEventListener('mouseleave', hide);
trigger.addEventListener('focus', show);
trigger.addEventListener('blur', hide);
return {
destroy: () => {
clearTimeout(timeout);
trigger.removeEventListener('mouseenter', show);
trigger.removeEventListener('mouseleave', hide);
trigger.removeEventListener('focus', show);
trigger.removeEventListener('blur', hide);
}
};
}
function focusTrap(container) {
const focusable = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const activate = () => {
const elements = container.querySelectorAll(focusable);
if (elements.length === 0)
return;
const first = elements[0];
const last = elements[elements.length - 1];
const handleTab = (e) => {
if (e.key !== 'Tab')
return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
}
else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
};
container.addEventListener('keydown', handleTab);
first.focus();
return () => container.removeEventListener('keydown', handleTab);
};
let deactivate = () => { };
return {
activate: () => { deactivate = activate() || (() => { }); },
deactivate: () => deactivate()
};
}
function menu(container) {
const items = container.querySelectorAll('[role="menuitem"]');
let currentIndex = 0;
const navigate = (e) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
currentIndex = (currentIndex + 1) % items.length;
items[currentIndex].focus();
break;
case 'ArrowUp':
e.preventDefault();
currentIndex = currentIndex === 0 ? items.length - 1 : currentIndex - 1;
items[currentIndex].focus();
break;
case 'Escape':
e.preventDefault();
container.dispatchEvent(new CustomEvent('menu:close'));
break;
}
};
container.setAttribute('role', 'menu');
container.addEventListener('keydown', navigate);
return {
destroy: () => container.removeEventListener('keydown', navigate)
};
}
var index = { dialog, tooltip, focusTrap, menu };
export { index as default, dialog, focusTrap, menu, tooltip };
//# sourceMappingURL=a11y-primitives.esm.js.map