@bquery/bquery 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -33
- package/dist/{a11y-DgUQ8-fI.js → a11y-IV_bfLyn.js} +3 -3
- package/dist/{a11y-DgUQ8-fI.js.map → a11y-IV_bfLyn.js.map} +1 -1
- package/dist/a11y.es.mjs +1 -1
- package/dist/{component-D8ydhe58.js → component-BtsRbf6c.js} +7 -7
- package/dist/{component-D8ydhe58.js.map → component-BtsRbf6c.js.map} +1 -1
- package/dist/component.es.mjs +1 -1
- package/dist/{concurrency-BU1wPEsZ.js → concurrency-kycgAZvW.js} +3 -3
- package/dist/{concurrency-BU1wPEsZ.js.map → concurrency-kycgAZvW.js.map} +1 -1
- package/dist/concurrency.es.mjs +1 -1
- package/dist/{config-DhT9auRm.js → config-BP7KwiR5.js} +1 -1
- package/dist/{config-DhT9auRm.js.map → config-BP7KwiR5.js.map} +1 -1
- package/dist/constraints-Dlbx_m1b.js.map +1 -1
- package/dist/core-tOP6QOrY.js.map +1 -1
- package/dist/{core-CongXJuo.js → core-yg9rJXiR.js} +1 -1
- package/dist/{core-CongXJuo.js.map → core-yg9rJXiR.js.map} +1 -1
- package/dist/custom-directives-5DlKqvd2.js.map +1 -1
- package/dist/devtools-QosAqo0T.js.map +1 -1
- package/dist/dnd-d2OU4len.js.map +1 -1
- package/dist/{effect-Cc51IH91.js → effect-v8OIEmPs.js} +2 -2
- package/dist/{effect-Cc51IH91.js.map → effect-v8OIEmPs.js.map} +1 -1
- package/dist/env-PvwYHnJq.js.map +1 -1
- package/dist/{forms-BLx4ZzT7.js → forms-DYcdlk_h.js} +8 -8
- package/dist/{forms-BLx4ZzT7.js.map → forms-DYcdlk_h.js.map} +1 -1
- package/dist/forms.es.mjs +1 -1
- package/dist/full.d.ts +11 -11
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +324 -279
- package/dist/full.iife.js +26 -26
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +26 -26
- package/dist/full.umd.js.map +1 -1
- package/dist/function-Cybd57JV.js.map +1 -1
- package/dist/{i18n--p7PM-9r.js → i18n-unHU1jMo.js} +3 -3
- package/dist/{i18n--p7PM-9r.js.map → i18n-unHU1jMo.js.map} +1 -1
- package/dist/i18n.es.mjs +1 -1
- package/dist/index.es.mjs +198 -196
- package/dist/match-CrZRVC4z.js.map +1 -1
- package/dist/{media-gjbWNq50.js → media-Chh6mA0v.js} +3 -3
- package/dist/{media-gjbWNq50.js.map → media-Chh6mA0v.js.map} +1 -1
- package/dist/media.es.mjs +1 -1
- package/dist/{motion-BBMso9Ir.js → motion-27Od9aFE.js} +2 -2
- package/dist/{motion-BBMso9Ir.js.map → motion-27Od9aFE.js.map} +1 -1
- package/dist/motion.es.mjs +1 -1
- package/dist/{mount-0A9qtcRJ.js → mount-DwUFujZ_.js} +4 -4
- package/dist/{mount-0A9qtcRJ.js.map → mount-DwUFujZ_.js.map} +1 -1
- package/dist/object-BCk-1c8T.js.map +1 -1
- package/dist/{platform-BPHIXbw8.js → platform-2YkFA11t.js} +4 -4
- package/dist/{platform-BPHIXbw8.js.map → platform-2YkFA11t.js.map} +1 -1
- package/dist/platform.es.mjs +2 -2
- package/dist/plugin-SZEirbwq.js.map +1 -1
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/signal.d.ts +7 -7
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/websocket.d.ts +15 -3
- package/dist/reactive/websocket.d.ts.map +1 -1
- package/dist/{reactive-BAd2hfl8.js → reactive-BvPR_FYA.js} +10 -10
- package/dist/{reactive-BAd2hfl8.js.map → reactive-BvPR_FYA.js.map} +1 -1
- package/dist/reactive.es.mjs +40 -40
- package/dist/{readonly-C0ZwS1Tf.js → readonly-Br-6pAgj.js} +2 -2
- package/dist/{readonly-C0ZwS1Tf.js.map → readonly-Br-6pAgj.js.map} +1 -1
- package/dist/registry-jpUQHf4E.js.map +1 -1
- package/dist/{router-C4weu0QL.js → router-CbnWKprL.js} +7 -7
- package/dist/{router-C4weu0QL.js.map → router-CbnWKprL.js.map} +1 -1
- package/dist/router.es.mjs +1 -1
- package/dist/sanitize-DOMkRO9G.js.map +1 -1
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/{server-QdyKtCS1.js → server-Dwiq_F49.js} +2 -2
- package/dist/server-Dwiq_F49.js.map +1 -0
- package/dist/server.es.mjs +1 -1
- package/dist/ssr/context.d.ts.map +1 -1
- package/dist/ssr/renderer.d.ts.map +1 -1
- package/dist/ssr/suspense.d.ts.map +1 -1
- package/dist/{ssr-Bt6BQA3J.js → ssr-CqJU1Ogp.js} +8 -8
- package/dist/ssr-CqJU1Ogp.js.map +1 -0
- package/dist/ssr.es.mjs +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/plugins.d.ts +38 -0
- package/dist/store/plugins.d.ts.map +1 -1
- package/dist/{store-DnXuu6Li.js → store-DzrhVQ29.js} +69 -62
- package/dist/store-DzrhVQ29.js.map +1 -0
- package/dist/store.es.mjs +13 -11
- package/dist/storybook.es.mjs.map +1 -1
- package/dist/{testing-CeMUwrRD.js → testing-ByjwS2_D.js} +3 -3
- package/dist/{testing-CeMUwrRD.js.map → testing-ByjwS2_D.js.map} +1 -1
- package/dist/testing.es.mjs +1 -1
- package/dist/type-guards-BMX2c0LP.js.map +1 -1
- package/dist/{untrack-bjWDNdyE.js → untrack-uzz3JDNK.js} +3 -3
- package/dist/{untrack-bjWDNdyE.js.map → untrack-uzz3JDNK.js.map} +1 -1
- package/dist/view.es.mjs +8 -8
- package/package.json +8 -7
- package/src/full.ts +75 -6
- package/src/reactive/index.ts +5 -4
- package/src/reactive/signal.ts +7 -6
- package/src/reactive/websocket.ts +15 -2
- package/src/server/create-server.ts +10 -5
- package/src/server/types.ts +1 -5
- package/src/ssr/context.ts +6 -2
- package/src/ssr/renderer.ts +5 -2
- package/src/ssr/suspense.ts +17 -8
- package/src/store/index.ts +1 -1
- package/src/store/plugins.ts +48 -1
- package/dist/server-QdyKtCS1.js.map +0 -1
- package/dist/ssr-Bt6BQA3J.js.map +0 -1
- package/dist/store-DnXuu6Li.js.map +0 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
> **New in 1.
|
|
18
|
+
> **New in 1.12.0:** Store plugins now support teardown via `unregisterPlugin()` and `clearPlugins()`, Reactive WebSocket helpers expose the public `WebSocketSendData` payload type, and the `/full` bundle validation now catches runtime and type export drift before release. The runtime-agnostic SSR/server surface from 1.11.x remains first-class.
|
|
19
19
|
|
|
20
20
|
## Highlights
|
|
21
21
|
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
- **TypeScript-first and tree-shakeable**: explicit entry points keep large apps typed while letting smaller apps import focused modules.
|
|
27
27
|
- **Security-focused**: DOM writes are sanitized by default, with Trusted Types and CSP helpers built in.
|
|
28
28
|
|
|
29
|
-
|
|
30
29
|
## Installation
|
|
31
30
|
|
|
32
31
|
### Via npm/bun/pnpm
|
|
@@ -118,6 +117,7 @@ import {
|
|
|
118
117
|
createRequestQueue,
|
|
119
118
|
deduplicateRequest,
|
|
120
119
|
} from '@bquery/bquery/reactive';
|
|
120
|
+
import type { WebSocketSendData } from '@bquery/bquery/reactive';
|
|
121
121
|
|
|
122
122
|
// Concurrency only
|
|
123
123
|
import {
|
|
@@ -163,7 +163,7 @@ import { storage, cache, useCookie, definePageMeta, useAnnouncer } from '@bquery
|
|
|
163
163
|
|
|
164
164
|
// Router, Store, View
|
|
165
165
|
import { createRouter, navigate } from '@bquery/bquery/router';
|
|
166
|
-
import { createStore, defineStore } from '@bquery/bquery/store';
|
|
166
|
+
import { clearPlugins, createStore, defineStore, unregisterPlugin } from '@bquery/bquery/store';
|
|
167
167
|
import { mount, createTemplate } from '@bquery/bquery/view';
|
|
168
168
|
|
|
169
169
|
// Forms, i18n, accessibility, drag & drop, media
|
|
@@ -184,7 +184,15 @@ import {
|
|
|
184
184
|
import { use } from '@bquery/bquery/plugin';
|
|
185
185
|
import { enableDevtools, inspectSignals } from '@bquery/bquery/devtools';
|
|
186
186
|
import { renderComponent, fireEvent, waitFor } from '@bquery/bquery/testing';
|
|
187
|
-
import {
|
|
187
|
+
import {
|
|
188
|
+
createSSRContext,
|
|
189
|
+
hydrateMount,
|
|
190
|
+
renderToResponse,
|
|
191
|
+
renderToStream,
|
|
192
|
+
renderToString,
|
|
193
|
+
renderToStringAsync,
|
|
194
|
+
serializeStoreState,
|
|
195
|
+
} from '@bquery/bquery/ssr';
|
|
188
196
|
import { createServer } from '@bquery/bquery/server';
|
|
189
197
|
|
|
190
198
|
// Storybook helpers
|
|
@@ -193,29 +201,29 @@ import { storyHtml, when } from '@bquery/bquery/storybook';
|
|
|
193
201
|
|
|
194
202
|
## Modules at a glance
|
|
195
203
|
|
|
196
|
-
| Module | Status
|
|
197
|
-
| --------------- |
|
|
198
|
-
| **Core** | Stable
|
|
199
|
-
| **Reactive** | Stable
|
|
200
|
-
| **Concurrency** | Experimental
|
|
201
|
-
| **Component** | Stable
|
|
202
|
-
| **Storybook** | Beta
|
|
203
|
-
| **Motion** | Stable
|
|
204
|
-
| **Security** | Stable
|
|
205
|
-
| **Platform** | Stable
|
|
206
|
-
| **Router** | Stable
|
|
207
|
-
| **Store** | Stable
|
|
208
|
-
| **View** | Beta
|
|
209
|
-
| **Forms** | Beta
|
|
210
|
-
| **i18n** | Beta
|
|
211
|
-
| **A11y** | Beta
|
|
212
|
-
| **DnD** | Beta
|
|
213
|
-
| **Media** | Beta
|
|
214
|
-
| **Plugin** | Beta
|
|
215
|
-
| **Devtools** | Beta
|
|
216
|
-
| **Testing** | Beta
|
|
217
|
-
| **SSR** | Experimental
|
|
218
|
-
| **Server** | Experimental
|
|
204
|
+
| Module | Status | Description |
|
|
205
|
+
| --------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
206
|
+
| **Core** | Stable | Selectors, DOM manipulation, events, traversal, and typed utilities |
|
|
207
|
+
| **Reactive** | Stable | `signal`, `computed`, `effect`, `watchDebounce`, `watchThrottle`, async data, HTTP clients, polling, pagination, WebSocket / SSE, and REST helpers |
|
|
208
|
+
| **Concurrency** | Experimental | Zero-build worker tasks, explicit RPC helpers, optional reactive state wrappers, bounded worker pools, high-level collection helpers, and an optional fluent pipeline layer |
|
|
209
|
+
| **Component** | Stable | Typed Web Components with scoped reactivity and configurable Shadow DOM |
|
|
210
|
+
| **Storybook** | Beta | Safe story template helpers with boolean-attribute shorthand |
|
|
211
|
+
| **Motion** | Stable | View transitions, FLIP, morphing, parallax, typewriter, springs, and timelines |
|
|
212
|
+
| **Security** | Stable | HTML sanitization, Trusted Types, CSP helpers, and trusted fragment composition |
|
|
213
|
+
| **Platform** | Stable | Storage, cache, cookies, page metadata, announcers, and shared runtime config |
|
|
214
|
+
| **Router** | Stable | SPA routing, constrained params, redirects, guards, `useRoute()`, and `<bq-link>` |
|
|
215
|
+
| **Store** | Stable | Signal-based state management, persistence, migrations, action hooks, and plugin lifecycle helpers |
|
|
216
|
+
| **View** | Beta | Declarative DOM bindings with `bq-*` directives for content, classes, forms, errors, ARIA, and plugins |
|
|
217
|
+
| **Forms** | Beta | Reactive form state with sync/async validation and submit handling |
|
|
218
|
+
| **i18n** | Beta | Reactive locales, interpolation, pluralization, lazy loading, and Intl formatting |
|
|
219
|
+
| **A11y** | Beta | Focus traps, live-region announcements, roving tabindex, skip links, and audits |
|
|
220
|
+
| **DnD** | Beta | Draggable elements, droppable zones, and sortable lists |
|
|
221
|
+
| **Media** | Beta | Reactive browser/device signals for viewport, network, battery, geolocation, clipboard, and DOM observers |
|
|
222
|
+
| **Plugin** | Beta | Global plugin registration for custom directives and Web Components |
|
|
223
|
+
| **Devtools** | Beta | Runtime inspection helpers for signals, stores, components, and timelines |
|
|
224
|
+
| **Testing** | Beta | Component mounting, mock signals/router helpers, and async test utilities |
|
|
225
|
+
| **SSR** | Experimental | Runtime-agnostic server-side rendering (Node ≥ 24, Deno, Bun), streaming, async loaders, hydration islands, head/asset/CSP-nonce management, runtime adapters |
|
|
226
|
+
| **Server** | Experimental | Express-inspired backend routing, middleware, safe response helpers, SSR-aware request handling, and runtime-agnostic WebSocket sessions |
|
|
219
227
|
|
|
220
228
|
Storybook authoring helpers are also available as a dedicated entry point via `@bquery/bquery/storybook`. Worker-task, RPC, worker-pool, high-level task-list / collection helpers, and the optional fluent pipeline layer ship as a dedicated entry point via `@bquery/bquery/concurrency`. Server-side middleware, HTTP routing, and runtime-agnostic WebSocket session helpers ship as a dedicated entry point via `@bquery/bquery/server`.
|
|
221
229
|
|
|
@@ -785,7 +793,7 @@ modalTrap.release();
|
|
|
785
793
|
import { use } from '@bquery/bquery/plugin';
|
|
786
794
|
import { enableDevtools, getTimeline } from '@bquery/bquery/devtools';
|
|
787
795
|
import { renderComponent, fireEvent } from '@bquery/bquery/testing';
|
|
788
|
-
import { renderToString } from '@bquery/bquery/ssr';
|
|
796
|
+
import { createSSRContext, renderToResponse, renderToString } from '@bquery/bquery/ssr';
|
|
789
797
|
import { createServer } from '@bquery/bquery/server';
|
|
790
798
|
|
|
791
799
|
use({
|
|
@@ -808,7 +816,6 @@ const app = createServer();
|
|
|
808
816
|
app.get('/hello/:name', (ctx) => ctx.json({ name: ctx.params.name, q: ctx.query.q }));
|
|
809
817
|
|
|
810
818
|
// Runtime-agnostic async render with head injection (works on Node, Deno, Bun):
|
|
811
|
-
import { createSSRContext, renderToResponse } from '@bquery/bquery/ssr';
|
|
812
819
|
const ctx = createSSRContext({ request: new Request('http://localhost/') });
|
|
813
820
|
ctx.head.add({ title: 'Home' });
|
|
814
821
|
ctx.assets.module('/app.js');
|
|
@@ -825,10 +832,13 @@ mounted.unmount();
|
|
|
825
832
|
|
|
826
833
|
```ts
|
|
827
834
|
import {
|
|
835
|
+
clearPlugins,
|
|
828
836
|
createStore,
|
|
829
837
|
createPersistedStore,
|
|
830
838
|
defineStore,
|
|
831
839
|
mapGetters,
|
|
840
|
+
registerPlugin,
|
|
841
|
+
unregisterPlugin,
|
|
832
842
|
watchStore,
|
|
833
843
|
} from '@bquery/bquery/store';
|
|
834
844
|
|
|
@@ -872,6 +882,14 @@ watchStore(
|
|
|
872
882
|
console.log('Count changed:', value, getters.doubled);
|
|
873
883
|
}
|
|
874
884
|
);
|
|
885
|
+
|
|
886
|
+
const logger = ({ store, options }) => {
|
|
887
|
+
store.$subscribe((state) => console.log(`[${options.id}]`, state));
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
registerPlugin(logger);
|
|
891
|
+
unregisterPlugin(logger); // true when the plugin registration was present
|
|
892
|
+
clearPlugins(); // useful in test teardown
|
|
875
893
|
```
|
|
876
894
|
|
|
877
895
|
### View – declarative bindings
|
|
@@ -894,10 +912,10 @@ mount('#app', {
|
|
|
894
912
|
|
|
895
913
|
| Browser | Version | Support |
|
|
896
914
|
| ------- | ------- | ------- |
|
|
897
|
-
| Chrome | 90+ | ✅ Full
|
|
898
|
-
| Firefox | 90+ | ✅ Full
|
|
899
|
-
| Safari | 15+ | ✅ Full
|
|
900
|
-
| Edge | 90+ | ✅ Full
|
|
915
|
+
| Chrome | 90+ | ✅ Full |
|
|
916
|
+
| Firefox | 90+ | ✅ Full |
|
|
917
|
+
| Safari | 15+ | ✅ Full |
|
|
918
|
+
| Edge | 90+ | ✅ Full |
|
|
901
919
|
|
|
902
920
|
> **No IE support** by design.
|
|
903
921
|
>
|
|
@@ -1008,6 +1026,7 @@ This project provides dedicated context files for AI coding agents:
|
|
|
1008
1026
|
- **[llms.txt](llms.txt)** — Compact LLM-optimized project summary
|
|
1009
1027
|
- **[.github/copilot-instructions.md](.github/copilot-instructions.md)** — GitHub Copilot context
|
|
1010
1028
|
- **`bun run check:ai-guidance`** — Lightweight sync check for version / engine / AI guidance drift
|
|
1029
|
+
- **`bun run check:full-bundle`** — Static guard for `/full` runtime and type export drift
|
|
1011
1030
|
|
|
1012
1031
|
## License
|
|
1013
1032
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as E } from "./core-
|
|
2
|
-
import { n as I } from "./readonly-
|
|
1
|
+
import { n as E } from "./core-yg9rJXiR.js";
|
|
2
|
+
import { n as I } from "./readonly-Br-6pAgj.js";
|
|
3
3
|
var w = /* @__PURE__ */ new Map(), A = /* @__PURE__ */ new Map(), q = 50, M = (e) => {
|
|
4
4
|
const t = w.get(e);
|
|
5
5
|
if (t && t.isConnected) return t;
|
|
@@ -418,4 +418,4 @@ export {
|
|
|
418
418
|
G as u
|
|
419
419
|
};
|
|
420
420
|
|
|
421
|
-
//# sourceMappingURL=a11y-
|
|
421
|
+
//# sourceMappingURL=a11y-IV_bfLyn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a11y-DgUQ8-fI.js","names":[],"sources":["../src/a11y/announce.ts","../src/a11y/audit.ts","../src/a11y/media-preferences.ts","../src/a11y/roving-tab-index.ts","../src/a11y/skip-link.ts","../src/a11y/trap-focus.ts"],"sourcesContent":["/**\n * Screen reader announcement utility using ARIA live regions.\n *\n * Creates and manages off-screen live regions to announce dynamic\n * content changes to assistive technologies.\n *\n * @module bquery/a11y\n */\n\nimport type { AnnouncePriority } from './types';\n\n/** Cache for live region containers, keyed by priority. */\nconst liveRegions = new Map<AnnouncePriority, HTMLElement>();\nconst pendingAnnouncements = new Map<AnnouncePriority, ReturnType<typeof setTimeout>>();\n\n/**\n * Delay in milliseconds before updating the live region text.\n * This ensures screen readers detect the content change even when\n * the same message is announced consecutively — clearing first and\n * setting after a short timer delay forces a new live-region mutation event.\n * @internal\n */\nconst ANNOUNCEMENT_DELAY_MS = 50;\n\n/**\n * Gets or creates a visually-hidden ARIA live region for the given priority.\n *\n * @param priority - The aria-live priority level\n * @returns The live region element\n * @internal\n */\nconst getOrCreateLiveRegion = (priority: AnnouncePriority): HTMLElement => {\n const existing = liveRegions.get(priority);\n if (existing && existing.isConnected) {\n return existing;\n }\n\n const el = document.createElement('div');\n el.setAttribute('aria-live', priority);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('role', priority === 'assertive' ? 'alert' : 'status');\n\n // Visually hidden but accessible to screen readers\n Object.assign(el.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n });\n\n document.body.appendChild(el);\n liveRegions.set(priority, el);\n\n return el;\n};\n\n/**\n * Announces a message to screen readers via an ARIA live region.\n *\n * The message is injected into a visually-hidden live region element.\n * Screen readers will pick up the change and announce it to the user.\n *\n * @param message - The text message to announce\n * @param priority - The urgency level: `'polite'` (default) or `'assertive'`\n *\n * @example\n * ```ts\n * import { announceToScreenReader } from '@bquery/bquery/a11y';\n *\n * // Polite announcement (waits for idle)\n * announceToScreenReader('3 search results found');\n *\n * // Assertive announcement (interrupts current speech)\n * announceToScreenReader('Error: Please fix the form', 'assertive');\n * ```\n */\nexport const announceToScreenReader = (\n message: string,\n priority: AnnouncePriority = 'polite'\n): void => {\n if (!message) return;\n if (typeof document === 'undefined' || !document.body) return;\n\n const region = getOrCreateLiveRegion(priority);\n const pendingTimeout = pendingAnnouncements.get(priority);\n if (pendingTimeout !== undefined) {\n clearTimeout(pendingTimeout);\n }\n\n // Clear first, then set after a short timer delay to ensure screen readers\n // detect the change even if the same message is announced twice.\n region.textContent = '';\n\n // Use setTimeout to ensure the DOM update triggers a live region change event\n const timeout = setTimeout(() => {\n pendingAnnouncements.delete(priority);\n if (region.isConnected) {\n region.textContent = message;\n }\n }, ANNOUNCEMENT_DELAY_MS);\n\n pendingAnnouncements.set(priority, timeout);\n};\n\n/**\n * Removes all live region elements created by `announceToScreenReader`.\n * Useful for cleanup in tests or when unmounting an application.\n *\n * @example\n * ```ts\n * import { clearAnnouncements } from '@bquery/bquery/a11y';\n *\n * clearAnnouncements();\n * ```\n */\nexport const clearAnnouncements = (): void => {\n for (const timeout of pendingAnnouncements.values()) {\n clearTimeout(timeout);\n }\n pendingAnnouncements.clear();\n\n for (const [, el] of liveRegions) {\n el.remove();\n }\n liveRegions.clear();\n};\n","/**\n * Development-time accessibility audit utility.\n *\n * Scans DOM elements for common accessibility issues such as missing\n * alt text on images, missing labels on form inputs, empty links/buttons,\n * and incorrect ARIA usage.\n *\n * @module bquery/a11y\n */\n\nimport type { AuditFinding, AuditResult, AuditSeverity } from './types';\n\n/**\n * Creates a finding object.\n * @internal\n */\nconst finding = (\n severity: AuditSeverity,\n message: string,\n element: Element,\n rule: string\n): AuditFinding => ({\n severity,\n message,\n element,\n rule,\n});\n\n/**\n * Checks images for missing alt attributes.\n * @internal\n */\nconst auditImages = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n const images = container.querySelectorAll('img');\n\n for (const img of images) {\n if (!img.hasAttribute('alt')) {\n findings.push(\n finding(\n 'error',\n 'Image is missing an alt attribute. Add alt=\"\" for decorative images or a descriptive alt text.',\n img,\n 'img-alt'\n )\n );\n } else if (img.getAttribute('alt') === '' && !img.hasAttribute('role')) {\n findings.push(\n finding(\n 'info',\n 'Image has empty alt text. Consider adding role=\"presentation\" if decorative.',\n img,\n 'img-decorative'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Checks form inputs for missing labels.\n * @internal\n */\nconst auditFormInputs = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n const inputs = container.querySelectorAll('input, select, textarea');\n\n for (const input of inputs) {\n const type = input.getAttribute('type');\n\n // Hidden, submit, and button inputs don't need labels\n if (type === 'hidden' || type === 'submit' || type === 'button' || type === 'reset') {\n continue;\n }\n\n const id = input.getAttribute('id');\n const hasLabel = id ? !!container.querySelector(`label[for=\"${id}\"]`) : false;\n const hasAriaLabel = input.hasAttribute('aria-label') || input.hasAttribute('aria-labelledby');\n const hasTitle = input.hasAttribute('title');\n const isWrappedInLabel = input.closest('label') !== null;\n\n if (!hasLabel && !hasAriaLabel && !hasTitle && !isWrappedInLabel) {\n findings.push(\n finding(\n 'error',\n `Form input is missing a label. Add a <label for=\"id\">, aria-label, or aria-labelledby attribute.`,\n input,\n 'input-label'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Checks for empty interactive elements (buttons, links).\n * @internal\n */\nconst auditInteractiveElements = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n\n // Check buttons\n const buttons = container.querySelectorAll('button');\n for (const btn of buttons) {\n const hasText = (btn.textContent ?? '').trim().length > 0;\n const hasAriaLabel = btn.hasAttribute('aria-label') || btn.hasAttribute('aria-labelledby');\n const hasTitle = btn.hasAttribute('title');\n\n if (!hasText && !hasAriaLabel && !hasTitle) {\n findings.push(\n finding(\n 'error',\n 'Button has no accessible name. Add text content, aria-label, or title.',\n btn,\n 'button-name'\n )\n );\n }\n }\n\n // Check links\n const links = container.querySelectorAll('a[href]');\n for (const link of links) {\n const hasText = (link.textContent ?? '').trim().length > 0;\n const hasAriaLabel = link.hasAttribute('aria-label') || link.hasAttribute('aria-labelledby');\n const hasTitle = link.hasAttribute('title');\n const hasImage = link.querySelector('img[alt]') !== null;\n\n if (!hasText && !hasAriaLabel && !hasTitle && !hasImage) {\n findings.push(\n finding(\n 'error',\n 'Link has no accessible name. Add text content, aria-label, or title.',\n link,\n 'link-name'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Checks heading hierarchy for skipped levels.\n * @internal\n */\nconst auditHeadings = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n const headings = container.querySelectorAll('h1, h2, h3, h4, h5, h6');\n\n let previousLevel = 0;\n\n for (const heading of headings) {\n const level = parseInt(heading.tagName.charAt(1), 10);\n\n if (previousLevel > 0 && level > previousLevel + 1) {\n findings.push(\n finding(\n 'warning',\n `Heading level skipped: <${heading.tagName.toLowerCase()}> follows <h${previousLevel}>. Don't skip heading levels.`,\n heading,\n 'heading-order'\n )\n );\n }\n\n if ((heading.textContent ?? '').trim().length === 0) {\n findings.push(finding('warning', 'Heading element is empty.', heading, 'heading-empty'));\n }\n\n previousLevel = level;\n }\n\n return findings;\n};\n\n/**\n * Checks for valid ARIA attribute usage.\n * @internal\n */\nconst auditAria = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n\n // Check aria-labelledby references exist\n const labelled = container.querySelectorAll('[aria-labelledby]');\n for (const el of labelled) {\n const ids = (el.getAttribute('aria-labelledby') ?? '').split(/\\s+/);\n for (const id of ids) {\n if (id && !document.getElementById(id)) {\n findings.push(\n finding(\n 'error',\n `aria-labelledby references \"${id}\" which does not exist in the document.`,\n el,\n 'aria-labelledby-ref'\n )\n );\n }\n }\n }\n\n // Check aria-describedby references exist\n const described = container.querySelectorAll('[aria-describedby]');\n for (const el of described) {\n const ids = (el.getAttribute('aria-describedby') ?? '').split(/\\s+/);\n for (const id of ids) {\n if (id && !document.getElementById(id)) {\n findings.push(\n finding(\n 'error',\n `aria-describedby references \"${id}\" which does not exist in the document.`,\n el,\n 'aria-describedby-ref'\n )\n );\n }\n }\n }\n\n return findings;\n};\n\n/**\n * Checks for sufficient document landmarks.\n * @internal\n */\nconst auditLandmarks = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n\n // Only audit the document body or top-level container\n if (container === document.body || container === document.documentElement) {\n const hasMain = !!container.querySelector('main') || !!container.querySelector('[role=\"main\"]');\n\n if (!hasMain) {\n findings.push(\n finding(\n 'warning',\n 'Page is missing a <main> landmark. Add <main> or role=\"main\" to the primary content area.',\n container,\n 'landmark-main'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Runs a development-time accessibility audit on a container element.\n *\n * Checks for common accessibility issues including:\n * - Missing alt text on images\n * - Missing labels on form inputs\n * - Empty buttons and links\n * - Heading hierarchy issues\n * - Invalid ARIA references\n * - Missing document landmarks\n *\n * This is intended as a development tool — not a replacement for\n * manual testing or professional accessibility audits.\n *\n * @param container - The element to audit (defaults to `document.body`)\n * @returns An audit result with findings, counts, and pass/fail status\n *\n * @example\n * ```ts\n * import { auditA11y } from '@bquery/bquery/a11y';\n *\n * const result = auditA11y();\n * if (!result.passed) {\n * console.warn(`Found ${result.errors} accessibility errors:`);\n * for (const f of result.findings) {\n * console.warn(`[${f.severity}] ${f.message}`, f.element);\n * }\n * }\n * ```\n */\nexport const auditA11y = (container?: Element): AuditResult => {\n if (typeof document === 'undefined' || !document.body) {\n return {\n findings: [],\n errors: 0,\n warnings: 0,\n passed: true,\n };\n }\n\n const target = container ?? document.body;\n\n const allFindings: AuditFinding[] = [\n ...auditImages(target),\n ...auditFormInputs(target),\n ...auditInteractiveElements(target),\n ...auditHeadings(target),\n ...auditAria(target),\n ...auditLandmarks(target),\n ];\n\n const errors = allFindings.filter((f) => f.severity === 'error').length;\n const warnings = allFindings.filter((f) => f.severity === 'warning').length;\n\n return {\n findings: allFindings,\n errors,\n warnings,\n passed: errors === 0,\n };\n};\n","/**\n * Reactive media preference signals for accessibility.\n *\n * Provides reactive signals that track the user's system-level\n * accessibility preferences (reduced motion, color scheme, contrast).\n *\n * @module bquery/a11y\n */\n\nimport { readonly, signal, type ReadonlySignal } from '../reactive/index';\nimport type { ColorScheme, ContrastPreference, MediaPreferenceSignal } from './types';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent | MediaQueryList) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent | MediaQueryList) => void) => void;\n};\n\nconst bindMediaQueryListener = (\n mql: MediaQueryList,\n handler: (event: MediaQueryListEvent | MediaQueryList) => void\n): (() => void) | undefined => {\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', handler);\n return (): void => {\n mql.removeEventListener('change', handler);\n };\n }\n\n const legacyMql = mql as LegacyMediaQueryList;\n if (typeof legacyMql.addListener === 'function') {\n legacyMql.addListener(handler);\n return (): void => {\n legacyMql.removeListener?.(handler);\n };\n }\n\n return undefined;\n};\n\nconst withDestroy = <T>(\n signalHandle: ReadonlySignal<T>,\n cleanup: () => void\n): MediaPreferenceSignal<T> => {\n let destroyImpl = cleanup;\n const handle = signalHandle as MediaPreferenceSignal<T>;\n Object.defineProperty(handle, 'destroy', {\n configurable: true,\n enumerable: false,\n value: (): void => {\n const currentDestroy = destroyImpl;\n // Make cleanup idempotent so repeated destroy() calls from user code stay safe.\n destroyImpl = (): void => {};\n currentDestroy();\n },\n });\n return handle;\n};\n\n/**\n * Creates a reactive signal that tracks a CSS media query.\n *\n * @param query - The media query string\n * @param initialValue - Fallback value when `matchMedia` is unavailable\n * @returns A readonly signal handle that updates when the query match changes\n * @internal\n */\nconst createMediaSignal = (\n query: string,\n initialValue: boolean\n): MediaPreferenceSignal<boolean> => {\n const s = signal(initialValue);\n let destroy = (): void => {\n s.dispose();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n try {\n const mql = window.matchMedia(query);\n s.value = mql.matches;\n\n const handler = (e: MediaQueryListEvent | MediaQueryList): void => {\n s.value = e.matches;\n };\n\n const cleanupMql = bindMediaQueryListener(mql, handler);\n if (cleanupMql) {\n destroy = (): void => {\n cleanupMql();\n s.dispose();\n };\n }\n } catch {\n // matchMedia may throw in non-browser environments\n }\n }\n\n return withDestroy(readonly(s), destroy);\n};\n\n/**\n * Returns a reactive signal indicating whether the user prefers reduced motion.\n *\n * Tracks the `(prefers-reduced-motion: reduce)` media query. Returns `true`\n * when the user has requested reduced motion in their system settings.\n *\n * @returns A readonly reactive signal handle. Call `destroy()` to remove listeners.\n *\n * @example\n * ```ts\n * import { prefersReducedMotion } from '@bquery/bquery/a11y';\n * import { effect } from '@bquery/bquery/reactive';\n *\n * const reduced = prefersReducedMotion();\n * effect(() => {\n * if (reduced.value) {\n * console.log('User prefers reduced motion');\n * }\n * });\n * ```\n */\nexport const prefersReducedMotion = (): MediaPreferenceSignal<boolean> => {\n return createMediaSignal('(prefers-reduced-motion: reduce)', false);\n};\n\n/**\n * Returns a reactive signal tracking the user's preferred color scheme.\n *\n * Tracks the `(prefers-color-scheme: dark)` media query. Returns `'dark'`\n * when the user prefers a dark color scheme, `'light'` otherwise.\n *\n * @returns A readonly reactive signal handle with `'light'` or `'dark'`\n *\n * @example\n * ```ts\n * import { prefersColorScheme } from '@bquery/bquery/a11y';\n * import { effect } from '@bquery/bquery/reactive';\n *\n * const scheme = prefersColorScheme();\n * effect(() => {\n * document.body.dataset.theme = scheme.value;\n * });\n * ```\n */\nexport const prefersColorScheme = (): MediaPreferenceSignal<ColorScheme> => {\n const s = signal<ColorScheme>('light');\n let destroy = (): void => {\n s.dispose();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n try {\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n s.value = mql.matches ? 'dark' : 'light';\n\n const handler = (e: MediaQueryListEvent | MediaQueryList): void => {\n s.value = e.matches ? 'dark' : 'light';\n };\n\n const cleanupMql = bindMediaQueryListener(mql, handler);\n if (cleanupMql) {\n destroy = (): void => {\n cleanupMql();\n s.dispose();\n };\n }\n } catch {\n // matchMedia may throw in non-browser environments\n }\n }\n\n return withDestroy(readonly(s), destroy);\n};\n\n/**\n * Returns a reactive signal tracking the user's contrast preference.\n *\n * Tracks the `(prefers-contrast)` media query. Returns:\n * - `'more'` — user prefers higher contrast\n * - `'less'` — user prefers lower contrast\n * - `'custom'` — user has set a custom contrast level\n * - `'no-preference'` — no explicit preference\n *\n * @returns A readonly reactive signal handle\n *\n * @example\n * ```ts\n * import { prefersContrast } from '@bquery/bquery/a11y';\n * import { effect } from '@bquery/bquery/reactive';\n *\n * const contrast = prefersContrast();\n * effect(() => {\n * if (contrast.value === 'more') {\n * document.body.classList.add('high-contrast');\n * }\n * });\n * ```\n */\nexport const prefersContrast = (): MediaPreferenceSignal<ContrastPreference> => {\n const s = signal<ContrastPreference>('no-preference');\n let destroy = (): void => {\n s.dispose();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n let mql: MediaQueryList | undefined;\n let mqlLess: MediaQueryList | undefined;\n let mqlCustom: MediaQueryList | undefined;\n\n const update = (): void => {\n // Defensive guard for environments where matchMedia setup fails before\n // listeners are attached; update() is only expected to run after init.\n if (!mql || !mqlLess || !mqlCustom) {\n return;\n }\n\n if (mql.matches) {\n s.value = 'more';\n } else if (mqlLess.matches) {\n s.value = 'less';\n } else if (mqlCustom.matches) {\n s.value = 'custom';\n } else {\n s.value = 'no-preference';\n }\n };\n\n // Listen for changes on the contrast preference variants\n try {\n mql = window.matchMedia('(prefers-contrast: more)');\n mqlLess = window.matchMedia('(prefers-contrast: less)');\n mqlCustom = window.matchMedia('(prefers-contrast: custom)');\n update();\n const cleanupFns = [mql, mqlLess, mqlCustom]\n .map((entry) =>\n bindMediaQueryListener(entry, () => {\n update();\n })\n )\n .filter((cleanup): cleanup is () => void => cleanup !== undefined);\n\n if (cleanupFns.length > 0) {\n destroy = (): void => {\n for (const cleanup of cleanupFns) {\n cleanup();\n }\n s.dispose();\n };\n }\n } catch {\n // matchMedia may throw in non-browser environments\n }\n }\n\n return withDestroy(readonly(s), destroy);\n};\n","/**\n * Roving tab index utility for arrow-key navigation within groups.\n *\n * Implements the WAI-ARIA roving tabindex pattern: only one item\n * in the group has tabindex=\"0\" (the active item), while all others\n * have tabindex=\"-1\". Arrow keys move focus between items.\n *\n * @module bquery/a11y\n */\n\nimport type { RovingTabIndexHandle, RovingTabIndexOptions } from './types';\n\n/**\n * Sets up roving tab index navigation for a group of elements.\n *\n * Only the active item receives `tabindex=\"0\"`, making it the only\n * tabbable element in the group. Arrow keys move focus between items,\n * and Home/End jump to the first/last item.\n *\n * @param container - The parent element containing the navigable items\n * @param itemSelector - CSS selector for the navigable items within the container\n * @param options - Configuration options\n * @returns A handle with `destroy()`, `focusItem()`, and `activeIndex()`\n *\n * @example\n * ```ts\n * import { rovingTabIndex } from '@bquery/bquery/a11y';\n *\n * const toolbar = document.querySelector('[role=\"toolbar\"]');\n * const handle = rovingTabIndex(toolbar, 'button', {\n * orientation: 'horizontal',\n * wrap: true,\n * });\n *\n * // Later, clean up\n * handle.destroy();\n * ```\n */\nexport const rovingTabIndex = (\n container: HTMLElement,\n itemSelector: string,\n options: RovingTabIndexOptions = {}\n): RovingTabIndexHandle => {\n const { wrap = true, orientation = 'vertical', onActivate } = options;\n\n let currentIndex = 0;\n const originalTabIndexes = new Map<HTMLElement, string | null>();\n\n const getItems = (): HTMLElement[] => {\n return Array.from(container.querySelectorAll(itemSelector)) as HTMLElement[];\n };\n\n const trackItems = (items: HTMLElement[]): void => {\n for (const item of items) {\n if (!originalTabIndexes.has(item)) {\n originalTabIndexes.set(item, item.getAttribute('tabindex'));\n }\n }\n };\n\n const setActiveItem = (index: number): void => {\n const items = getItems();\n if (items.length === 0) return;\n trackItems(items);\n\n // Clamp index\n const clampedIndex = Math.max(0, Math.min(index, items.length - 1));\n\n // Update tabindex on all items\n for (let i = 0; i < items.length; i++) {\n items[i].setAttribute('tabindex', i === clampedIndex ? '0' : '-1');\n }\n\n currentIndex = clampedIndex;\n items[clampedIndex].focus();\n onActivate?.(items[clampedIndex], clampedIndex);\n };\n\n const isRelevantKey = (key: string): boolean => {\n if (key === 'Home' || key === 'End') return true;\n\n switch (orientation) {\n case 'horizontal':\n return key === 'ArrowLeft' || key === 'ArrowRight';\n case 'vertical':\n return key === 'ArrowUp' || key === 'ArrowDown';\n case 'both':\n return (\n key === 'ArrowLeft' || key === 'ArrowRight' || key === 'ArrowUp' || key === 'ArrowDown'\n );\n default:\n return false;\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent): void => {\n if (!isRelevantKey(event.key)) return;\n\n const items = getItems();\n if (items.length === 0) return;\n\n event.preventDefault();\n\n let nextIndex = currentIndex;\n\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n nextIndex = currentIndex + 1;\n if (nextIndex >= items.length) {\n nextIndex = wrap ? 0 : items.length - 1;\n }\n break;\n\n case 'ArrowUp':\n case 'ArrowLeft':\n nextIndex = currentIndex - 1;\n if (nextIndex < 0) {\n nextIndex = wrap ? items.length - 1 : 0;\n }\n break;\n\n case 'Home':\n nextIndex = 0;\n break;\n\n case 'End':\n nextIndex = items.length - 1;\n break;\n }\n\n setActiveItem(nextIndex);\n };\n\n // Initialize: set tabindex on all items\n const items = getItems();\n trackItems(items);\n for (let i = 0; i < items.length; i++) {\n items[i].setAttribute('tabindex', i === 0 ? '0' : '-1');\n }\n\n container.addEventListener('keydown', handleKeyDown);\n\n return {\n destroy: () => {\n container.removeEventListener('keydown', handleKeyDown);\n // Restore original tabindex values\n for (const [item, originalTabIndex] of originalTabIndexes) {\n if (originalTabIndex === null) {\n item.removeAttribute('tabindex');\n } else {\n item.setAttribute('tabindex', originalTabIndex);\n }\n }\n originalTabIndexes.clear();\n },\n\n focusItem: (index: number) => {\n setActiveItem(index);\n },\n\n activeIndex: () => currentIndex,\n };\n};\n","/**\n * Auto-generated skip navigation link utility.\n *\n * Creates a visually-hidden (but keyboard-focusable) \"Skip to content\"\n * link that becomes visible on focus, letting keyboard users bypass\n * repeated navigation.\n *\n * @module bquery/a11y\n */\n\nimport type { SkipLinkHandle, SkipLinkOptions } from './types';\n\n/** Default CSS for the skip link — visually hidden until focused. */\nconst DEFAULT_STYLES = `\n position: absolute;\n top: -9999px;\n left: -9999px;\n z-index: 999999;\n padding: 0.5em 1em;\n background: #000;\n color: #fff;\n font-size: 1rem;\n text-decoration: none;\n border-radius: 0 0 4px 0;\n outline: 2px solid #4A90D9;\n outline-offset: 2px;\n`;\n\nconst FOCUSED_STYLES = `\n top: 0;\n left: 0;\n`;\n\nlet skipTargetIdCounter = 0;\nconst generatedSkipTargetRefs = new Map<string, { count: number; target: HTMLElement }>();\n\nconst hasSkipLinkEnvironment = (): boolean => {\n if (typeof document === 'undefined') {\n return false;\n }\n\n return (\n typeof document.createElement === 'function' &&\n typeof document.querySelector === 'function' &&\n typeof document.getElementById === 'function' &&\n document.body !== null &&\n document.body !== undefined\n );\n};\n\nconst createNoopSkipLinkHandle = (): SkipLinkHandle => ({\n destroy: () => {},\n element: null,\n});\n\n/**\n * Creates a skip navigation link that jumps to the specified target.\n *\n * The link is visually hidden by default and becomes visible when\n * it receives keyboard focus. This follows the WCAG 2.4.1 \"Bypass Blocks\"\n * success criterion.\n *\n * @param targetSelector - CSS selector for the main content area (e.g. `'#main'`, `'main'`)\n * @param options - Configuration options\n * @returns A handle with `destroy()` method and reference to the created element\n *\n * @example\n * ```ts\n * import { skipLink } from '@bquery/bquery/a11y';\n *\n * // Creates a \"Skip to main content\" link pointing to <main>\n * const handle = skipLink('#main-content');\n *\n * // Custom text\n * const handle2 = skipLink('#content', { text: 'Jump to content' });\n *\n * // Remove when no longer needed\n * handle.destroy();\n * ```\n */\nexport const skipLink = (targetSelector: string, options: SkipLinkOptions = {}): SkipLinkHandle => {\n if (!hasSkipLinkEnvironment()) {\n return createNoopSkipLinkHandle();\n }\n\n const { text = 'Skip to main content', className = 'bq-skip-link' } = options;\n let trackedGeneratedTargetId: string | undefined;\n let trackedFocusTarget:\n | { target: HTMLElement; hadTabIndex: boolean; previousTabIndex: string | null }\n | undefined;\n const safeQuerySelector = (selector: string): HTMLElement | null => {\n try {\n return document.querySelector(selector) as HTMLElement | null;\n } catch {\n return null;\n }\n };\n const releaseTrackedGeneratedTargetId = (): void => {\n if (!trackedGeneratedTargetId) return;\n\n const entry = generatedSkipTargetRefs.get(trackedGeneratedTargetId);\n const remainingRefs = (entry?.count ?? 0) - 1;\n if (remainingRefs <= 0) {\n generatedSkipTargetRefs.delete(trackedGeneratedTargetId);\n if (entry?.target.isConnected && entry.target.id === trackedGeneratedTargetId) {\n entry.target.removeAttribute('id');\n }\n } else {\n generatedSkipTargetRefs.set(trackedGeneratedTargetId, {\n count: remainingRefs,\n target: entry!.target,\n });\n }\n\n trackedGeneratedTargetId = undefined;\n };\n const restoreTrackedFocusTarget = (): void => {\n if (!trackedFocusTarget) return;\n\n const { target, hadTabIndex, previousTabIndex } = trackedFocusTarget;\n if (target.isConnected) {\n if (hadTabIndex) {\n target.setAttribute('tabindex', previousTabIndex ?? '');\n } else {\n target.removeAttribute('tabindex');\n }\n }\n\n trackedFocusTarget = undefined;\n };\n const ensureTargetFocusable = (target: HTMLElement): void => {\n if (trackedFocusTarget?.target === target) {\n return;\n }\n\n restoreTrackedFocusTarget();\n\n if (target.hasAttribute('tabindex')) {\n trackedFocusTarget = {\n target,\n hadTabIndex: true,\n previousTabIndex: target.getAttribute('tabindex'),\n };\n return;\n }\n\n if (target.tabIndex !== -1) {\n return;\n }\n\n trackedFocusTarget = {\n target,\n hadTabIndex: false,\n previousTabIndex: null,\n };\n target.setAttribute('tabindex', '-1');\n };\n const trackGeneratedTargetId = (target: HTMLElement, id: string): void => {\n if (trackedGeneratedTargetId === id) return;\n releaseTrackedGeneratedTargetId();\n const entry = generatedSkipTargetRefs.get(id);\n generatedSkipTargetRefs.set(id, {\n count: (entry?.count ?? 0) + 1,\n target,\n });\n trackedGeneratedTargetId = id;\n };\n const resolveTarget = (): HTMLElement | null => {\n if (targetSelector.startsWith('#')) {\n const id = targetSelector.slice(1);\n const byId = id ? (document.getElementById(id) as HTMLElement | null) : null;\n if (byId) {\n return byId;\n }\n\n return safeQuerySelector(targetSelector);\n }\n\n const byId = document.getElementById(targetSelector) as HTMLElement | null;\n if (byId) {\n return byId;\n }\n\n return safeQuerySelector(targetSelector);\n };\n\n const ensureTargetId = (target: HTMLElement): string => {\n if (target.id) {\n const generatedTarget = generatedSkipTargetRefs.get(target.id);\n if (generatedTarget?.target === target) {\n trackGeneratedTargetId(target, target.id);\n }\n return target.id;\n }\n\n let generatedTargetId: string;\n do {\n skipTargetIdCounter += 1;\n generatedTargetId = `bq-skip-target-${skipTargetIdCounter}`;\n } while (document.getElementById(generatedTargetId) !== null);\n\n target.id = generatedTargetId;\n trackGeneratedTargetId(target, generatedTargetId);\n return generatedTargetId;\n };\n\n const link = document.createElement('a');\n const initialTarget = resolveTarget();\n link.href = targetSelector.startsWith('#')\n ? targetSelector\n : initialTarget\n ? `#${ensureTargetId(initialTarget)}`\n : `#${targetSelector}`;\n link.textContent = text;\n link.className = className;\n link.setAttribute('style', DEFAULT_STYLES);\n\n link.addEventListener('focus', () => {\n link.setAttribute('style', DEFAULT_STYLES + FOCUSED_STYLES);\n });\n\n link.addEventListener('blur', () => {\n link.setAttribute('style', DEFAULT_STYLES);\n });\n\n link.addEventListener('click', (e) => {\n e.preventDefault();\n\n const target = resolveTarget();\n if (!target) {\n return;\n }\n\n link.href = `#${ensureTargetId(target)}`;\n // Make the target focusable if it isn't already\n ensureTargetFocusable(target);\n target.focus();\n });\n\n // Insert as the first child of <body>\n if (document.body.firstChild) {\n document.body.insertBefore(link, document.body.firstChild);\n } else {\n document.body.appendChild(link);\n }\n\n return {\n destroy: () => {\n restoreTrackedFocusTarget();\n releaseTrackedGeneratedTargetId();\n link.remove();\n },\n element: link,\n };\n};\n","/**\n * Focus trapping utility for modals, dialogs, and popover content.\n *\n * Constrains keyboard focus within a container so that Tab and Shift+Tab\n * cycle only through the container's focusable elements.\n *\n * @module bquery/a11y\n */\n\nimport type { FocusTrapHandle, TrapFocusOptions } from './types';\n\n/** Selector for elements that can receive focus. */\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n 'details > summary',\n 'audio[controls]',\n 'video[controls]',\n].join(', ');\n\n/**\n * Gets all focusable elements within a container.\n *\n * @param container - The container element\n * @returns Array of focusable elements\n * @internal\n */\nexport const getFocusableElements = (container: Element): HTMLElement[] => {\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)) as HTMLElement[];\n return elements.filter(\n (el) => !el.hasAttribute('disabled') && el.tabIndex !== -1 && el.getClientRects().length > 0\n );\n};\n\n/**\n * Resolves an element from a string selector or returns the element directly.\n * @internal\n */\nconst resolveElement = (\n target: HTMLElement | string | undefined,\n container: Element\n): HTMLElement | null => {\n if (!target) return null;\n if (typeof target === 'string') {\n return container.querySelector(target) as HTMLElement | null;\n }\n return target;\n};\n\n/**\n * Traps keyboard focus within a container element.\n *\n * When activated, Tab and Shift+Tab will cycle only through focusable\n * elements within the container. Useful for modals, dialogs, and\n * dropdown menus.\n *\n * @param container - The DOM element to trap focus within\n * @param options - Configuration options\n * @returns A handle with a `release()` method to deactivate the trap\n *\n * @example\n * ```ts\n * import { trapFocus } from '@bquery/bquery/a11y';\n *\n * const dialog = document.querySelector('#my-dialog');\n * const trap = trapFocus(dialog, { escapeDeactivates: true });\n *\n * // Later, release the trap\n * trap.release();\n * ```\n */\nexport const trapFocus = (\n container: HTMLElement,\n options: TrapFocusOptions = {}\n): FocusTrapHandle => {\n const { escapeDeactivates = true, onEscape, initialFocus, returnFocus } = options;\n\n if (\n typeof document === 'undefined' ||\n typeof document.addEventListener !== 'function' ||\n typeof document.removeEventListener !== 'function'\n ) {\n let active = false;\n return {\n get active() {\n return active;\n },\n release: () => {\n active = false;\n },\n };\n }\n\n const previouslyFocused = document.activeElement as HTMLElement | null;\n let active = true;\n\n const handleKeyDown = (event: KeyboardEvent): void => {\n if (!active) return;\n\n if (event.key === 'Escape' && escapeDeactivates) {\n event.preventDefault();\n handle.release();\n onEscape?.();\n return;\n }\n\n if (event.key !== 'Tab') return;\n\n const focusable = getFocusableElements(container);\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (event.shiftKey) {\n // Shift+Tab: if at first element, wrap to last\n if (document.activeElement === first || !container.contains(document.activeElement)) {\n event.preventDefault();\n last.focus();\n }\n } else {\n // Tab: if at last element, wrap to first\n if (document.activeElement === last || !container.contains(document.activeElement)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n // Attach the event listener\n document.addEventListener('keydown', handleKeyDown, true);\n\n // Set initial focus\n const initialEl = resolveElement(initialFocus, container);\n if (initialEl) {\n initialEl.focus();\n } else {\n const focusable = getFocusableElements(container);\n if (focusable.length > 0) {\n focusable[0].focus();\n }\n }\n\n const handle: FocusTrapHandle = {\n get active() {\n return active;\n },\n\n release: () => {\n if (!active) return;\n active = false;\n document.removeEventListener('keydown', handleKeyDown, true);\n\n // Return focus\n const returnEl = resolveElement(returnFocus, document.body);\n if (returnEl) {\n returnEl.focus();\n } else if (previouslyFocused && previouslyFocused.focus) {\n previouslyFocused.focus();\n }\n },\n };\n\n return handle;\n};\n\n/**\n * Releases a focus trap handle.\n * This is a convenience function — in most cases, use the `release()`\n * method on the individual trap handle directly.\n *\n * @deprecated Prefer using the handle returned by `trapFocus()` directly.\n */\nexport const releaseFocus = (handle: FocusTrapHandle): void => {\n handle.release();\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"a11y-IV_bfLyn.js","names":[],"sources":["../src/a11y/announce.ts","../src/a11y/audit.ts","../src/a11y/media-preferences.ts","../src/a11y/roving-tab-index.ts","../src/a11y/skip-link.ts","../src/a11y/trap-focus.ts"],"sourcesContent":["/**\n * Screen reader announcement utility using ARIA live regions.\n *\n * Creates and manages off-screen live regions to announce dynamic\n * content changes to assistive technologies.\n *\n * @module bquery/a11y\n */\n\nimport type { AnnouncePriority } from './types';\n\n/** Cache for live region containers, keyed by priority. */\nconst liveRegions = new Map<AnnouncePriority, HTMLElement>();\nconst pendingAnnouncements = new Map<AnnouncePriority, ReturnType<typeof setTimeout>>();\n\n/**\n * Delay in milliseconds before updating the live region text.\n * This ensures screen readers detect the content change even when\n * the same message is announced consecutively — clearing first and\n * setting after a short timer delay forces a new live-region mutation event.\n * @internal\n */\nconst ANNOUNCEMENT_DELAY_MS = 50;\n\n/**\n * Gets or creates a visually-hidden ARIA live region for the given priority.\n *\n * @param priority - The aria-live priority level\n * @returns The live region element\n * @internal\n */\nconst getOrCreateLiveRegion = (priority: AnnouncePriority): HTMLElement => {\n const existing = liveRegions.get(priority);\n if (existing && existing.isConnected) {\n return existing;\n }\n\n const el = document.createElement('div');\n el.setAttribute('aria-live', priority);\n el.setAttribute('aria-atomic', 'true');\n el.setAttribute('role', priority === 'assertive' ? 'alert' : 'status');\n\n // Visually hidden but accessible to screen readers\n Object.assign(el.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n });\n\n document.body.appendChild(el);\n liveRegions.set(priority, el);\n\n return el;\n};\n\n/**\n * Announces a message to screen readers via an ARIA live region.\n *\n * The message is injected into a visually-hidden live region element.\n * Screen readers will pick up the change and announce it to the user.\n *\n * @param message - The text message to announce\n * @param priority - The urgency level: `'polite'` (default) or `'assertive'`\n *\n * @example\n * ```ts\n * import { announceToScreenReader } from '@bquery/bquery/a11y';\n *\n * // Polite announcement (waits for idle)\n * announceToScreenReader('3 search results found');\n *\n * // Assertive announcement (interrupts current speech)\n * announceToScreenReader('Error: Please fix the form', 'assertive');\n * ```\n */\nexport const announceToScreenReader = (\n message: string,\n priority: AnnouncePriority = 'polite'\n): void => {\n if (!message) return;\n if (typeof document === 'undefined' || !document.body) return;\n\n const region = getOrCreateLiveRegion(priority);\n const pendingTimeout = pendingAnnouncements.get(priority);\n if (pendingTimeout !== undefined) {\n clearTimeout(pendingTimeout);\n }\n\n // Clear first, then set after a short timer delay to ensure screen readers\n // detect the change even if the same message is announced twice.\n region.textContent = '';\n\n // Use setTimeout to ensure the DOM update triggers a live region change event\n const timeout = setTimeout(() => {\n pendingAnnouncements.delete(priority);\n if (region.isConnected) {\n region.textContent = message;\n }\n }, ANNOUNCEMENT_DELAY_MS);\n\n pendingAnnouncements.set(priority, timeout);\n};\n\n/**\n * Removes all live region elements created by `announceToScreenReader`.\n * Useful for cleanup in tests or when unmounting an application.\n *\n * @example\n * ```ts\n * import { clearAnnouncements } from '@bquery/bquery/a11y';\n *\n * clearAnnouncements();\n * ```\n */\nexport const clearAnnouncements = (): void => {\n for (const timeout of pendingAnnouncements.values()) {\n clearTimeout(timeout);\n }\n pendingAnnouncements.clear();\n\n for (const [, el] of liveRegions) {\n el.remove();\n }\n liveRegions.clear();\n};\n","/**\n * Development-time accessibility audit utility.\n *\n * Scans DOM elements for common accessibility issues such as missing\n * alt text on images, missing labels on form inputs, empty links/buttons,\n * and incorrect ARIA usage.\n *\n * @module bquery/a11y\n */\n\nimport type { AuditFinding, AuditResult, AuditSeverity } from './types';\n\n/**\n * Creates a finding object.\n * @internal\n */\nconst finding = (\n severity: AuditSeverity,\n message: string,\n element: Element,\n rule: string\n): AuditFinding => ({\n severity,\n message,\n element,\n rule,\n});\n\n/**\n * Checks images for missing alt attributes.\n * @internal\n */\nconst auditImages = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n const images = container.querySelectorAll('img');\n\n for (const img of images) {\n if (!img.hasAttribute('alt')) {\n findings.push(\n finding(\n 'error',\n 'Image is missing an alt attribute. Add alt=\"\" for decorative images or a descriptive alt text.',\n img,\n 'img-alt'\n )\n );\n } else if (img.getAttribute('alt') === '' && !img.hasAttribute('role')) {\n findings.push(\n finding(\n 'info',\n 'Image has empty alt text. Consider adding role=\"presentation\" if decorative.',\n img,\n 'img-decorative'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Checks form inputs for missing labels.\n * @internal\n */\nconst auditFormInputs = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n const inputs = container.querySelectorAll('input, select, textarea');\n\n for (const input of inputs) {\n const type = input.getAttribute('type');\n\n // Hidden, submit, and button inputs don't need labels\n if (type === 'hidden' || type === 'submit' || type === 'button' || type === 'reset') {\n continue;\n }\n\n const id = input.getAttribute('id');\n const hasLabel = id ? !!container.querySelector(`label[for=\"${id}\"]`) : false;\n const hasAriaLabel = input.hasAttribute('aria-label') || input.hasAttribute('aria-labelledby');\n const hasTitle = input.hasAttribute('title');\n const isWrappedInLabel = input.closest('label') !== null;\n\n if (!hasLabel && !hasAriaLabel && !hasTitle && !isWrappedInLabel) {\n findings.push(\n finding(\n 'error',\n `Form input is missing a label. Add a <label for=\"id\">, aria-label, or aria-labelledby attribute.`,\n input,\n 'input-label'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Checks for empty interactive elements (buttons, links).\n * @internal\n */\nconst auditInteractiveElements = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n\n // Check buttons\n const buttons = container.querySelectorAll('button');\n for (const btn of buttons) {\n const hasText = (btn.textContent ?? '').trim().length > 0;\n const hasAriaLabel = btn.hasAttribute('aria-label') || btn.hasAttribute('aria-labelledby');\n const hasTitle = btn.hasAttribute('title');\n\n if (!hasText && !hasAriaLabel && !hasTitle) {\n findings.push(\n finding(\n 'error',\n 'Button has no accessible name. Add text content, aria-label, or title.',\n btn,\n 'button-name'\n )\n );\n }\n }\n\n // Check links\n const links = container.querySelectorAll('a[href]');\n for (const link of links) {\n const hasText = (link.textContent ?? '').trim().length > 0;\n const hasAriaLabel = link.hasAttribute('aria-label') || link.hasAttribute('aria-labelledby');\n const hasTitle = link.hasAttribute('title');\n const hasImage = link.querySelector('img[alt]') !== null;\n\n if (!hasText && !hasAriaLabel && !hasTitle && !hasImage) {\n findings.push(\n finding(\n 'error',\n 'Link has no accessible name. Add text content, aria-label, or title.',\n link,\n 'link-name'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Checks heading hierarchy for skipped levels.\n * @internal\n */\nconst auditHeadings = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n const headings = container.querySelectorAll('h1, h2, h3, h4, h5, h6');\n\n let previousLevel = 0;\n\n for (const heading of headings) {\n const level = parseInt(heading.tagName.charAt(1), 10);\n\n if (previousLevel > 0 && level > previousLevel + 1) {\n findings.push(\n finding(\n 'warning',\n `Heading level skipped: <${heading.tagName.toLowerCase()}> follows <h${previousLevel}>. Don't skip heading levels.`,\n heading,\n 'heading-order'\n )\n );\n }\n\n if ((heading.textContent ?? '').trim().length === 0) {\n findings.push(finding('warning', 'Heading element is empty.', heading, 'heading-empty'));\n }\n\n previousLevel = level;\n }\n\n return findings;\n};\n\n/**\n * Checks for valid ARIA attribute usage.\n * @internal\n */\nconst auditAria = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n\n // Check aria-labelledby references exist\n const labelled = container.querySelectorAll('[aria-labelledby]');\n for (const el of labelled) {\n const ids = (el.getAttribute('aria-labelledby') ?? '').split(/\\s+/);\n for (const id of ids) {\n if (id && !document.getElementById(id)) {\n findings.push(\n finding(\n 'error',\n `aria-labelledby references \"${id}\" which does not exist in the document.`,\n el,\n 'aria-labelledby-ref'\n )\n );\n }\n }\n }\n\n // Check aria-describedby references exist\n const described = container.querySelectorAll('[aria-describedby]');\n for (const el of described) {\n const ids = (el.getAttribute('aria-describedby') ?? '').split(/\\s+/);\n for (const id of ids) {\n if (id && !document.getElementById(id)) {\n findings.push(\n finding(\n 'error',\n `aria-describedby references \"${id}\" which does not exist in the document.`,\n el,\n 'aria-describedby-ref'\n )\n );\n }\n }\n }\n\n return findings;\n};\n\n/**\n * Checks for sufficient document landmarks.\n * @internal\n */\nconst auditLandmarks = (container: Element): AuditFinding[] => {\n const findings: AuditFinding[] = [];\n\n // Only audit the document body or top-level container\n if (container === document.body || container === document.documentElement) {\n const hasMain = !!container.querySelector('main') || !!container.querySelector('[role=\"main\"]');\n\n if (!hasMain) {\n findings.push(\n finding(\n 'warning',\n 'Page is missing a <main> landmark. Add <main> or role=\"main\" to the primary content area.',\n container,\n 'landmark-main'\n )\n );\n }\n }\n\n return findings;\n};\n\n/**\n * Runs a development-time accessibility audit on a container element.\n *\n * Checks for common accessibility issues including:\n * - Missing alt text on images\n * - Missing labels on form inputs\n * - Empty buttons and links\n * - Heading hierarchy issues\n * - Invalid ARIA references\n * - Missing document landmarks\n *\n * This is intended as a development tool — not a replacement for\n * manual testing or professional accessibility audits.\n *\n * @param container - The element to audit (defaults to `document.body`)\n * @returns An audit result with findings, counts, and pass/fail status\n *\n * @example\n * ```ts\n * import { auditA11y } from '@bquery/bquery/a11y';\n *\n * const result = auditA11y();\n * if (!result.passed) {\n * console.warn(`Found ${result.errors} accessibility errors:`);\n * for (const f of result.findings) {\n * console.warn(`[${f.severity}] ${f.message}`, f.element);\n * }\n * }\n * ```\n */\nexport const auditA11y = (container?: Element): AuditResult => {\n if (typeof document === 'undefined' || !document.body) {\n return {\n findings: [],\n errors: 0,\n warnings: 0,\n passed: true,\n };\n }\n\n const target = container ?? document.body;\n\n const allFindings: AuditFinding[] = [\n ...auditImages(target),\n ...auditFormInputs(target),\n ...auditInteractiveElements(target),\n ...auditHeadings(target),\n ...auditAria(target),\n ...auditLandmarks(target),\n ];\n\n const errors = allFindings.filter((f) => f.severity === 'error').length;\n const warnings = allFindings.filter((f) => f.severity === 'warning').length;\n\n return {\n findings: allFindings,\n errors,\n warnings,\n passed: errors === 0,\n };\n};\n","/**\n * Reactive media preference signals for accessibility.\n *\n * Provides reactive signals that track the user's system-level\n * accessibility preferences (reduced motion, color scheme, contrast).\n *\n * @module bquery/a11y\n */\n\nimport { readonly, signal, type ReadonlySignal } from '../reactive/index';\nimport type { ColorScheme, ContrastPreference, MediaPreferenceSignal } from './types';\n\ntype LegacyMediaQueryList = MediaQueryList & {\n addListener?: (listener: (event: MediaQueryListEvent | MediaQueryList) => void) => void;\n removeListener?: (listener: (event: MediaQueryListEvent | MediaQueryList) => void) => void;\n};\n\nconst bindMediaQueryListener = (\n mql: MediaQueryList,\n handler: (event: MediaQueryListEvent | MediaQueryList) => void\n): (() => void) | undefined => {\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', handler);\n return (): void => {\n mql.removeEventListener('change', handler);\n };\n }\n\n const legacyMql = mql as LegacyMediaQueryList;\n if (typeof legacyMql.addListener === 'function') {\n legacyMql.addListener(handler);\n return (): void => {\n legacyMql.removeListener?.(handler);\n };\n }\n\n return undefined;\n};\n\nconst withDestroy = <T>(\n signalHandle: ReadonlySignal<T>,\n cleanup: () => void\n): MediaPreferenceSignal<T> => {\n let destroyImpl = cleanup;\n const handle = signalHandle as MediaPreferenceSignal<T>;\n Object.defineProperty(handle, 'destroy', {\n configurable: true,\n enumerable: false,\n value: (): void => {\n const currentDestroy = destroyImpl;\n // Make cleanup idempotent so repeated destroy() calls from user code stay safe.\n destroyImpl = (): void => {};\n currentDestroy();\n },\n });\n return handle;\n};\n\n/**\n * Creates a reactive signal that tracks a CSS media query.\n *\n * @param query - The media query string\n * @param initialValue - Fallback value when `matchMedia` is unavailable\n * @returns A readonly signal handle that updates when the query match changes\n * @internal\n */\nconst createMediaSignal = (\n query: string,\n initialValue: boolean\n): MediaPreferenceSignal<boolean> => {\n const s = signal(initialValue);\n let destroy = (): void => {\n s.dispose();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n try {\n const mql = window.matchMedia(query);\n s.value = mql.matches;\n\n const handler = (e: MediaQueryListEvent | MediaQueryList): void => {\n s.value = e.matches;\n };\n\n const cleanupMql = bindMediaQueryListener(mql, handler);\n if (cleanupMql) {\n destroy = (): void => {\n cleanupMql();\n s.dispose();\n };\n }\n } catch {\n // matchMedia may throw in non-browser environments\n }\n }\n\n return withDestroy(readonly(s), destroy);\n};\n\n/**\n * Returns a reactive signal indicating whether the user prefers reduced motion.\n *\n * Tracks the `(prefers-reduced-motion: reduce)` media query. Returns `true`\n * when the user has requested reduced motion in their system settings.\n *\n * @returns A readonly reactive signal handle. Call `destroy()` to remove listeners.\n *\n * @example\n * ```ts\n * import { prefersReducedMotion } from '@bquery/bquery/a11y';\n * import { effect } from '@bquery/bquery/reactive';\n *\n * const reduced = prefersReducedMotion();\n * effect(() => {\n * if (reduced.value) {\n * console.log('User prefers reduced motion');\n * }\n * });\n * ```\n */\nexport const prefersReducedMotion = (): MediaPreferenceSignal<boolean> => {\n return createMediaSignal('(prefers-reduced-motion: reduce)', false);\n};\n\n/**\n * Returns a reactive signal tracking the user's preferred color scheme.\n *\n * Tracks the `(prefers-color-scheme: dark)` media query. Returns `'dark'`\n * when the user prefers a dark color scheme, `'light'` otherwise.\n *\n * @returns A readonly reactive signal handle with `'light'` or `'dark'`\n *\n * @example\n * ```ts\n * import { prefersColorScheme } from '@bquery/bquery/a11y';\n * import { effect } from '@bquery/bquery/reactive';\n *\n * const scheme = prefersColorScheme();\n * effect(() => {\n * document.body.dataset.theme = scheme.value;\n * });\n * ```\n */\nexport const prefersColorScheme = (): MediaPreferenceSignal<ColorScheme> => {\n const s = signal<ColorScheme>('light');\n let destroy = (): void => {\n s.dispose();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n try {\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n s.value = mql.matches ? 'dark' : 'light';\n\n const handler = (e: MediaQueryListEvent | MediaQueryList): void => {\n s.value = e.matches ? 'dark' : 'light';\n };\n\n const cleanupMql = bindMediaQueryListener(mql, handler);\n if (cleanupMql) {\n destroy = (): void => {\n cleanupMql();\n s.dispose();\n };\n }\n } catch {\n // matchMedia may throw in non-browser environments\n }\n }\n\n return withDestroy(readonly(s), destroy);\n};\n\n/**\n * Returns a reactive signal tracking the user's contrast preference.\n *\n * Tracks the `(prefers-contrast)` media query. Returns:\n * - `'more'` — user prefers higher contrast\n * - `'less'` — user prefers lower contrast\n * - `'custom'` — user has set a custom contrast level\n * - `'no-preference'` — no explicit preference\n *\n * @returns A readonly reactive signal handle\n *\n * @example\n * ```ts\n * import { prefersContrast } from '@bquery/bquery/a11y';\n * import { effect } from '@bquery/bquery/reactive';\n *\n * const contrast = prefersContrast();\n * effect(() => {\n * if (contrast.value === 'more') {\n * document.body.classList.add('high-contrast');\n * }\n * });\n * ```\n */\nexport const prefersContrast = (): MediaPreferenceSignal<ContrastPreference> => {\n const s = signal<ContrastPreference>('no-preference');\n let destroy = (): void => {\n s.dispose();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n let mql: MediaQueryList | undefined;\n let mqlLess: MediaQueryList | undefined;\n let mqlCustom: MediaQueryList | undefined;\n\n const update = (): void => {\n // Defensive guard for environments where matchMedia setup fails before\n // listeners are attached; update() is only expected to run after init.\n if (!mql || !mqlLess || !mqlCustom) {\n return;\n }\n\n if (mql.matches) {\n s.value = 'more';\n } else if (mqlLess.matches) {\n s.value = 'less';\n } else if (mqlCustom.matches) {\n s.value = 'custom';\n } else {\n s.value = 'no-preference';\n }\n };\n\n // Listen for changes on the contrast preference variants\n try {\n mql = window.matchMedia('(prefers-contrast: more)');\n mqlLess = window.matchMedia('(prefers-contrast: less)');\n mqlCustom = window.matchMedia('(prefers-contrast: custom)');\n update();\n const cleanupFns = [mql, mqlLess, mqlCustom]\n .map((entry) =>\n bindMediaQueryListener(entry, () => {\n update();\n })\n )\n .filter((cleanup): cleanup is () => void => cleanup !== undefined);\n\n if (cleanupFns.length > 0) {\n destroy = (): void => {\n for (const cleanup of cleanupFns) {\n cleanup();\n }\n s.dispose();\n };\n }\n } catch {\n // matchMedia may throw in non-browser environments\n }\n }\n\n return withDestroy(readonly(s), destroy);\n};\n","/**\n * Roving tab index utility for arrow-key navigation within groups.\n *\n * Implements the WAI-ARIA roving tabindex pattern: only one item\n * in the group has tabindex=\"0\" (the active item), while all others\n * have tabindex=\"-1\". Arrow keys move focus between items.\n *\n * @module bquery/a11y\n */\n\nimport type { RovingTabIndexHandle, RovingTabIndexOptions } from './types';\n\n/**\n * Sets up roving tab index navigation for a group of elements.\n *\n * Only the active item receives `tabindex=\"0\"`, making it the only\n * tabbable element in the group. Arrow keys move focus between items,\n * and Home/End jump to the first/last item.\n *\n * @param container - The parent element containing the navigable items\n * @param itemSelector - CSS selector for the navigable items within the container\n * @param options - Configuration options\n * @returns A handle with `destroy()`, `focusItem()`, and `activeIndex()`\n *\n * @example\n * ```ts\n * import { rovingTabIndex } from '@bquery/bquery/a11y';\n *\n * const toolbar = document.querySelector('[role=\"toolbar\"]');\n * const handle = rovingTabIndex(toolbar, 'button', {\n * orientation: 'horizontal',\n * wrap: true,\n * });\n *\n * // Later, clean up\n * handle.destroy();\n * ```\n */\nexport const rovingTabIndex = (\n container: HTMLElement,\n itemSelector: string,\n options: RovingTabIndexOptions = {}\n): RovingTabIndexHandle => {\n const { wrap = true, orientation = 'vertical', onActivate } = options;\n\n let currentIndex = 0;\n const originalTabIndexes = new Map<HTMLElement, string | null>();\n\n const getItems = (): HTMLElement[] => {\n return Array.from(container.querySelectorAll(itemSelector)) as HTMLElement[];\n };\n\n const trackItems = (items: HTMLElement[]): void => {\n for (const item of items) {\n if (!originalTabIndexes.has(item)) {\n originalTabIndexes.set(item, item.getAttribute('tabindex'));\n }\n }\n };\n\n const setActiveItem = (index: number): void => {\n const items = getItems();\n if (items.length === 0) return;\n trackItems(items);\n\n // Clamp index\n const clampedIndex = Math.max(0, Math.min(index, items.length - 1));\n\n // Update tabindex on all items\n for (let i = 0; i < items.length; i++) {\n items[i].setAttribute('tabindex', i === clampedIndex ? '0' : '-1');\n }\n\n currentIndex = clampedIndex;\n items[clampedIndex].focus();\n onActivate?.(items[clampedIndex], clampedIndex);\n };\n\n const isRelevantKey = (key: string): boolean => {\n if (key === 'Home' || key === 'End') return true;\n\n switch (orientation) {\n case 'horizontal':\n return key === 'ArrowLeft' || key === 'ArrowRight';\n case 'vertical':\n return key === 'ArrowUp' || key === 'ArrowDown';\n case 'both':\n return (\n key === 'ArrowLeft' || key === 'ArrowRight' || key === 'ArrowUp' || key === 'ArrowDown'\n );\n default:\n return false;\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent): void => {\n if (!isRelevantKey(event.key)) return;\n\n const items = getItems();\n if (items.length === 0) return;\n\n event.preventDefault();\n\n let nextIndex = currentIndex;\n\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n nextIndex = currentIndex + 1;\n if (nextIndex >= items.length) {\n nextIndex = wrap ? 0 : items.length - 1;\n }\n break;\n\n case 'ArrowUp':\n case 'ArrowLeft':\n nextIndex = currentIndex - 1;\n if (nextIndex < 0) {\n nextIndex = wrap ? items.length - 1 : 0;\n }\n break;\n\n case 'Home':\n nextIndex = 0;\n break;\n\n case 'End':\n nextIndex = items.length - 1;\n break;\n }\n\n setActiveItem(nextIndex);\n };\n\n // Initialize: set tabindex on all items\n const items = getItems();\n trackItems(items);\n for (let i = 0; i < items.length; i++) {\n items[i].setAttribute('tabindex', i === 0 ? '0' : '-1');\n }\n\n container.addEventListener('keydown', handleKeyDown);\n\n return {\n destroy: () => {\n container.removeEventListener('keydown', handleKeyDown);\n // Restore original tabindex values\n for (const [item, originalTabIndex] of originalTabIndexes) {\n if (originalTabIndex === null) {\n item.removeAttribute('tabindex');\n } else {\n item.setAttribute('tabindex', originalTabIndex);\n }\n }\n originalTabIndexes.clear();\n },\n\n focusItem: (index: number) => {\n setActiveItem(index);\n },\n\n activeIndex: () => currentIndex,\n };\n};\n","/**\n * Auto-generated skip navigation link utility.\n *\n * Creates a visually-hidden (but keyboard-focusable) \"Skip to content\"\n * link that becomes visible on focus, letting keyboard users bypass\n * repeated navigation.\n *\n * @module bquery/a11y\n */\n\nimport type { SkipLinkHandle, SkipLinkOptions } from './types';\n\n/** Default CSS for the skip link — visually hidden until focused. */\nconst DEFAULT_STYLES = `\n position: absolute;\n top: -9999px;\n left: -9999px;\n z-index: 999999;\n padding: 0.5em 1em;\n background: #000;\n color: #fff;\n font-size: 1rem;\n text-decoration: none;\n border-radius: 0 0 4px 0;\n outline: 2px solid #4A90D9;\n outline-offset: 2px;\n`;\n\nconst FOCUSED_STYLES = `\n top: 0;\n left: 0;\n`;\n\nlet skipTargetIdCounter = 0;\nconst generatedSkipTargetRefs = new Map<string, { count: number; target: HTMLElement }>();\n\nconst hasSkipLinkEnvironment = (): boolean => {\n if (typeof document === 'undefined') {\n return false;\n }\n\n return (\n typeof document.createElement === 'function' &&\n typeof document.querySelector === 'function' &&\n typeof document.getElementById === 'function' &&\n document.body !== null &&\n document.body !== undefined\n );\n};\n\nconst createNoopSkipLinkHandle = (): SkipLinkHandle => ({\n destroy: () => {},\n element: null,\n});\n\n/**\n * Creates a skip navigation link that jumps to the specified target.\n *\n * The link is visually hidden by default and becomes visible when\n * it receives keyboard focus. This follows the WCAG 2.4.1 \"Bypass Blocks\"\n * success criterion.\n *\n * @param targetSelector - CSS selector for the main content area (e.g. `'#main'`, `'main'`)\n * @param options - Configuration options\n * @returns A handle with `destroy()` method and reference to the created element\n *\n * @example\n * ```ts\n * import { skipLink } from '@bquery/bquery/a11y';\n *\n * // Creates a \"Skip to main content\" link pointing to <main>\n * const handle = skipLink('#main-content');\n *\n * // Custom text\n * const handle2 = skipLink('#content', { text: 'Jump to content' });\n *\n * // Remove when no longer needed\n * handle.destroy();\n * ```\n */\nexport const skipLink = (targetSelector: string, options: SkipLinkOptions = {}): SkipLinkHandle => {\n if (!hasSkipLinkEnvironment()) {\n return createNoopSkipLinkHandle();\n }\n\n const { text = 'Skip to main content', className = 'bq-skip-link' } = options;\n let trackedGeneratedTargetId: string | undefined;\n let trackedFocusTarget:\n | { target: HTMLElement; hadTabIndex: boolean; previousTabIndex: string | null }\n | undefined;\n const safeQuerySelector = (selector: string): HTMLElement | null => {\n try {\n return document.querySelector(selector) as HTMLElement | null;\n } catch {\n return null;\n }\n };\n const releaseTrackedGeneratedTargetId = (): void => {\n if (!trackedGeneratedTargetId) return;\n\n const entry = generatedSkipTargetRefs.get(trackedGeneratedTargetId);\n const remainingRefs = (entry?.count ?? 0) - 1;\n if (remainingRefs <= 0) {\n generatedSkipTargetRefs.delete(trackedGeneratedTargetId);\n if (entry?.target.isConnected && entry.target.id === trackedGeneratedTargetId) {\n entry.target.removeAttribute('id');\n }\n } else {\n generatedSkipTargetRefs.set(trackedGeneratedTargetId, {\n count: remainingRefs,\n target: entry!.target,\n });\n }\n\n trackedGeneratedTargetId = undefined;\n };\n const restoreTrackedFocusTarget = (): void => {\n if (!trackedFocusTarget) return;\n\n const { target, hadTabIndex, previousTabIndex } = trackedFocusTarget;\n if (target.isConnected) {\n if (hadTabIndex) {\n target.setAttribute('tabindex', previousTabIndex ?? '');\n } else {\n target.removeAttribute('tabindex');\n }\n }\n\n trackedFocusTarget = undefined;\n };\n const ensureTargetFocusable = (target: HTMLElement): void => {\n if (trackedFocusTarget?.target === target) {\n return;\n }\n\n restoreTrackedFocusTarget();\n\n if (target.hasAttribute('tabindex')) {\n trackedFocusTarget = {\n target,\n hadTabIndex: true,\n previousTabIndex: target.getAttribute('tabindex'),\n };\n return;\n }\n\n if (target.tabIndex !== -1) {\n return;\n }\n\n trackedFocusTarget = {\n target,\n hadTabIndex: false,\n previousTabIndex: null,\n };\n target.setAttribute('tabindex', '-1');\n };\n const trackGeneratedTargetId = (target: HTMLElement, id: string): void => {\n if (trackedGeneratedTargetId === id) return;\n releaseTrackedGeneratedTargetId();\n const entry = generatedSkipTargetRefs.get(id);\n generatedSkipTargetRefs.set(id, {\n count: (entry?.count ?? 0) + 1,\n target,\n });\n trackedGeneratedTargetId = id;\n };\n const resolveTarget = (): HTMLElement | null => {\n if (targetSelector.startsWith('#')) {\n const id = targetSelector.slice(1);\n const byId = id ? (document.getElementById(id) as HTMLElement | null) : null;\n if (byId) {\n return byId;\n }\n\n return safeQuerySelector(targetSelector);\n }\n\n const byId = document.getElementById(targetSelector) as HTMLElement | null;\n if (byId) {\n return byId;\n }\n\n return safeQuerySelector(targetSelector);\n };\n\n const ensureTargetId = (target: HTMLElement): string => {\n if (target.id) {\n const generatedTarget = generatedSkipTargetRefs.get(target.id);\n if (generatedTarget?.target === target) {\n trackGeneratedTargetId(target, target.id);\n }\n return target.id;\n }\n\n let generatedTargetId: string;\n do {\n skipTargetIdCounter += 1;\n generatedTargetId = `bq-skip-target-${skipTargetIdCounter}`;\n } while (document.getElementById(generatedTargetId) !== null);\n\n target.id = generatedTargetId;\n trackGeneratedTargetId(target, generatedTargetId);\n return generatedTargetId;\n };\n\n const link = document.createElement('a');\n const initialTarget = resolveTarget();\n link.href = targetSelector.startsWith('#')\n ? targetSelector\n : initialTarget\n ? `#${ensureTargetId(initialTarget)}`\n : `#${targetSelector}`;\n link.textContent = text;\n link.className = className;\n link.setAttribute('style', DEFAULT_STYLES);\n\n link.addEventListener('focus', () => {\n link.setAttribute('style', DEFAULT_STYLES + FOCUSED_STYLES);\n });\n\n link.addEventListener('blur', () => {\n link.setAttribute('style', DEFAULT_STYLES);\n });\n\n link.addEventListener('click', (e) => {\n e.preventDefault();\n\n const target = resolveTarget();\n if (!target) {\n return;\n }\n\n link.href = `#${ensureTargetId(target)}`;\n // Make the target focusable if it isn't already\n ensureTargetFocusable(target);\n target.focus();\n });\n\n // Insert as the first child of <body>\n if (document.body.firstChild) {\n document.body.insertBefore(link, document.body.firstChild);\n } else {\n document.body.appendChild(link);\n }\n\n return {\n destroy: () => {\n restoreTrackedFocusTarget();\n releaseTrackedGeneratedTargetId();\n link.remove();\n },\n element: link,\n };\n};\n","/**\n * Focus trapping utility for modals, dialogs, and popover content.\n *\n * Constrains keyboard focus within a container so that Tab and Shift+Tab\n * cycle only through the container's focusable elements.\n *\n * @module bquery/a11y\n */\n\nimport type { FocusTrapHandle, TrapFocusOptions } from './types';\n\n/** Selector for elements that can receive focus. */\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n 'details > summary',\n 'audio[controls]',\n 'video[controls]',\n].join(', ');\n\n/**\n * Gets all focusable elements within a container.\n *\n * @param container - The container element\n * @returns Array of focusable elements\n * @internal\n */\nexport const getFocusableElements = (container: Element): HTMLElement[] => {\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)) as HTMLElement[];\n return elements.filter(\n (el) => !el.hasAttribute('disabled') && el.tabIndex !== -1 && el.getClientRects().length > 0\n );\n};\n\n/**\n * Resolves an element from a string selector or returns the element directly.\n * @internal\n */\nconst resolveElement = (\n target: HTMLElement | string | undefined,\n container: Element\n): HTMLElement | null => {\n if (!target) return null;\n if (typeof target === 'string') {\n return container.querySelector(target) as HTMLElement | null;\n }\n return target;\n};\n\n/**\n * Traps keyboard focus within a container element.\n *\n * When activated, Tab and Shift+Tab will cycle only through focusable\n * elements within the container. Useful for modals, dialogs, and\n * dropdown menus.\n *\n * @param container - The DOM element to trap focus within\n * @param options - Configuration options\n * @returns A handle with a `release()` method to deactivate the trap\n *\n * @example\n * ```ts\n * import { trapFocus } from '@bquery/bquery/a11y';\n *\n * const dialog = document.querySelector('#my-dialog');\n * const trap = trapFocus(dialog, { escapeDeactivates: true });\n *\n * // Later, release the trap\n * trap.release();\n * ```\n */\nexport const trapFocus = (\n container: HTMLElement,\n options: TrapFocusOptions = {}\n): FocusTrapHandle => {\n const { escapeDeactivates = true, onEscape, initialFocus, returnFocus } = options;\n\n if (\n typeof document === 'undefined' ||\n typeof document.addEventListener !== 'function' ||\n typeof document.removeEventListener !== 'function'\n ) {\n let active = false;\n return {\n get active() {\n return active;\n },\n release: () => {\n active = false;\n },\n };\n }\n\n const previouslyFocused = document.activeElement as HTMLElement | null;\n let active = true;\n\n const handleKeyDown = (event: KeyboardEvent): void => {\n if (!active) return;\n\n if (event.key === 'Escape' && escapeDeactivates) {\n event.preventDefault();\n handle.release();\n onEscape?.();\n return;\n }\n\n if (event.key !== 'Tab') return;\n\n const focusable = getFocusableElements(container);\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (event.shiftKey) {\n // Shift+Tab: if at first element, wrap to last\n if (document.activeElement === first || !container.contains(document.activeElement)) {\n event.preventDefault();\n last.focus();\n }\n } else {\n // Tab: if at last element, wrap to first\n if (document.activeElement === last || !container.contains(document.activeElement)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n // Attach the event listener\n document.addEventListener('keydown', handleKeyDown, true);\n\n // Set initial focus\n const initialEl = resolveElement(initialFocus, container);\n if (initialEl) {\n initialEl.focus();\n } else {\n const focusable = getFocusableElements(container);\n if (focusable.length > 0) {\n focusable[0].focus();\n }\n }\n\n const handle: FocusTrapHandle = {\n get active() {\n return active;\n },\n\n release: () => {\n if (!active) return;\n active = false;\n document.removeEventListener('keydown', handleKeyDown, true);\n\n // Return focus\n const returnEl = resolveElement(returnFocus, document.body);\n if (returnEl) {\n returnEl.focus();\n } else if (previouslyFocused && previouslyFocused.focus) {\n previouslyFocused.focus();\n }\n },\n };\n\n return handle;\n};\n\n/**\n * Releases a focus trap handle.\n * This is a convenience function — in most cases, use the `release()`\n * method on the individual trap handle directly.\n *\n * @deprecated Prefer using the handle returned by `trapFocus()` directly.\n */\nexport const releaseFocus = (handle: FocusTrapHandle): void => {\n handle.release();\n};\n"],"mappings":"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"}
|
package/dist/a11y.es.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as s, c as a, d as r, i as o, l as n, n as t, o as c, r as u, s as i, t as p, u as d } from "./a11y-
|
|
1
|
+
import { a as s, c as a, d as r, i as o, l as n, n as t, o as c, r as u, s as i, t as p, u as d } from "./a11y-IV_bfLyn.js";
|
|
2
2
|
export {
|
|
3
3
|
d as announceToScreenReader,
|
|
4
4
|
n as auditA11y,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { f as L, l as N, n as I, t as j, u as D } from "./sanitize-DOMkRO9G.js";
|
|
2
|
-
import { n as Q } from "./
|
|
3
|
-
import { n as U } from "./
|
|
4
|
-
import { t as q } from "./effect-
|
|
5
|
-
import { r as W, t as w } from "./untrack-
|
|
2
|
+
import { n as Q } from "./config-BP7KwiR5.js";
|
|
3
|
+
import { n as U } from "./core-yg9rJXiR.js";
|
|
4
|
+
import { t as q } from "./effect-v8OIEmPs.js";
|
|
5
|
+
import { r as W, t as w } from "./untrack-uzz3JDNK.js";
|
|
6
6
|
import { t as J } from "./env-PvwYHnJq.js";
|
|
7
7
|
var F = (r, e) => {
|
|
8
8
|
const { type: o } = e;
|
|
@@ -65,7 +65,7 @@ function R() {
|
|
|
65
65
|
function le(r) {
|
|
66
66
|
const e = y;
|
|
67
67
|
if (!e) throw new Error("bQuery component: useSignal() must be called inside a component lifecycle hook. Avoid calling it directly from render()");
|
|
68
|
-
const o =
|
|
68
|
+
const o = U(r);
|
|
69
69
|
return e.addDisposer(() => o.dispose()), o;
|
|
70
70
|
}
|
|
71
71
|
function ie(r) {
|
|
@@ -364,7 +364,7 @@ var z = /* @__PURE__ */ Symbol("bquery.booleanAttribute"), Y = /^[^\0-\x20"'/>=]
|
|
|
364
364
|
r.disabled ? " disabled" : "",
|
|
365
365
|
`>${c(r.value)}</textarea>`
|
|
366
366
|
].join(""), he = (r = {}) => {
|
|
367
|
-
const e = r.prefix ??
|
|
367
|
+
const e = r.prefix ?? Q().components?.prefix ?? "bq", o = {
|
|
368
368
|
button: `${e}-button`,
|
|
369
369
|
card: `${e}-card`,
|
|
370
370
|
input: `${e}-input`,
|
|
@@ -682,4 +682,4 @@ export {
|
|
|
682
682
|
he as t
|
|
683
683
|
};
|
|
684
684
|
|
|
685
|
-
//# sourceMappingURL=component-
|
|
685
|
+
//# sourceMappingURL=component-BtsRbf6c.js.map
|