@compa11y/core 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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/announcer/index.cjs +49 -0
- package/dist/announcer/index.cjs.map +1 -0
- package/dist/announcer/index.d.cts +61 -0
- package/dist/announcer/index.d.ts +61 -0
- package/dist/announcer/index.js +4 -0
- package/dist/announcer/index.js.map +1 -0
- package/dist/aria/index.cjs +24 -0
- package/dist/aria/index.cjs.map +1 -0
- package/dist/aria/index.d.cts +176 -0
- package/dist/aria/index.d.ts +176 -0
- package/dist/aria/index.js +3 -0
- package/dist/aria/index.js.map +1 -0
- package/dist/chunk-24U5HHMC.js +309 -0
- package/dist/chunk-24U5HHMC.js.map +1 -0
- package/dist/chunk-2CQOLVQH.js +147 -0
- package/dist/chunk-2CQOLVQH.js.map +1 -0
- package/dist/chunk-2PUYKF2E.js +631 -0
- package/dist/chunk-2PUYKF2E.js.map +1 -0
- package/dist/chunk-2WF5Y6D7.js +175 -0
- package/dist/chunk-2WF5Y6D7.js.map +1 -0
- package/dist/chunk-CQXMBRLD.cjs +657 -0
- package/dist/chunk-CQXMBRLD.cjs.map +1 -0
- package/dist/chunk-HQOFVJFO.cjs +181 -0
- package/dist/chunk-HQOFVJFO.cjs.map +1 -0
- package/dist/chunk-NBGFFCIJ.cjs +314 -0
- package/dist/chunk-NBGFFCIJ.cjs.map +1 -0
- package/dist/chunk-V6TZIZZ4.cjs +158 -0
- package/dist/chunk-V6TZIZZ4.cjs.map +1 -0
- package/dist/chunk-XEGB27QF.cjs +78 -0
- package/dist/chunk-XEGB27QF.cjs.map +1 -0
- package/dist/chunk-Z7K2G6FX.js +66 -0
- package/dist/chunk-Z7K2G6FX.js.map +1 -0
- package/dist/focus/index.cjs +53 -0
- package/dist/focus/index.cjs.map +1 -0
- package/dist/focus/index.d.cts +139 -0
- package/dist/focus/index.d.ts +139 -0
- package/dist/focus/index.js +4 -0
- package/dist/focus/index.js.map +1 -0
- package/dist/index.cjs +573 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +241 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +343 -0
- package/dist/index.js.map +1 -0
- package/dist/keyboard/index.cjs +28 -0
- package/dist/keyboard/index.cjs.map +1 -0
- package/dist/keyboard/index.d.cts +102 -0
- package/dist/keyboard/index.d.ts +102 -0
- package/dist/keyboard/index.js +3 -0
- package/dist/keyboard/index.js.map +1 -0
- package/dist/types-DDSPmE8m.d.cts +52 -0
- package/dist/types-DDSPmE8m.d.ts +52 -0
- package/package.json +78 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
export { FOCUSABLE_SELECTORS, TABBABLE_SELECTORS, containsFocus, createFocusScope, createFocusTrap, createRovingTabindex, focusWithVisibleRing, getActiveFocusTrap, getFirstFocusable, getFocusableElements, getLastFocusSource, getLastFocusable, getNextFocusable, getPreviousFocusable, getTabbableElements, hasFocusTrap, hasVisibleFocus, initFocusVisible, isFocusVisible, isFocusable, isTabbable, isVisible, isWithinContainer, resolveElement, setFocusVisible } from './chunk-2PUYKF2E.js';
|
|
2
|
+
export { KeyboardPatterns, createKeyboardManager, createTypeAhead, getKeyCombo, normalizeKey } from './chunk-2WF5Y6D7.js';
|
|
3
|
+
export { announce, announceAssertive, announceError, announcePolite, announceProgress, announceStatus, clearAnnouncements, createAnnouncer, initAnnouncer, queueAnnouncement } from './chunk-2CQOLVQH.js';
|
|
4
|
+
export { createMediaQueryListener, getScreenReaderHints, isAndroid, isBrowser, isIOS, isMac, isTouchDevice, isWindows, prefersDarkMode, prefersHighContrast, prefersReducedMotion } from './chunk-Z7K2G6FX.js';
|
|
5
|
+
import { hasAccessibleName } from './chunk-24U5HHMC.js';
|
|
6
|
+
export { aria, buildAriaProps, hasAccessibleName, mergeAriaIds } from './chunk-24U5HHMC.js';
|
|
7
|
+
|
|
8
|
+
// src/utils/id.ts
|
|
9
|
+
var idCounter = 0;
|
|
10
|
+
var PREFIX = "compa11y";
|
|
11
|
+
function generateId(prefix) {
|
|
12
|
+
const id = ++idCounter;
|
|
13
|
+
return prefix ? `${PREFIX}-${prefix}-${id}` : `${PREFIX}-${id}`;
|
|
14
|
+
}
|
|
15
|
+
function generateIds(parts, prefix) {
|
|
16
|
+
const baseId = generateId(prefix);
|
|
17
|
+
const ids = {};
|
|
18
|
+
for (const part of parts) {
|
|
19
|
+
ids[part] = `${baseId}-${part}`;
|
|
20
|
+
}
|
|
21
|
+
return ids;
|
|
22
|
+
}
|
|
23
|
+
function resetIdCounter() {
|
|
24
|
+
idCounter = 0;
|
|
25
|
+
}
|
|
26
|
+
function createIdScope(componentName) {
|
|
27
|
+
const scopeId = generateId(componentName);
|
|
28
|
+
return {
|
|
29
|
+
id: scopeId,
|
|
30
|
+
generate: (suffix) => `${scopeId}-${suffix}`,
|
|
31
|
+
generateMultiple: (parts) => {
|
|
32
|
+
const ids = {};
|
|
33
|
+
for (const part of parts) {
|
|
34
|
+
ids[part] = `${scopeId}-${part}`;
|
|
35
|
+
}
|
|
36
|
+
return ids;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/dev/warnings.ts
|
|
42
|
+
var warningHandler = null;
|
|
43
|
+
var issuedWarnings = /* @__PURE__ */ new Set();
|
|
44
|
+
function isDev() {
|
|
45
|
+
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
46
|
+
}
|
|
47
|
+
function setWarningHandler(handler) {
|
|
48
|
+
warningHandler = handler;
|
|
49
|
+
}
|
|
50
|
+
function createWarningKey(warning) {
|
|
51
|
+
return `${warning.component}:${warning.message}`;
|
|
52
|
+
}
|
|
53
|
+
function warn(warning) {
|
|
54
|
+
if (!isDev()) return;
|
|
55
|
+
const key = createWarningKey(warning);
|
|
56
|
+
if (issuedWarnings.has(key)) return;
|
|
57
|
+
issuedWarnings.add(key);
|
|
58
|
+
if (warningHandler) {
|
|
59
|
+
warningHandler(warning);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const prefix = `[compa11y/${warning.component}]`;
|
|
63
|
+
const style = getConsoleStyle(warning.type);
|
|
64
|
+
const message = warning.suggestion ? `${warning.message}
|
|
65
|
+
|
|
66
|
+
\u{1F4A1} Suggestion: ${warning.suggestion}` : warning.message;
|
|
67
|
+
switch (warning.type) {
|
|
68
|
+
case "error":
|
|
69
|
+
console.error(`%c${prefix}%c ${message}`, style, "");
|
|
70
|
+
break;
|
|
71
|
+
case "warning":
|
|
72
|
+
console.warn(`%c${prefix}%c ${message}`, style, "");
|
|
73
|
+
break;
|
|
74
|
+
case "info":
|
|
75
|
+
console.info(`%c${prefix}%c ${message}`, style, "");
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (warning.element) {
|
|
79
|
+
console.log("Element:", warning.element);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function getConsoleStyle(type) {
|
|
83
|
+
switch (type) {
|
|
84
|
+
case "error":
|
|
85
|
+
return "background: #ff5555; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;";
|
|
86
|
+
case "warning":
|
|
87
|
+
return "background: #ffaa00; color: black; padding: 2px 4px; border-radius: 2px; font-weight: bold;";
|
|
88
|
+
case "info":
|
|
89
|
+
return "background: #5555ff; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function clearWarnings() {
|
|
93
|
+
issuedWarnings.clear();
|
|
94
|
+
}
|
|
95
|
+
var checks = {
|
|
96
|
+
/**
|
|
97
|
+
* Check for missing accessible label
|
|
98
|
+
*/
|
|
99
|
+
accessibleLabel(element, component, propName = "aria-label") {
|
|
100
|
+
if (!element) return;
|
|
101
|
+
if (!hasAccessibleName(element)) {
|
|
102
|
+
warn({
|
|
103
|
+
type: "error",
|
|
104
|
+
component,
|
|
105
|
+
message: `Missing accessible label. Screen reader users will not be able to understand this element.`,
|
|
106
|
+
suggestion: `Add ${propName}, aria-labelledby, or visible text content.`,
|
|
107
|
+
element
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Check for missing required prop
|
|
113
|
+
*/
|
|
114
|
+
requiredProp(value, propName, component) {
|
|
115
|
+
if (value === void 0 || value === null || value === "") {
|
|
116
|
+
warn({
|
|
117
|
+
type: "error",
|
|
118
|
+
component,
|
|
119
|
+
message: `Missing required prop "${propName}".`,
|
|
120
|
+
suggestion: `Provide a value for the "${propName}" prop.`
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Check for invalid ARIA role
|
|
126
|
+
*/
|
|
127
|
+
validRole(role, component, element) {
|
|
128
|
+
if (!role) return;
|
|
129
|
+
const validRoles = /* @__PURE__ */ new Set([
|
|
130
|
+
"alert",
|
|
131
|
+
"alertdialog",
|
|
132
|
+
"application",
|
|
133
|
+
"article",
|
|
134
|
+
"banner",
|
|
135
|
+
"button",
|
|
136
|
+
"cell",
|
|
137
|
+
"checkbox",
|
|
138
|
+
"columnheader",
|
|
139
|
+
"combobox",
|
|
140
|
+
"complementary",
|
|
141
|
+
"contentinfo",
|
|
142
|
+
"definition",
|
|
143
|
+
"dialog",
|
|
144
|
+
"directory",
|
|
145
|
+
"document",
|
|
146
|
+
"feed",
|
|
147
|
+
"figure",
|
|
148
|
+
"form",
|
|
149
|
+
"grid",
|
|
150
|
+
"gridcell",
|
|
151
|
+
"group",
|
|
152
|
+
"heading",
|
|
153
|
+
"img",
|
|
154
|
+
"link",
|
|
155
|
+
"list",
|
|
156
|
+
"listbox",
|
|
157
|
+
"listitem",
|
|
158
|
+
"log",
|
|
159
|
+
"main",
|
|
160
|
+
"marquee",
|
|
161
|
+
"math",
|
|
162
|
+
"menu",
|
|
163
|
+
"menubar",
|
|
164
|
+
"menuitem",
|
|
165
|
+
"menuitemcheckbox",
|
|
166
|
+
"menuitemradio",
|
|
167
|
+
"navigation",
|
|
168
|
+
"none",
|
|
169
|
+
"note",
|
|
170
|
+
"option",
|
|
171
|
+
"presentation",
|
|
172
|
+
"progressbar",
|
|
173
|
+
"radio",
|
|
174
|
+
"radiogroup",
|
|
175
|
+
"region",
|
|
176
|
+
"row",
|
|
177
|
+
"rowgroup",
|
|
178
|
+
"rowheader",
|
|
179
|
+
"scrollbar",
|
|
180
|
+
"search",
|
|
181
|
+
"searchbox",
|
|
182
|
+
"separator",
|
|
183
|
+
"slider",
|
|
184
|
+
"spinbutton",
|
|
185
|
+
"status",
|
|
186
|
+
"switch",
|
|
187
|
+
"tab",
|
|
188
|
+
"table",
|
|
189
|
+
"tablist",
|
|
190
|
+
"tabpanel",
|
|
191
|
+
"term",
|
|
192
|
+
"textbox",
|
|
193
|
+
"timer",
|
|
194
|
+
"toolbar",
|
|
195
|
+
"tooltip",
|
|
196
|
+
"tree",
|
|
197
|
+
"treegrid",
|
|
198
|
+
"treeitem"
|
|
199
|
+
]);
|
|
200
|
+
if (!validRoles.has(role)) {
|
|
201
|
+
warn({
|
|
202
|
+
type: "warning",
|
|
203
|
+
component,
|
|
204
|
+
message: `Invalid ARIA role "${role}".`,
|
|
205
|
+
suggestion: "Use a valid ARIA role from the WAI-ARIA specification.",
|
|
206
|
+
element
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
/**
|
|
211
|
+
* Check for interactive element without keyboard support
|
|
212
|
+
*/
|
|
213
|
+
keyboardAccessible(element, component, handlers) {
|
|
214
|
+
if (!element) return;
|
|
215
|
+
if (handlers.onClick && !handlers.onKeyDown) {
|
|
216
|
+
const tagName = element.tagName.toLowerCase();
|
|
217
|
+
const role = element.getAttribute("role");
|
|
218
|
+
if (["button", "a", "input", "select", "textarea"].includes(tagName)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const interactiveRoles = ["button", "link", "menuitem", "option", "tab"];
|
|
222
|
+
if (role && interactiveRoles.includes(role)) {
|
|
223
|
+
warn({
|
|
224
|
+
type: "warning",
|
|
225
|
+
component,
|
|
226
|
+
message: `Element has onClick but no onKeyDown handler.`,
|
|
227
|
+
suggestion: "Add keyboard event handling for Enter and Space keys.",
|
|
228
|
+
element
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* Check tabIndex usage
|
|
235
|
+
*/
|
|
236
|
+
tabIndex(tabIndex, component, element) {
|
|
237
|
+
if (tabIndex === void 0) return;
|
|
238
|
+
if (tabIndex > 0) {
|
|
239
|
+
warn({
|
|
240
|
+
type: "warning",
|
|
241
|
+
component,
|
|
242
|
+
message: `Positive tabIndex (${tabIndex}) disrupts natural tab order.`,
|
|
243
|
+
suggestion: "Use tabIndex={0} or tabIndex={-1} instead. Rely on DOM order for tab sequence.",
|
|
244
|
+
element
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Check for autofocus in dialogs
|
|
250
|
+
*/
|
|
251
|
+
dialogAutoFocus(hasAutoFocus, component) {
|
|
252
|
+
if (!hasAutoFocus) {
|
|
253
|
+
warn({
|
|
254
|
+
type: "info",
|
|
255
|
+
component,
|
|
256
|
+
message: "No initial focus element specified for dialog.",
|
|
257
|
+
suggestion: "Consider setting initialFocus to guide keyboard users."
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
/**
|
|
262
|
+
* Check for missing form labels
|
|
263
|
+
*/
|
|
264
|
+
formLabel(inputElement, labelId, component) {
|
|
265
|
+
if (!inputElement) return;
|
|
266
|
+
const hasLabel = labelId || inputElement.getAttribute("aria-label") || inputElement.getAttribute("aria-labelledby") || inputElement.labels?.length;
|
|
267
|
+
if (!hasLabel) {
|
|
268
|
+
warn({
|
|
269
|
+
type: "error",
|
|
270
|
+
component,
|
|
271
|
+
message: "Form input is missing an accessible label.",
|
|
272
|
+
suggestion: "Add a <label>, aria-label, or aria-labelledby.",
|
|
273
|
+
element: inputElement
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
/**
|
|
278
|
+
* Check for missing alt text on images
|
|
279
|
+
*/
|
|
280
|
+
imageAlt(element, component) {
|
|
281
|
+
if (!element || element.tagName !== "IMG") return;
|
|
282
|
+
const alt = element.getAttribute("alt");
|
|
283
|
+
const role = element.getAttribute("role");
|
|
284
|
+
if (alt === null && role !== "presentation" && role !== "none") {
|
|
285
|
+
warn({
|
|
286
|
+
type: "error",
|
|
287
|
+
component,
|
|
288
|
+
message: "Image is missing alt attribute.",
|
|
289
|
+
suggestion: 'Add alt="" for decorative images or descriptive alt text for meaningful images.',
|
|
290
|
+
element
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
function createComponentWarnings(componentName) {
|
|
296
|
+
return {
|
|
297
|
+
error: (message, suggestion, element) => warn({
|
|
298
|
+
type: "error",
|
|
299
|
+
component: componentName,
|
|
300
|
+
message,
|
|
301
|
+
suggestion,
|
|
302
|
+
element
|
|
303
|
+
}),
|
|
304
|
+
warning: (message, suggestion, element) => warn({
|
|
305
|
+
type: "warning",
|
|
306
|
+
component: componentName,
|
|
307
|
+
message,
|
|
308
|
+
suggestion,
|
|
309
|
+
element
|
|
310
|
+
}),
|
|
311
|
+
info: (message, suggestion, element) => warn({
|
|
312
|
+
type: "info",
|
|
313
|
+
component: componentName,
|
|
314
|
+
message,
|
|
315
|
+
suggestion,
|
|
316
|
+
element
|
|
317
|
+
}),
|
|
318
|
+
checks: {
|
|
319
|
+
accessibleLabel: (element, propName) => checks.accessibleLabel(element, componentName, propName),
|
|
320
|
+
requiredProp: (value, propName) => checks.requiredProp(value, propName, componentName),
|
|
321
|
+
keyboardAccessible: (element, handlers) => checks.keyboardAccessible(element, componentName, handlers),
|
|
322
|
+
tabIndex: (tabIndex, element) => checks.tabIndex(tabIndex, componentName, element)
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/index.ts
|
|
328
|
+
function initA11yKit() {
|
|
329
|
+
const cleanups = [];
|
|
330
|
+
import('./focus/index.js').then(({ initFocusVisible: initFocusVisible2 }) => {
|
|
331
|
+
cleanups.push(initFocusVisible2());
|
|
332
|
+
});
|
|
333
|
+
import('./announcer/index.js').then(({ initAnnouncer: initAnnouncer2 }) => {
|
|
334
|
+
cleanups.push(initAnnouncer2());
|
|
335
|
+
});
|
|
336
|
+
return () => {
|
|
337
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export { checks, clearWarnings, createComponentWarnings, createIdScope, generateId, generateIds, initA11yKit, resetIdCounter, setWarningHandler, warn };
|
|
342
|
+
//# sourceMappingURL=index.js.map
|
|
343
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/id.ts","../src/dev/warnings.ts","../src/index.ts"],"names":["initFocusVisible","initAnnouncer"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ID generation utilities for accessible components\n *\n * Generates unique, predictable IDs for ARIA relationships\n */\n\nlet idCounter = 0;\n\nconst PREFIX = 'compa11y';\n\n/**\n * Generate a unique ID with optional prefix\n */\nexport function generateId(prefix?: string): string {\n const id = ++idCounter;\n return prefix ? `${PREFIX}-${prefix}-${id}` : `${PREFIX}-${id}`;\n}\n\n/**\n * Generate a set of related IDs for a component\n * Useful for complex components with multiple ARIA relationships\n */\nexport function generateIds<T extends readonly string[]>(\n parts: T,\n prefix?: string\n): Record<T[number], string> {\n const baseId = generateId(prefix);\n const ids = {} as Record<string, string>;\n\n for (const part of parts) {\n ids[part] = `${baseId}-${part}`;\n }\n\n return ids as Record<T[number], string>;\n}\n\n/**\n * Reset the ID counter (useful for testing)\n */\nexport function resetIdCounter(): void {\n idCounter = 0;\n}\n\n/**\n * Create a scoped ID generator for a component instance\n */\nexport function createIdScope(componentName: string) {\n const scopeId = generateId(componentName);\n\n return {\n id: scopeId,\n generate: (suffix: string) => `${scopeId}-${suffix}`,\n generateMultiple: <T extends readonly string[]>(parts: T) => {\n const ids = {} as Record<string, string>;\n for (const part of parts) {\n ids[part] = `${scopeId}-${part}`;\n }\n return ids as Record<T[number], string>;\n },\n };\n}\n","/**\n * Developer Warnings System\n *\n * Provides development-time feedback for accessibility issues\n */\n\nimport type { DevWarning, DevWarningHandler } from '../types';\nimport { hasAccessibleName } from '../aria/aria-utils';\n\n// Global warning handler\nlet warningHandler: DevWarningHandler | null = null;\n\n// Track issued warnings to avoid duplicates\nconst issuedWarnings = new Set<string>();\n\n// Check if we're in development mode\nfunction isDev(): boolean {\n return (\n typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production'\n );\n}\n\n/**\n * Set a custom warning handler\n */\nexport function setWarningHandler(handler: DevWarningHandler | null): void {\n warningHandler = handler;\n}\n\n/**\n * Create a warning key for deduplication\n */\nfunction createWarningKey(warning: DevWarning): string {\n return `${warning.component}:${warning.message}`;\n}\n\n/**\n * Issue a development warning\n */\nexport function warn(warning: DevWarning): void {\n if (!isDev()) return;\n\n const key = createWarningKey(warning);\n if (issuedWarnings.has(key)) return;\n issuedWarnings.add(key);\n\n if (warningHandler) {\n warningHandler(warning);\n return;\n }\n\n const prefix = `[compa11y/${warning.component}]`;\n const style = getConsoleStyle(warning.type);\n\n const message = warning.suggestion\n ? `${warning.message}\\n\\n💡 Suggestion: ${warning.suggestion}`\n : warning.message;\n\n switch (warning.type) {\n case 'error':\n console.error(`%c${prefix}%c ${message}`, style, '');\n break;\n case 'warning':\n console.warn(`%c${prefix}%c ${message}`, style, '');\n break;\n case 'info':\n console.info(`%c${prefix}%c ${message}`, style, '');\n break;\n }\n\n if (warning.element) {\n console.log('Element:', warning.element);\n }\n}\n\n/**\n * Get console style for warning type\n */\nfunction getConsoleStyle(type: DevWarning['type']): string {\n switch (type) {\n case 'error':\n return 'background: #ff5555; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;';\n case 'warning':\n return 'background: #ffaa00; color: black; padding: 2px 4px; border-radius: 2px; font-weight: bold;';\n case 'info':\n return 'background: #5555ff; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;';\n }\n}\n\n/**\n * Clear issued warnings (useful for testing)\n */\nexport function clearWarnings(): void {\n issuedWarnings.clear();\n}\n\n/**\n * Pre-built warning checks\n */\nexport const checks = {\n /**\n * Check for missing accessible label\n */\n accessibleLabel(\n element: HTMLElement | null,\n component: string,\n propName = 'aria-label'\n ): void {\n if (!element) return;\n\n if (!hasAccessibleName(element)) {\n warn({\n type: 'error',\n component,\n message: `Missing accessible label. Screen reader users will not be able to understand this element.`,\n suggestion: `Add ${propName}, aria-labelledby, or visible text content.`,\n element,\n });\n }\n },\n\n /**\n * Check for missing required prop\n */\n requiredProp(value: unknown, propName: string, component: string): void {\n if (value === undefined || value === null || value === '') {\n warn({\n type: 'error',\n component,\n message: `Missing required prop \"${propName}\".`,\n suggestion: `Provide a value for the \"${propName}\" prop.`,\n });\n }\n },\n\n /**\n * Check for invalid ARIA role\n */\n validRole(\n role: string | undefined,\n component: string,\n element?: HTMLElement\n ): void {\n if (!role) return;\n\n const validRoles = new Set([\n 'alert',\n 'alertdialog',\n 'application',\n 'article',\n 'banner',\n 'button',\n 'cell',\n 'checkbox',\n 'columnheader',\n 'combobox',\n 'complementary',\n 'contentinfo',\n 'definition',\n 'dialog',\n 'directory',\n 'document',\n 'feed',\n 'figure',\n 'form',\n 'grid',\n 'gridcell',\n 'group',\n 'heading',\n 'img',\n 'link',\n 'list',\n 'listbox',\n 'listitem',\n 'log',\n 'main',\n 'marquee',\n 'math',\n 'menu',\n 'menubar',\n 'menuitem',\n 'menuitemcheckbox',\n 'menuitemradio',\n 'navigation',\n 'none',\n 'note',\n 'option',\n 'presentation',\n 'progressbar',\n 'radio',\n 'radiogroup',\n 'region',\n 'row',\n 'rowgroup',\n 'rowheader',\n 'scrollbar',\n 'search',\n 'searchbox',\n 'separator',\n 'slider',\n 'spinbutton',\n 'status',\n 'switch',\n 'tab',\n 'table',\n 'tablist',\n 'tabpanel',\n 'term',\n 'textbox',\n 'timer',\n 'toolbar',\n 'tooltip',\n 'tree',\n 'treegrid',\n 'treeitem',\n ]);\n\n if (!validRoles.has(role)) {\n warn({\n type: 'warning',\n component,\n message: `Invalid ARIA role \"${role}\".`,\n suggestion: 'Use a valid ARIA role from the WAI-ARIA specification.',\n element,\n });\n }\n },\n\n /**\n * Check for interactive element without keyboard support\n */\n keyboardAccessible(\n element: HTMLElement | null,\n component: string,\n handlers: { onClick?: unknown; onKeyDown?: unknown }\n ): void {\n if (!element) return;\n\n if (handlers.onClick && !handlers.onKeyDown) {\n const tagName = element.tagName.toLowerCase();\n const role = element.getAttribute('role');\n\n // Native interactive elements are fine\n if (['button', 'a', 'input', 'select', 'textarea'].includes(tagName)) {\n return;\n }\n\n // Elements with interactive roles need keyboard handling\n const interactiveRoles = ['button', 'link', 'menuitem', 'option', 'tab'];\n if (role && interactiveRoles.includes(role)) {\n warn({\n type: 'warning',\n component,\n message: `Element has onClick but no onKeyDown handler.`,\n suggestion: 'Add keyboard event handling for Enter and Space keys.',\n element,\n });\n }\n }\n },\n\n /**\n * Check tabIndex usage\n */\n tabIndex(\n tabIndex: number | undefined,\n component: string,\n element?: HTMLElement\n ): void {\n if (tabIndex === undefined) return;\n\n if (tabIndex > 0) {\n warn({\n type: 'warning',\n component,\n message: `Positive tabIndex (${tabIndex}) disrupts natural tab order.`,\n suggestion:\n 'Use tabIndex={0} or tabIndex={-1} instead. Rely on DOM order for tab sequence.',\n element,\n });\n }\n },\n\n /**\n * Check for autofocus in dialogs\n */\n dialogAutoFocus(hasAutoFocus: boolean, component: string): void {\n if (!hasAutoFocus) {\n warn({\n type: 'info',\n component,\n message: 'No initial focus element specified for dialog.',\n suggestion: 'Consider setting initialFocus to guide keyboard users.',\n });\n }\n },\n\n /**\n * Check for missing form labels\n */\n formLabel(\n inputElement: HTMLElement | null,\n labelId: string | undefined,\n component: string\n ): void {\n if (!inputElement) return;\n\n const hasLabel =\n labelId ||\n inputElement.getAttribute('aria-label') ||\n inputElement.getAttribute('aria-labelledby') ||\n (inputElement as HTMLInputElement).labels?.length;\n\n if (!hasLabel) {\n warn({\n type: 'error',\n component,\n message: 'Form input is missing an accessible label.',\n suggestion: 'Add a <label>, aria-label, or aria-labelledby.',\n element: inputElement,\n });\n }\n },\n\n /**\n * Check for missing alt text on images\n */\n imageAlt(element: HTMLElement | null, component: string): void {\n if (!element || element.tagName !== 'IMG') return;\n\n const alt = element.getAttribute('alt');\n const role = element.getAttribute('role');\n\n if (alt === null && role !== 'presentation' && role !== 'none') {\n warn({\n type: 'error',\n component,\n message: 'Image is missing alt attribute.',\n suggestion:\n 'Add alt=\"\" for decorative images or descriptive alt text for meaningful images.',\n element,\n });\n }\n },\n};\n\n/**\n * Create a component-scoped warning function\n */\nexport function createComponentWarnings(componentName: string) {\n return {\n error: (message: string, suggestion?: string, element?: Element) =>\n warn({\n type: 'error',\n component: componentName,\n message,\n suggestion,\n element,\n }),\n warning: (message: string, suggestion?: string, element?: Element) =>\n warn({\n type: 'warning',\n component: componentName,\n message,\n suggestion,\n element,\n }),\n info: (message: string, suggestion?: string, element?: Element) =>\n warn({\n type: 'info',\n component: componentName,\n message,\n suggestion,\n element,\n }),\n checks: {\n accessibleLabel: (element: HTMLElement | null, propName?: string) =>\n checks.accessibleLabel(element, componentName, propName),\n requiredProp: (value: unknown, propName: string) =>\n checks.requiredProp(value, propName, componentName),\n keyboardAccessible: (\n element: HTMLElement | null,\n handlers: { onClick?: unknown; onKeyDown?: unknown }\n ) => checks.keyboardAccessible(element, componentName, handlers),\n tabIndex: (tabIndex: number | undefined, element?: HTMLElement) =>\n checks.tabIndex(tabIndex, componentName, element),\n },\n };\n}\n","/**\n * @compa11y/core\n *\n * Framework-agnostic accessibility primitives\n *\n * This package provides the foundational utilities used by all\n * compa11y packages (React, Web Components, etc.)\n */\n\n// Types\nexport * from './types';\n\n// Utilities\nexport * from './utils/id';\nexport * from './utils/dom';\nexport * from './utils/platform';\n\n// Focus Management\nexport * from './focus';\n\n// Keyboard Navigation\nexport * from './keyboard';\n\n// Screen Reader Announcements\nexport * from './announcer';\n\n// ARIA Utilities\nexport * from './aria';\n\n// Development Tools\nexport * from './dev';\n\n/**\n * Initialize all compa11y systems\n * Call this once at app startup for optimal behavior\n */\nexport function initA11yKit(): () => void {\n const cleanups: Array<() => void> = [];\n\n // Dynamic imports to support tree-shaking\n // These are safe because they're from this package\n import('./focus').then(({ initFocusVisible }) => {\n cleanups.push(initFocusVisible());\n });\n\n import('./announcer').then(({ initAnnouncer }) => {\n cleanups.push(initAnnouncer());\n });\n\n return () => {\n cleanups.forEach((cleanup) => cleanup());\n };\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkHQOFVJFO_cjs = require('../chunk-HQOFVJFO.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports, "KeyboardPatterns", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () { return chunkHQOFVJFO_cjs.KeyboardPatterns; }
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(exports, "createKeyboardManager", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkHQOFVJFO_cjs.createKeyboardManager; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "createTypeAhead", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return chunkHQOFVJFO_cjs.createTypeAhead; }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "getKeyCombo", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return chunkHQOFVJFO_cjs.getKeyCombo; }
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports, "normalizeKey", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return chunkHQOFVJFO_cjs.normalizeKey; }
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=index.cjs.map
|
|
28
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard Navigation Manager
|
|
3
|
+
*
|
|
4
|
+
* Provides keyboard navigation patterns for common accessibility scenarios
|
|
5
|
+
*/
|
|
6
|
+
type KeyboardHandler = (event: KeyboardEvent) => void | boolean;
|
|
7
|
+
interface KeyboardHandlers {
|
|
8
|
+
[key: string]: KeyboardHandler;
|
|
9
|
+
}
|
|
10
|
+
interface KeyboardManagerOptions {
|
|
11
|
+
/** Prevent default for handled keys */
|
|
12
|
+
preventDefault?: boolean;
|
|
13
|
+
/** Stop propagation for handled keys */
|
|
14
|
+
stopPropagation?: boolean;
|
|
15
|
+
/** Only handle when target matches selector */
|
|
16
|
+
targetSelector?: string;
|
|
17
|
+
}
|
|
18
|
+
interface KeyboardManager {
|
|
19
|
+
/** Attach to an element */
|
|
20
|
+
attach: (element: HTMLElement) => void;
|
|
21
|
+
/** Detach from current element */
|
|
22
|
+
detach: () => void;
|
|
23
|
+
/** Add a key handler */
|
|
24
|
+
on: (key: string, handler: KeyboardHandler) => void;
|
|
25
|
+
/** Remove a key handler */
|
|
26
|
+
off: (key: string) => void;
|
|
27
|
+
/** Destroy the manager */
|
|
28
|
+
destroy: () => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalize key names for cross-browser compatibility
|
|
32
|
+
*/
|
|
33
|
+
declare function normalizeKey(event: KeyboardEvent): string;
|
|
34
|
+
/**
|
|
35
|
+
* Build a key combo string from an event
|
|
36
|
+
*/
|
|
37
|
+
declare function getKeyCombo(event: KeyboardEvent): string;
|
|
38
|
+
/**
|
|
39
|
+
* Create a keyboard manager for handling key events
|
|
40
|
+
*/
|
|
41
|
+
declare function createKeyboardManager(handlers?: KeyboardHandlers, options?: KeyboardManagerOptions): KeyboardManager;
|
|
42
|
+
/**
|
|
43
|
+
* Common keyboard navigation patterns
|
|
44
|
+
*/
|
|
45
|
+
declare const KeyboardPatterns: {
|
|
46
|
+
/**
|
|
47
|
+
* Menu/Listbox navigation
|
|
48
|
+
*/
|
|
49
|
+
menu: (options: {
|
|
50
|
+
onUp?: () => void;
|
|
51
|
+
onDown?: () => void;
|
|
52
|
+
onEnter?: () => void;
|
|
53
|
+
onEscape?: () => void;
|
|
54
|
+
onHome?: () => void;
|
|
55
|
+
onEnd?: () => void;
|
|
56
|
+
onTypeAhead?: (char: string) => void;
|
|
57
|
+
}) => KeyboardHandlers;
|
|
58
|
+
/**
|
|
59
|
+
* Tab navigation (horizontal)
|
|
60
|
+
*/
|
|
61
|
+
tabs: (options: {
|
|
62
|
+
onLeft?: () => void;
|
|
63
|
+
onRight?: () => void;
|
|
64
|
+
onHome?: () => void;
|
|
65
|
+
onEnd?: () => void;
|
|
66
|
+
}) => KeyboardHandlers;
|
|
67
|
+
/**
|
|
68
|
+
* Dialog navigation
|
|
69
|
+
*/
|
|
70
|
+
dialog: (options: {
|
|
71
|
+
onEscape?: () => void;
|
|
72
|
+
}) => KeyboardHandlers;
|
|
73
|
+
/**
|
|
74
|
+
* Grid navigation (2D)
|
|
75
|
+
*/
|
|
76
|
+
grid: (options: {
|
|
77
|
+
onUp?: () => void;
|
|
78
|
+
onDown?: () => void;
|
|
79
|
+
onLeft?: () => void;
|
|
80
|
+
onRight?: () => void;
|
|
81
|
+
onHome?: () => void;
|
|
82
|
+
onEnd?: () => void;
|
|
83
|
+
onCtrlHome?: () => void;
|
|
84
|
+
onCtrlEnd?: () => void;
|
|
85
|
+
}) => KeyboardHandlers;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Type-ahead search for menus and listboxes
|
|
89
|
+
*/
|
|
90
|
+
interface TypeAhead {
|
|
91
|
+
/** Handle a character input */
|
|
92
|
+
type: (char: string) => string | null;
|
|
93
|
+
/** Reset the search */
|
|
94
|
+
reset: () => void;
|
|
95
|
+
/** Get current search string */
|
|
96
|
+
getSearch: () => string;
|
|
97
|
+
}
|
|
98
|
+
declare function createTypeAhead(items: string[], options?: {
|
|
99
|
+
timeout?: number;
|
|
100
|
+
}): TypeAhead;
|
|
101
|
+
|
|
102
|
+
export { type KeyboardHandler, type KeyboardHandlers, type KeyboardManager, type KeyboardManagerOptions, KeyboardPatterns, type TypeAhead, createKeyboardManager, createTypeAhead, getKeyCombo, normalizeKey };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard Navigation Manager
|
|
3
|
+
*
|
|
4
|
+
* Provides keyboard navigation patterns for common accessibility scenarios
|
|
5
|
+
*/
|
|
6
|
+
type KeyboardHandler = (event: KeyboardEvent) => void | boolean;
|
|
7
|
+
interface KeyboardHandlers {
|
|
8
|
+
[key: string]: KeyboardHandler;
|
|
9
|
+
}
|
|
10
|
+
interface KeyboardManagerOptions {
|
|
11
|
+
/** Prevent default for handled keys */
|
|
12
|
+
preventDefault?: boolean;
|
|
13
|
+
/** Stop propagation for handled keys */
|
|
14
|
+
stopPropagation?: boolean;
|
|
15
|
+
/** Only handle when target matches selector */
|
|
16
|
+
targetSelector?: string;
|
|
17
|
+
}
|
|
18
|
+
interface KeyboardManager {
|
|
19
|
+
/** Attach to an element */
|
|
20
|
+
attach: (element: HTMLElement) => void;
|
|
21
|
+
/** Detach from current element */
|
|
22
|
+
detach: () => void;
|
|
23
|
+
/** Add a key handler */
|
|
24
|
+
on: (key: string, handler: KeyboardHandler) => void;
|
|
25
|
+
/** Remove a key handler */
|
|
26
|
+
off: (key: string) => void;
|
|
27
|
+
/** Destroy the manager */
|
|
28
|
+
destroy: () => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalize key names for cross-browser compatibility
|
|
32
|
+
*/
|
|
33
|
+
declare function normalizeKey(event: KeyboardEvent): string;
|
|
34
|
+
/**
|
|
35
|
+
* Build a key combo string from an event
|
|
36
|
+
*/
|
|
37
|
+
declare function getKeyCombo(event: KeyboardEvent): string;
|
|
38
|
+
/**
|
|
39
|
+
* Create a keyboard manager for handling key events
|
|
40
|
+
*/
|
|
41
|
+
declare function createKeyboardManager(handlers?: KeyboardHandlers, options?: KeyboardManagerOptions): KeyboardManager;
|
|
42
|
+
/**
|
|
43
|
+
* Common keyboard navigation patterns
|
|
44
|
+
*/
|
|
45
|
+
declare const KeyboardPatterns: {
|
|
46
|
+
/**
|
|
47
|
+
* Menu/Listbox navigation
|
|
48
|
+
*/
|
|
49
|
+
menu: (options: {
|
|
50
|
+
onUp?: () => void;
|
|
51
|
+
onDown?: () => void;
|
|
52
|
+
onEnter?: () => void;
|
|
53
|
+
onEscape?: () => void;
|
|
54
|
+
onHome?: () => void;
|
|
55
|
+
onEnd?: () => void;
|
|
56
|
+
onTypeAhead?: (char: string) => void;
|
|
57
|
+
}) => KeyboardHandlers;
|
|
58
|
+
/**
|
|
59
|
+
* Tab navigation (horizontal)
|
|
60
|
+
*/
|
|
61
|
+
tabs: (options: {
|
|
62
|
+
onLeft?: () => void;
|
|
63
|
+
onRight?: () => void;
|
|
64
|
+
onHome?: () => void;
|
|
65
|
+
onEnd?: () => void;
|
|
66
|
+
}) => KeyboardHandlers;
|
|
67
|
+
/**
|
|
68
|
+
* Dialog navigation
|
|
69
|
+
*/
|
|
70
|
+
dialog: (options: {
|
|
71
|
+
onEscape?: () => void;
|
|
72
|
+
}) => KeyboardHandlers;
|
|
73
|
+
/**
|
|
74
|
+
* Grid navigation (2D)
|
|
75
|
+
*/
|
|
76
|
+
grid: (options: {
|
|
77
|
+
onUp?: () => void;
|
|
78
|
+
onDown?: () => void;
|
|
79
|
+
onLeft?: () => void;
|
|
80
|
+
onRight?: () => void;
|
|
81
|
+
onHome?: () => void;
|
|
82
|
+
onEnd?: () => void;
|
|
83
|
+
onCtrlHome?: () => void;
|
|
84
|
+
onCtrlEnd?: () => void;
|
|
85
|
+
}) => KeyboardHandlers;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Type-ahead search for menus and listboxes
|
|
89
|
+
*/
|
|
90
|
+
interface TypeAhead {
|
|
91
|
+
/** Handle a character input */
|
|
92
|
+
type: (char: string) => string | null;
|
|
93
|
+
/** Reset the search */
|
|
94
|
+
reset: () => void;
|
|
95
|
+
/** Get current search string */
|
|
96
|
+
getSearch: () => string;
|
|
97
|
+
}
|
|
98
|
+
declare function createTypeAhead(items: string[], options?: {
|
|
99
|
+
timeout?: number;
|
|
100
|
+
}): TypeAhead;
|
|
101
|
+
|
|
102
|
+
export { type KeyboardHandler, type KeyboardHandlers, type KeyboardManager, type KeyboardManagerOptions, KeyboardPatterns, type TypeAhead, createKeyboardManager, createTypeAhead, getKeyCombo, normalizeKey };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|