vanilla-aria-modals 1.1.4 → 1.1.6

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/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. Dates use I
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [1.1.6] - 2026-02-20
8
+ ### Fixed
9
+ - Update trap focus logic to ignore hidden elements, selecting only visible focusable ones
10
+
11
+ ### Added
12
+ - Informational debug statements for focus management
13
+ - Example JS now toggles `inert` on `<main>` when a modal opens
14
+
15
+ ## [1.1.5] - 2026-02-19
16
+ ### Fixed
17
+ - Remove the unnecessary `isToggle` parameter type definition from the `.d.ts` file.
18
+
7
19
  ## [1.1.4] - 2026-02-16
8
20
  ### Fixed
9
21
  - Improve overlayless modal close logic. Removed the `isToggle` parameter from `removeA11yEvents` as it is no longer needed. The logic now works more smoothly, only unregisters modals in `removeA11yEvents` instead of the close handler wrapper, and preserves the overlayless modal bypass behavior
package/README.md CHANGED
@@ -14,7 +14,7 @@ Written in vanilla JS for full flexibility. You can modify it directly in `node_
14
14
 
15
15
  ## Set up
16
16
 
17
- ```js
17
+ ```javascript
18
18
  import ModalHandler from 'vanilla-aria-modals';
19
19
  const modalHandler = new ModalHandler();
20
20
  ```
@@ -33,7 +33,7 @@ A fully detailed example including the necessary JavaScript, HTML, and CSS files
33
33
 
34
34
  **Note:** `lm` in the code stands for *HTMLElement*.
35
35
 
36
- ```js
36
+ ```javascript
37
37
  // Basic example of showing a modal
38
38
  showModal() {
39
39
  modalContainerLm.style.display = 'block';
@@ -88,7 +88,7 @@ In Single Page Applications (SPA) or frameworks like React, Vue, or vanilla JS w
88
88
 
89
89
  ### Example: Cleanup on route change or component unmount
90
90
 
91
- ```js
91
+ ```javascript
92
92
  // Suppose your SPA route or component changes
93
93
  function onRouteChange() {
94
94
  // Clear leftover document events, active modals, focus tracking, modal ID key counter
@@ -186,7 +186,7 @@ Registers A11y events and modal stacking handling:
186
186
 
187
187
  If you need to pass additional arguments to the close handler, you can wrap it in a function that returns a handler accepting only the two parameters (`e` and `modalKey`):
188
188
 
189
- ```js
189
+ ```javascript
190
190
  const closeModalWrapper = (...args) => {
191
191
  // Returns a handler that the utility calls internally with e and modalKey
192
192
  return (e, modalKey) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-aria-modals",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Framework-agnostic utility for managing accessibility in modals or modal-like UIs, including modal stacking, focus management, and closing via Escape key or outside click.",
5
5
  "main": "src/ModalHandler.js",
6
6
  "scripts": {
@@ -30,7 +30,6 @@ export default class ModalHandler {
30
30
 
31
31
  removeA11yEvents(options: {
32
32
  modalKey: string;
33
- isToggle?: boolean
34
33
  }): void;
35
34
 
36
35
  addFocus(options: {
@@ -80,15 +80,15 @@ export default class ModalHandler {
80
80
  }
81
81
 
82
82
  #trapFocus(e, element) {
83
- // Select all focusable elements within the given element
84
- const focusableLms = element.querySelectorAll(`
83
+ // Select all visible (not hidden) focusable elements within the given element
84
+ const focusableLms = [...element.querySelectorAll(`
85
85
  a[href]:not([disabled]),
86
86
  button:not([disabled]),
87
87
  textarea:not([disabled]),
88
88
  input:not([disabled]),
89
89
  select:not([disabled]),
90
90
  [tabindex]:not([tabindex="-1"])
91
- `);
91
+ `)].filter(lm => lm.offsetParent !== null && getComputedStyle(lm).visibility !== 'hidden');
92
92
  // Get the first and last focusable elements
93
93
  const firstFocusableLm = focusableLms[0];
94
94
  const lastFocusableLm = focusableLms[focusableLms.length - 1];
@@ -399,6 +399,11 @@ export default class ModalHandler {
399
399
  const lastFocusableLm = lastFocusedLm ? lastFocusedLm : document.activeElement;
400
400
  if (auto) this.#focusHandler[modalKey] = lastFocusableLm;
401
401
 
402
+ if (this.#debug) {
403
+ console.log(`[ModalHandler][DEBUG]: Focusing first element in modal "${modalKey}" => `, firstFocusableLm);
404
+ console.log(`[ModalHandler][DEBUG]: ${auto ? 'Stored' : 'Returned'} last focused element for modal "${modalKey}" => `, lastFocusableLm);
405
+ }
406
+
402
407
  // Needs a timeout for keyboard navigation, if not focus is unreliable
403
408
  setTimeout(() => {
404
409
  firstFocusableLm.focus();
@@ -418,8 +423,17 @@ export default class ModalHandler {
418
423
  }
419
424
 
420
425
  const lastFocusableLm = auto ? this.#focusHandler[modalKey] : lastFocusedLm;
426
+
427
+ if (this.#debug) {
428
+ console.log(`[ModalHandler][DEBUG]: Restoring focus for modal "${modalKey}" to element => `, lastFocusableLm);
429
+ }
430
+
421
431
  lastFocusableLm.focus();
422
432
 
433
+ if (this.#debug && auto) {
434
+ console.log(`[ModalHandler][DEBUG]: Cleared stored focus for modal "${modalKey}"`);
435
+ }
436
+
423
437
  // Clean up the stored focus
424
438
  if (auto) {
425
439
  delete this.#focusHandler[modalKey];