@lessonkit/accessibility 1.1.0 → 1.3.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/dist/index.cjs CHANGED
@@ -23,9 +23,11 @@ __export(index_exports, {
23
23
  createRovingTabIndex: () => createRovingTabIndex,
24
24
  focusFirst: () => focusFirst,
25
25
  getFocusableElements: () => getFocusableElements,
26
+ getPrefersReducedMotionSnapshot: () => getPrefersReducedMotionSnapshot,
26
27
  getReducedMotionPreference: () => getReducedMotionPreference,
27
28
  prefersReducedMotion: () => prefersReducedMotion,
28
29
  shouldAnimate: () => shouldAnimate,
30
+ subscribeReducedMotion: () => subscribeReducedMotion,
29
31
  trapFocus: () => trapFocus,
30
32
  visuallyHiddenStyle: () => visuallyHiddenStyle
31
33
  });
@@ -56,8 +58,23 @@ function isHTMLElement(v) {
56
58
  return typeof HTMLElement !== "undefined" && v instanceof HTMLElement;
57
59
  }
58
60
  function prefersReducedMotion() {
61
+ return getPrefersReducedMotionSnapshot();
62
+ }
63
+ function getPrefersReducedMotionSnapshot() {
59
64
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
60
65
  }
66
+ function subscribeReducedMotion(listener) {
67
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
68
+ listener(false);
69
+ return () => {
70
+ };
71
+ }
72
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
73
+ const onChange = () => listener(mq.matches);
74
+ listener(mq.matches);
75
+ mq.addEventListener("change", onChange);
76
+ return () => mq.removeEventListener("change", onChange);
77
+ }
61
78
  function getReducedMotionPreference() {
62
79
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return "unknown";
63
80
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "reduce" : "no-preference";
@@ -70,16 +87,7 @@ function shouldAnimate(opts) {
70
87
  }
71
88
  function focusFirst(container) {
72
89
  if (!container) return false;
73
- const el = container.querySelector(
74
- [
75
- "a[href]",
76
- "button:not([disabled])",
77
- "input:not([disabled])",
78
- "select:not([disabled])",
79
- "textarea:not([disabled])",
80
- "[tabindex]:not([tabindex='-1'])"
81
- ].join(",")
82
- );
90
+ const el = container.querySelector(FOCUSABLE_SELECTORS);
83
91
  el?.focus();
84
92
  return Boolean(el);
85
93
  }
@@ -140,10 +148,12 @@ function trapFocus(container, opts) {
140
148
  prevFocused = restoreFocus && isHTMLElement(document.activeElement) ? document.activeElement : null;
141
149
  document.addEventListener("keydown", onKeyDown);
142
150
  document.addEventListener("focusin", onFocusIn);
151
+ document.addEventListener("pointerdown", onPointerDownCapture, true);
143
152
  document.addEventListener("mousedown", onPointerDownCapture, true);
144
153
  removeListeners = () => {
145
154
  document.removeEventListener("keydown", onKeyDown);
146
155
  document.removeEventListener("focusin", onFocusIn);
156
+ document.removeEventListener("pointerdown", onPointerDownCapture, true);
147
157
  document.removeEventListener("mousedown", onPointerDownCapture, true);
148
158
  };
149
159
  const focusables = getFocusableElements(container);
@@ -242,9 +252,11 @@ function clampIndex(index, itemCount) {
242
252
  createRovingTabIndex,
243
253
  focusFirst,
244
254
  getFocusableElements,
255
+ getPrefersReducedMotionSnapshot,
245
256
  getReducedMotionPreference,
246
257
  prefersReducedMotion,
247
258
  shouldAnimate,
259
+ subscribeReducedMotion,
248
260
  trapFocus,
249
261
  visuallyHiddenStyle
250
262
  });
package/dist/index.d.cts CHANGED
@@ -30,6 +30,10 @@ type FocusContainer = {
30
30
  querySelector<T>(selectors: string): T | null;
31
31
  };
32
32
  declare function prefersReducedMotion(): boolean;
33
+ /** One-shot read of the prefers-reduced-motion media query. */
34
+ declare function getPrefersReducedMotionSnapshot(): boolean;
35
+ /** Subscribe to OS reduced-motion preference changes; returns an unsubscribe function. */
36
+ declare function subscribeReducedMotion(listener: (reduce: boolean) => void): () => void;
33
37
  declare function getReducedMotionPreference(): "reduce" | "no-preference" | "unknown";
34
38
  declare function shouldAnimate(opts?: {
35
39
  default?: boolean;
@@ -54,4 +58,4 @@ declare function createRovingTabIndex(opts: RovingTabIndexOptions): {
54
58
  };
55
59
  };
56
60
 
57
- export { type FocusContainer, type Focusable, type RovingTabIndexOptions, type TrapFocusOptions, type VisuallyHiddenStyle, createRovingTabIndex, focusFirst, getFocusableElements, getReducedMotionPreference, prefersReducedMotion, shouldAnimate, trapFocus, visuallyHiddenStyle };
61
+ export { type FocusContainer, type Focusable, type RovingTabIndexOptions, type TrapFocusOptions, type VisuallyHiddenStyle, createRovingTabIndex, focusFirst, getFocusableElements, getPrefersReducedMotionSnapshot, getReducedMotionPreference, prefersReducedMotion, shouldAnimate, subscribeReducedMotion, trapFocus, visuallyHiddenStyle };
package/dist/index.d.ts CHANGED
@@ -30,6 +30,10 @@ type FocusContainer = {
30
30
  querySelector<T>(selectors: string): T | null;
31
31
  };
32
32
  declare function prefersReducedMotion(): boolean;
33
+ /** One-shot read of the prefers-reduced-motion media query. */
34
+ declare function getPrefersReducedMotionSnapshot(): boolean;
35
+ /** Subscribe to OS reduced-motion preference changes; returns an unsubscribe function. */
36
+ declare function subscribeReducedMotion(listener: (reduce: boolean) => void): () => void;
33
37
  declare function getReducedMotionPreference(): "reduce" | "no-preference" | "unknown";
34
38
  declare function shouldAnimate(opts?: {
35
39
  default?: boolean;
@@ -54,4 +58,4 @@ declare function createRovingTabIndex(opts: RovingTabIndexOptions): {
54
58
  };
55
59
  };
56
60
 
57
- export { type FocusContainer, type Focusable, type RovingTabIndexOptions, type TrapFocusOptions, type VisuallyHiddenStyle, createRovingTabIndex, focusFirst, getFocusableElements, getReducedMotionPreference, prefersReducedMotion, shouldAnimate, trapFocus, visuallyHiddenStyle };
61
+ export { type FocusContainer, type Focusable, type RovingTabIndexOptions, type TrapFocusOptions, type VisuallyHiddenStyle, createRovingTabIndex, focusFirst, getFocusableElements, getPrefersReducedMotionSnapshot, getReducedMotionPreference, prefersReducedMotion, shouldAnimate, subscribeReducedMotion, trapFocus, visuallyHiddenStyle };
package/dist/index.js CHANGED
@@ -25,8 +25,23 @@ function isHTMLElement(v) {
25
25
  return typeof HTMLElement !== "undefined" && v instanceof HTMLElement;
26
26
  }
27
27
  function prefersReducedMotion() {
28
+ return getPrefersReducedMotionSnapshot();
29
+ }
30
+ function getPrefersReducedMotionSnapshot() {
28
31
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
29
32
  }
33
+ function subscribeReducedMotion(listener) {
34
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
35
+ listener(false);
36
+ return () => {
37
+ };
38
+ }
39
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
40
+ const onChange = () => listener(mq.matches);
41
+ listener(mq.matches);
42
+ mq.addEventListener("change", onChange);
43
+ return () => mq.removeEventListener("change", onChange);
44
+ }
30
45
  function getReducedMotionPreference() {
31
46
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return "unknown";
32
47
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "reduce" : "no-preference";
@@ -39,16 +54,7 @@ function shouldAnimate(opts) {
39
54
  }
40
55
  function focusFirst(container) {
41
56
  if (!container) return false;
42
- const el = container.querySelector(
43
- [
44
- "a[href]",
45
- "button:not([disabled])",
46
- "input:not([disabled])",
47
- "select:not([disabled])",
48
- "textarea:not([disabled])",
49
- "[tabindex]:not([tabindex='-1'])"
50
- ].join(",")
51
- );
57
+ const el = container.querySelector(FOCUSABLE_SELECTORS);
52
58
  el?.focus();
53
59
  return Boolean(el);
54
60
  }
@@ -109,10 +115,12 @@ function trapFocus(container, opts) {
109
115
  prevFocused = restoreFocus && isHTMLElement(document.activeElement) ? document.activeElement : null;
110
116
  document.addEventListener("keydown", onKeyDown);
111
117
  document.addEventListener("focusin", onFocusIn);
118
+ document.addEventListener("pointerdown", onPointerDownCapture, true);
112
119
  document.addEventListener("mousedown", onPointerDownCapture, true);
113
120
  removeListeners = () => {
114
121
  document.removeEventListener("keydown", onKeyDown);
115
122
  document.removeEventListener("focusin", onFocusIn);
123
+ document.removeEventListener("pointerdown", onPointerDownCapture, true);
116
124
  document.removeEventListener("mousedown", onPointerDownCapture, true);
117
125
  };
118
126
  const focusables = getFocusableElements(container);
@@ -210,9 +218,11 @@ export {
210
218
  createRovingTabIndex,
211
219
  focusFirst,
212
220
  getFocusableElements,
221
+ getPrefersReducedMotionSnapshot,
213
222
  getReducedMotionPreference,
214
223
  prefersReducedMotion,
215
224
  shouldAnimate,
225
+ subscribeReducedMotion,
216
226
  trapFocus,
217
227
  visuallyHiddenStyle
218
228
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/accessibility",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "private": false,
5
5
  "description": "Accessibility utilities for LessonKit packages and apps.",
6
6
  "license": "Apache-2.0",
@@ -9,7 +9,7 @@
9
9
  "url": "git+https://github.com/eddiethedean/lessonkit.git",
10
10
  "directory": "packages/accessibility"
11
11
  },
12
- "homepage": "https://github.com/eddiethedean/lessonkit",
12
+ "homepage": "https://lessonkit.readthedocs.io/en/latest/reference/accessibility.html",
13
13
  "bugs": {
14
14
  "url": "https://github.com/eddiethedean/lessonkit/issues"
15
15
  },