a11y-alert-dialog 0.1.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/A11Y.md ADDED
@@ -0,0 +1,207 @@
1
+ # Accessibility Contract (A11Y.md)
2
+
3
+ This document defines the **non-negotiable accessibility guarantees** of the
4
+ Accessible Alert library.
5
+
6
+ If any change violates this contract, it is considered a **breaking bug**,
7
+ not a feature request.
8
+
9
+ Accessibility is **not optional** in this project.
10
+
11
+ ---
12
+
13
+ ## 🎯 Core Principle
14
+
15
+ > If a user can operate a keyboard, they must be able to fully operate this dialog.
16
+
17
+ This library is designed to work for:
18
+ - Keyboard-only users
19
+ - Screen reader users
20
+ - Users with motor impairments
21
+ - Users relying on assistive technologies (switch devices, voice control)
22
+
23
+ ---
24
+
25
+ ## 🚫 Non-Negotiable Rules
26
+
27
+ The following rules **must never be disabled, bypassed, or overridden**.
28
+
29
+ ---
30
+
31
+ ## 1. Focus Management
32
+
33
+ ### 1.1 Before Opening a Dialog
34
+ - The currently focused element **MUST** be stored.
35
+ - If no element is focused, `document.body` is used as fallback.
36
+
37
+ ### 1.2 When Dialog Opens
38
+ - Focus **MUST** move inside the dialog immediately.
39
+ - Focus **MUST NOT** remain on background content.
40
+ - Initial focus priority:
41
+ 1. Primary action button (Confirm)
42
+ 2. First focusable element inside the dialog
43
+
44
+ ### 1.3 While Dialog Is Open
45
+ - Focus **MUST NOT** escape the dialog.
46
+ - Background content **MUST NOT** be focusable.
47
+
48
+ ### 1.4 When Dialog Closes
49
+ - Focus **MUST** be restored to the previously focused element.
50
+ - If the original element no longer exists, focus moves to:
51
+ - The closest available parent
52
+ - Or `document.body` as final fallback
53
+
54
+ ❌ Losing focus after closing is a **critical accessibility failure**.
55
+
56
+ ---
57
+
58
+ ## 2. Focus Trap
59
+
60
+ ### Requirements
61
+ - `Tab` and `Shift + Tab` navigation **MUST** loop inside the dialog.
62
+ - Focus trapping **MUST** work without relying on CSS hacks.
63
+ - Focus trap **MUST** handle dynamically added or removed focusable elements.
64
+
65
+ ### Forbidden
66
+ - Disabling focus trap
67
+ - Allowing focus to move into the page behind the dialog
68
+
69
+ ---
70
+
71
+ ## 3. Keyboard Interaction
72
+
73
+ Keyboard support is **mandatory and always enabled**.
74
+
75
+ ### Required Key Bindings
76
+
77
+ | Key | Behavior |
78
+ |----|---------|
79
+ | Tab | Move focus forward |
80
+ | Shift + Tab | Move focus backward |
81
+ | Enter | Activate primary action |
82
+ | Esc | Cancel or close dialog |
83
+
84
+ ### Rules
85
+ - Keyboard behavior **MUST NOT** depend on mouse interaction.
86
+ - Keyboard handling **MUST NOT** interfere with text input fields.
87
+ - `Esc` **MUST ALWAYS** close the dialog.
88
+
89
+ ---
90
+
91
+ ## 4. Screen Reader Semantics
92
+
93
+ ### Dialog Roles
94
+ - Use `role="dialog"` for normal dialogs
95
+ - Use `role="alertdialog"` for destructive or dangerous actions
96
+
97
+ ### ARIA Attributes
98
+ Each dialog **MUST** include:
99
+ - `aria-modal="true"`
100
+ - `aria-labelledby` referencing the dialog title
101
+ - `aria-describedby` referencing the dialog description (if present)
102
+
103
+ ### Announcements
104
+ - Screen readers **MUST** announce:
105
+ - Dialog type (dialog / alert dialog)
106
+ - Title
107
+ - Description
108
+ - Announcement **MUST** occur immediately when the dialog opens.
109
+
110
+ ---
111
+
112
+ ## 5. Visual & Motion Safety
113
+
114
+ ### Motion
115
+ - Respect `prefers-reduced-motion`
116
+ - Avoid animations that:
117
+ - Cause dizziness
118
+ - Delay keyboard interaction
119
+ - Block screen reader announcements
120
+
121
+ ### Visibility
122
+ - Focused elements **MUST** have visible focus indicators.
123
+ - Focus indicators **MUST NOT** be removed.
124
+
125
+ ---
126
+
127
+ ## 6. Timing & Control
128
+
129
+ ### Forbidden Behaviors
130
+ - Auto-close timers
131
+ - Forced timeouts
132
+ - Auto-confirm actions
133
+
134
+ Users **MUST** remain in control at all times.
135
+
136
+ ---
137
+
138
+ ## 7. HTML Semantics
139
+
140
+ ### Required
141
+ - Use native interactive elements:
142
+ - `<button>` for actions
143
+ - `<input>` for form fields
144
+
145
+ ### Forbidden
146
+ - Clickable `<div>` or `<span>` for core actions
147
+ - Custom elements without proper ARIA roles
148
+
149
+ Voice control and assistive tools depend on correct semantics.
150
+
151
+ ---
152
+
153
+ ## 8. API-Level Constraints
154
+
155
+ The public API **MUST NOT** expose options that allow:
156
+
157
+ - Disabling focus management
158
+ - Disabling keyboard handling
159
+ - Injecting custom HTML for critical controls
160
+ - Bypassing screen reader semantics
161
+
162
+ If an API option can break accessibility, it must be removed.
163
+
164
+ ---
165
+
166
+ ## 9. Testing Requirements
167
+
168
+ ### Manual Testing (Required)
169
+ Every release **MUST** be tested with:
170
+ - Keyboard only (no mouse)
171
+ - At least one screen reader:
172
+ - NVDA (Windows) or
173
+ - VoiceOver (macOS)
174
+
175
+ ### Automated Testing
176
+ Recommended (not sufficient alone):
177
+ - jest-axe
178
+ - Keyboard navigation tests (Playwright)
179
+
180
+ Automated tests **DO NOT** replace manual accessibility testing.
181
+
182
+ ---
183
+
184
+ ## 10. Definition of Done (Accessibility)
185
+
186
+ A feature is considered **DONE** only if:
187
+
188
+ - It works without a mouse
189
+ - It works with a keyboard only
190
+ - It announces correctly in a screen reader
191
+ - It does not break focus restoration
192
+ - It does not violate any rule in this document
193
+
194
+ ---
195
+
196
+ ## ⚠️ Final Warning
197
+
198
+ Accessibility bugs are treated as **critical bugs**, not minor issues.
199
+
200
+ No feature, customization, or visual improvement is allowed to violate this contract.
201
+
202
+ ---
203
+
204
+ ## 💬 Philosophy
205
+
206
+ > Accessibility is not about edge cases.
207
+ > It is about real people trying to use your software.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 xShiroeNguyenx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Accessible Alert
2
+
3
+ > An alert / dialog library that works without a mouse.
4
+
5
+ **Accessible Alert** is a **framework-agnostic JavaScript library** for displaying
6
+ `alert`, `confirm`, and `prompt` dialogs with **accessibility-first (a11y-first)**
7
+ design.
8
+
9
+ If you can use a **keyboard**, you can use this library.
10
+
11
+ ---
12
+
13
+ ## ✨ Why does this library exist?
14
+
15
+ Most alert/modal libraries:
16
+ - Prioritize visuals and animations
17
+ - Treat accessibility as an afterthought
18
+ - Break keyboard navigation and screen readers easily
19
+
20
+ 👉 **Accessible Alert** is built the opposite way:
21
+
22
+ > Accessibility is the foundation, not a feature added later.
23
+
24
+ ---
25
+
26
+ ## ✅ Accessibility Guarantees (A11Y Contract)
27
+
28
+ This library **always guarantees**:
29
+
30
+ - No mouse required
31
+ - Fully usable with keyboard only (Tab / Enter / Esc)
32
+ - Correct focus management at all times
33
+ - Focus is never lost after closing dialogs
34
+ - Proper screen reader announcements
35
+ - No auto-close behaviors that steal control from users
36
+
37
+ Standards:
38
+ - WCAG 2.1 AA
39
+ - WAI-ARIA Authoring Practices (Dialog)
40
+
41
+ ---
42
+
43
+ ## 📦 Installation
44
+
45
+ ### NPM
46
+ ```bash
47
+ npm install accessible-alert
48
+ ```
49
+
50
+ ### CDN
51
+ ```html
52
+ <script src="https://unpkg.com/accessible-alert"></script>
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 🚀 Basic Usage
58
+
59
+ ### Alert
60
+ ```js
61
+ await alert("Saved successfully")
62
+ ```
63
+
64
+ ### Confirm
65
+ ```js
66
+ const ok = await confirm({
67
+ title: "Delete user",
68
+ description: "This action cannot be undone",
69
+ danger: true
70
+ })
71
+
72
+ if (ok) {
73
+ deleteUser()
74
+ }
75
+ ```
76
+
77
+ ### Prompt
78
+ ```js
79
+ const name = await prompt({
80
+ title: "Your name",
81
+ placeholder: "Enter your name"
82
+ })
83
+ ```
84
+
85
+ ---
86
+
87
+ ## ⌨️ Keyboard Behavior (Always Enabled)
88
+
89
+ | Key | Action |
90
+ |----|-------|
91
+ | Tab | Move forward within the dialog |
92
+ | Shift + Tab | Move backward |
93
+ | Enter | Confirm primary action |
94
+ | Esc | Cancel / Close dialog |
95
+
96
+ Keyboard support **cannot be disabled**.
97
+
98
+ ---
99
+
100
+ ## 🧠 How Accessibility Is Handled
101
+
102
+ ### Focus Management
103
+ - Saves the currently focused element before opening
104
+ - Automatically moves focus into the dialog
105
+ - Restores focus to the original element after closing
106
+
107
+ ### Focus Trap
108
+ - Tab navigation is trapped inside the dialog
109
+ - Focus never escapes to the background
110
+ - No CSS hacks or pointer-event tricks
111
+
112
+ ### Screen Reader Support
113
+ - Uses `role="dialog"` or `role="alertdialog"`
114
+ - Proper `aria-labelledby` and `aria-describedby`
115
+ - Dialog content is announced immediately on open
116
+
117
+ ---
118
+
119
+ ## 🧩 API Design Principles
120
+
121
+ - Promise-based API
122
+ - No callbacks
123
+ - No configuration options that can break accessibility
124
+ - No custom HTML injection for critical controls
125
+
126
+ ```js
127
+ await confirm({ title: "Are you sure?" })
128
+ ```
129
+
130
+ > If an API allows breaking accessibility, the API is wrong.
131
+
132
+ ---
133
+
134
+ ## ❌ What This Library Does NOT Support
135
+
136
+ To protect accessibility, this library intentionally does NOT support:
137
+
138
+ - Auto-close timers
139
+ - Disabling focus trapping
140
+ - Custom button HTML
141
+ - Complex animations
142
+ - Theme systems
143
+ - Toast-style notifications
144
+
145
+ 👉 If you want flashy UI effects, this library is **not** for you.
146
+
147
+ ---
148
+
149
+ ## 🧪 Testing
150
+
151
+ Manually tested with:
152
+ - Keyboard-only navigation
153
+ - NVDA (Windows)
154
+ - VoiceOver (macOS)
155
+
156
+ Automated testing includes:
157
+ - jest-axe
158
+ - Playwright keyboard tests
159
+
160
+ ---
161
+
162
+ ## 🎯 When Should You Use This Library?
163
+
164
+ ✔ Internal tools
165
+ ✔ Admin dashboards
166
+ ✔ SaaS products
167
+ ✔ Enterprise / Government / Fintech
168
+ ✔ Any project where accessibility is a real requirement
169
+
170
+ ---
171
+
172
+ ## 📄 License
173
+
174
+ MIT
175
+
176
+ ---
177
+
178
+ ## 💬 Philosophy
179
+
180
+ > “If you can use a keyboard, you can use this alert.”
package/dist/index.cjs ADDED
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ alert: () => alert,
24
+ confirm: () => confirm
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/core/focus.ts
29
+ var previousFocus = null;
30
+ function saveFocus() {
31
+ previousFocus = document.activeElement;
32
+ }
33
+ function restoreFocus() {
34
+ if (previousFocus && document.contains(previousFocus)) {
35
+ previousFocus.focus();
36
+ } else {
37
+ document.body.focus();
38
+ }
39
+ }
40
+
41
+ // src/core/focusTrap.ts
42
+ function trapFocus(container) {
43
+ function getFocusable() {
44
+ return Array.from(
45
+ container.querySelectorAll(
46
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
47
+ )
48
+ );
49
+ }
50
+ function handle(e) {
51
+ if (e.key !== "Tab") return;
52
+ const items = getFocusable();
53
+ if (!items.length) return;
54
+ const first = items[0];
55
+ const last = items[items.length - 1];
56
+ if (e.shiftKey && document.activeElement === first) {
57
+ e.preventDefault();
58
+ last.focus();
59
+ } else if (!e.shiftKey && document.activeElement === last) {
60
+ e.preventDefault();
61
+ first.focus();
62
+ }
63
+ }
64
+ container.addEventListener("keydown", handle);
65
+ return () => container.removeEventListener("keydown", handle);
66
+ }
67
+
68
+ // src/core/Dialog.ts
69
+ function openDialog(options) {
70
+ return new Promise((resolve) => {
71
+ saveFocus();
72
+ const overlay = document.createElement("div");
73
+ overlay.style.position = "fixed";
74
+ overlay.style.inset = "0";
75
+ overlay.style.background = "rgba(0,0,0,0.4)";
76
+ const dialog = document.createElement("div");
77
+ dialog.setAttribute("role", options.role ?? "dialog");
78
+ dialog.setAttribute("aria-modal", "true");
79
+ dialog.style.background = "white";
80
+ dialog.style.padding = "1rem";
81
+ dialog.style.maxWidth = "400px";
82
+ dialog.style.margin = "20vh auto";
83
+ const title = document.createElement("h2");
84
+ title.id = "dialog-title";
85
+ title.textContent = options.title;
86
+ dialog.setAttribute("aria-labelledby", title.id);
87
+ dialog.appendChild(title);
88
+ if (options.description) {
89
+ const desc = document.createElement("p");
90
+ desc.id = "dialog-desc";
91
+ desc.textContent = options.description;
92
+ dialog.setAttribute("aria-describedby", desc.id);
93
+ dialog.appendChild(desc);
94
+ }
95
+ const actions = document.createElement("div");
96
+ const btnConfirm = document.createElement("button");
97
+ btnConfirm.textContent = options.confirmText ?? "OK";
98
+ actions.appendChild(btnConfirm);
99
+ let btnCancel = null;
100
+ if (options.cancelText !== null) {
101
+ btnCancel = document.createElement("button");
102
+ btnCancel.textContent = options.cancelText ?? "Cancel";
103
+ actions.appendChild(btnCancel);
104
+ }
105
+ dialog.appendChild(actions);
106
+ overlay.appendChild(dialog);
107
+ document.body.appendChild(overlay);
108
+ const releaseTrap = trapFocus(dialog);
109
+ btnConfirm.focus();
110
+ function close(result) {
111
+ releaseTrap();
112
+ overlay.remove();
113
+ restoreFocus();
114
+ resolve(result);
115
+ }
116
+ btnConfirm.onclick = () => close(true);
117
+ btnCancel && (btnCancel.onclick = () => close(false));
118
+ dialog.addEventListener("keydown", (e) => {
119
+ if (e.key === "Escape") close(false);
120
+ });
121
+ });
122
+ }
123
+
124
+ // src/api/alert.ts
125
+ function alert(message) {
126
+ return openDialog({
127
+ title: message,
128
+ role: "dialog",
129
+ confirmText: "OK",
130
+ cancelText: null
131
+ });
132
+ }
133
+
134
+ // src/api/confirm.ts
135
+ function confirm(options) {
136
+ return openDialog({
137
+ ...options,
138
+ role: "alertdialog",
139
+ confirmText: "Confirm",
140
+ cancelText: "Cancel"
141
+ });
142
+ }
143
+ // Annotate the CommonJS export names for ESM import in node:
144
+ 0 && (module.exports = {
145
+ alert,
146
+ confirm
147
+ });
@@ -0,0 +1,8 @@
1
+ declare function alert(message: string): Promise<boolean>;
2
+
3
+ declare function confirm(options: {
4
+ title: string;
5
+ description?: string;
6
+ }): Promise<boolean>;
7
+
8
+ export { alert, confirm };
@@ -0,0 +1,8 @@
1
+ declare function alert(message: string): Promise<boolean>;
2
+
3
+ declare function confirm(options: {
4
+ title: string;
5
+ description?: string;
6
+ }): Promise<boolean>;
7
+
8
+ export { alert, confirm };
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ // src/core/focus.ts
2
+ var previousFocus = null;
3
+ function saveFocus() {
4
+ previousFocus = document.activeElement;
5
+ }
6
+ function restoreFocus() {
7
+ if (previousFocus && document.contains(previousFocus)) {
8
+ previousFocus.focus();
9
+ } else {
10
+ document.body.focus();
11
+ }
12
+ }
13
+
14
+ // src/core/focusTrap.ts
15
+ function trapFocus(container) {
16
+ function getFocusable() {
17
+ return Array.from(
18
+ container.querySelectorAll(
19
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
20
+ )
21
+ );
22
+ }
23
+ function handle(e) {
24
+ if (e.key !== "Tab") return;
25
+ const items = getFocusable();
26
+ if (!items.length) return;
27
+ const first = items[0];
28
+ const last = items[items.length - 1];
29
+ if (e.shiftKey && document.activeElement === first) {
30
+ e.preventDefault();
31
+ last.focus();
32
+ } else if (!e.shiftKey && document.activeElement === last) {
33
+ e.preventDefault();
34
+ first.focus();
35
+ }
36
+ }
37
+ container.addEventListener("keydown", handle);
38
+ return () => container.removeEventListener("keydown", handle);
39
+ }
40
+
41
+ // src/core/Dialog.ts
42
+ function openDialog(options) {
43
+ return new Promise((resolve) => {
44
+ saveFocus();
45
+ const overlay = document.createElement("div");
46
+ overlay.style.position = "fixed";
47
+ overlay.style.inset = "0";
48
+ overlay.style.background = "rgba(0,0,0,0.4)";
49
+ const dialog = document.createElement("div");
50
+ dialog.setAttribute("role", options.role ?? "dialog");
51
+ dialog.setAttribute("aria-modal", "true");
52
+ dialog.style.background = "white";
53
+ dialog.style.padding = "1rem";
54
+ dialog.style.maxWidth = "400px";
55
+ dialog.style.margin = "20vh auto";
56
+ const title = document.createElement("h2");
57
+ title.id = "dialog-title";
58
+ title.textContent = options.title;
59
+ dialog.setAttribute("aria-labelledby", title.id);
60
+ dialog.appendChild(title);
61
+ if (options.description) {
62
+ const desc = document.createElement("p");
63
+ desc.id = "dialog-desc";
64
+ desc.textContent = options.description;
65
+ dialog.setAttribute("aria-describedby", desc.id);
66
+ dialog.appendChild(desc);
67
+ }
68
+ const actions = document.createElement("div");
69
+ const btnConfirm = document.createElement("button");
70
+ btnConfirm.textContent = options.confirmText ?? "OK";
71
+ actions.appendChild(btnConfirm);
72
+ let btnCancel = null;
73
+ if (options.cancelText !== null) {
74
+ btnCancel = document.createElement("button");
75
+ btnCancel.textContent = options.cancelText ?? "Cancel";
76
+ actions.appendChild(btnCancel);
77
+ }
78
+ dialog.appendChild(actions);
79
+ overlay.appendChild(dialog);
80
+ document.body.appendChild(overlay);
81
+ const releaseTrap = trapFocus(dialog);
82
+ btnConfirm.focus();
83
+ function close(result) {
84
+ releaseTrap();
85
+ overlay.remove();
86
+ restoreFocus();
87
+ resolve(result);
88
+ }
89
+ btnConfirm.onclick = () => close(true);
90
+ btnCancel && (btnCancel.onclick = () => close(false));
91
+ dialog.addEventListener("keydown", (e) => {
92
+ if (e.key === "Escape") close(false);
93
+ });
94
+ });
95
+ }
96
+
97
+ // src/api/alert.ts
98
+ function alert(message) {
99
+ return openDialog({
100
+ title: message,
101
+ role: "dialog",
102
+ confirmText: "OK",
103
+ cancelText: null
104
+ });
105
+ }
106
+
107
+ // src/api/confirm.ts
108
+ function confirm(options) {
109
+ return openDialog({
110
+ ...options,
111
+ role: "alertdialog",
112
+ confirmText: "Confirm",
113
+ cancelText: "Cancel"
114
+ });
115
+ }
116
+ export {
117
+ alert,
118
+ confirm
119
+ };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "a11y-alert-dialog",
3
+ "version": "0.1.0",
4
+ "description": "Accessibility-first alert and dialog library that works without a mouse",
5
+ "license": "MIT",
6
+ "author": "Nguyen Khanh",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/nguyenkhanh/a11y-alert-dialog"
10
+ },
11
+ "type": "module",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.cjs",
16
+ "types": "./dist/index.d.ts"
17
+ }
18
+ },
19
+ "main": "./dist/index.cjs",
20
+ "module": "./dist/index.mjs",
21
+ "types": "./dist/index.d.ts",
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "A11Y.md"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "test": "playwright test",
30
+ "prepublishOnly": "npm run build && npm test"
31
+ },
32
+ "keywords": [
33
+ "accessibility",
34
+ "a11y",
35
+ "alert",
36
+ "dialog",
37
+ "modal",
38
+ "keyboard",
39
+ "wcag",
40
+ "aria"
41
+ ],
42
+ "devDependencies": {
43
+ "tsup": "^8.0.0",
44
+ "typescript": "^5.0.0",
45
+ "@playwright/test": "^1.40.0"
46
+ }
47
+ }