Accessibility
ARIA Live Regions
Announce messages to screen readers:
js
import { announce, aria, role } from 'weblisk/a11y/aria.js'; // Polite announcement (waits for user to finish reading) announce('3 new items loaded'); // Assertive announcement (interrupts) announce('Error: Connection lost', 'assertive'); // Declarative ARIA attributes aria('#dialog', { modal: true, labelledby: 'dialog-title' }); // Set a role with required attributes role('#tab-panel', 'tabpanel', { labelledby: 'tab-1' });
Focus Management
Focus Trap
Trap focus within a container (modals, dialogs):
js
import { focusTrap } from 'weblisk/a11y/focus.js'; const trap = focusTrap('#modal'); trap.activate(); // Tab now cycles within #modal trap.deactivate(); // Release focus
Roving Tabindex
Arrow key navigation within a group (tabs, toolbars, menus):
js
import { rovingIndex } from 'weblisk/a11y/focus.js'; const cleanup = rovingIndex('#toolbar', 'button', { horizontal: true, wrap: true, // Arrow keys wrap around });
Skip Link
Add a "Skip to main content" link for keyboard navigation:
js
import { skipLink } from 'weblisk/a11y/focus.js'; skipLink('main-content'); // Creates: <a class="wl-skip" href="#main-content">Skip to main content</a>
Reduced Motion
Respect the user's motion preference:
js
import { motionReduced, motion, safeTransition, ifMotion } from 'weblisk/a11y/motion.js'; // Reactive signal effect(() => { if (motionReduced()) { console.log('User prefers reduced motion'); } }); // Choose value based on preference const duration = motion('0.3s', '0s'); // Only set transition if motion is allowed safeTransition('#panel', 'transform 0.3s ease'); // Run function only if motion is allowed ifMotion(() => { el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300 }); });
Global CSS Override
Inject CSS that disables all animations when reduced motion is preferred:
js
import { injectReducedMotionCSS } from 'weblisk/a11y/motion.js'; injectReducedMotionCSS();
This adds:
css
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
Locale & Internationalization
Signal-driven locale detection and Intl API formatting wrappers:
js
import { locale, setLocale, textDirection, formatter } from 'weblisk/a11y/locale.js'; // Reactive locale signal (reads html[lang] or navigator.language) console.log(locale()); // 'en-US' // Switch locale — all formatters update reactively setLocale('fr'); // Text direction signal for RTL support const dir = textDirection(); // 'ltr' or 'rtl' // Intl formatters tied to the locale signal const fmt = formatter(); fmt.number(1234.5); // '1,234.5' or '1 234,5' fmt.date(new Date()); // locale-formatted date fmt.currency(99.99, 'USD'); // '$99.99' or '99,99 $US' fmt.relative(-2, 'day'); // '2 days ago' or 'il y a 2 jours' fmt.list(['A', 'B', 'C']); // 'A, B, and C' or 'A, B et C'
No translation dictionaries — uses the browser's native Intl API for locale-aware formatting.