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.

115 lines (113 loc) 3.9 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/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