@a13y/react 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.
@@ -0,0 +1,725 @@
1
+ export { A as AccessibleButtonProps, P as PressEvent, U as UseAccessibleButtonProps, a as UseAccessibleButtonReturn, u as useAccessibleButton } from '../use-accessible-button-B0syf-Az.js';
2
+ import { AriaRole } from 'react';
3
+
4
+ /**
5
+ * @a13y/react - useAccessibleDialog
6
+ * Type-safe dialog/modal hook with full ARIA support
7
+ */
8
+
9
+ /**
10
+ * Props for useAccessibleDialog
11
+ */
12
+ interface UseAccessibleDialogProps {
13
+ /**
14
+ * Whether the dialog is open
15
+ */
16
+ isOpen: boolean;
17
+ /**
18
+ * Callback when dialog should close
19
+ * Called on Escape key or backdrop click
20
+ */
21
+ onClose: () => void;
22
+ /**
23
+ * Dialog title - REQUIRED for accessibility
24
+ * This becomes the aria-labelledby target
25
+ */
26
+ title: string;
27
+ /**
28
+ * Optional dialog description
29
+ * This becomes the aria-describedby target
30
+ */
31
+ description?: string;
32
+ /**
33
+ * ARIA role
34
+ * @default 'dialog'
35
+ */
36
+ role?: Extract<AriaRole, 'dialog' | 'alertdialog'>;
37
+ /**
38
+ * Whether dialog is modal (blocking)
39
+ * @default true
40
+ */
41
+ isModal?: boolean;
42
+ /**
43
+ * Whether clicking backdrop closes dialog
44
+ * @default true
45
+ */
46
+ closeOnBackdropClick?: boolean;
47
+ }
48
+ /**
49
+ * Dialog container props
50
+ */
51
+ interface DialogContainerProps {
52
+ ref: React.RefObject<HTMLElement | null>;
53
+ role: AriaRole;
54
+ 'aria-labelledby': string;
55
+ 'aria-describedby'?: string;
56
+ 'aria-modal': boolean;
57
+ tabIndex: -1;
58
+ }
59
+ /**
60
+ * Title props
61
+ */
62
+ interface DialogTitleProps {
63
+ id: string;
64
+ }
65
+ /**
66
+ * Description props
67
+ */
68
+ interface DialogDescriptionProps {
69
+ id: string;
70
+ }
71
+ /**
72
+ * Backdrop props
73
+ */
74
+ interface DialogBackdropProps {
75
+ onClick: () => void;
76
+ 'aria-hidden': true;
77
+ }
78
+ /**
79
+ * Return type
80
+ */
81
+ interface UseAccessibleDialogReturn {
82
+ dialogProps: DialogContainerProps;
83
+ titleProps: DialogTitleProps;
84
+ descriptionProps: DialogDescriptionProps | null;
85
+ backdropProps: DialogBackdropProps | null;
86
+ close: () => void;
87
+ }
88
+ /**
89
+ * Hook for creating accessible dialogs/modals
90
+ *
91
+ * Features:
92
+ * - Focus trap with Tab/Shift+Tab cycling
93
+ * - Escape key to close
94
+ * - Focus restoration on close
95
+ * - ARIA attributes (modal, labelledby, describedby)
96
+ * - Backdrop click handling
97
+ * - Development-time validation
98
+ *
99
+ * @example
100
+ * ```tsx
101
+ * const { dialogProps, titleProps, descriptionProps, backdropProps } =
102
+ * useAccessibleDialog({
103
+ * isOpen,
104
+ * onClose: () => setIsOpen(false),
105
+ * title: 'Delete Item',
106
+ * description: 'This action cannot be undone',
107
+ * });
108
+ *
109
+ * if (!isOpen) return null;
110
+ *
111
+ * return (
112
+ * <>
113
+ * <div {...backdropProps} />
114
+ * <div {...dialogProps}>
115
+ * <h2 {...titleProps}>Delete Item</h2>
116
+ * <p {...descriptionProps}>This action cannot be undone</p>
117
+ * <button onClick={close}>Cancel</button>
118
+ * </div>
119
+ * </>
120
+ * );
121
+ * ```
122
+ */
123
+ declare const useAccessibleDialog: (props: UseAccessibleDialogProps) => UseAccessibleDialogReturn;
124
+
125
+ /**
126
+ * @a13y/react - useFocusTrap
127
+ * Focus trap hook for modals and dialogs
128
+ */
129
+ /**
130
+ * Props for useFocusTrap
131
+ */
132
+ interface UseFocusTrapProps {
133
+ /**
134
+ * Whether the focus trap is active
135
+ */
136
+ isActive: boolean;
137
+ /**
138
+ * Callback when Escape key is pressed
139
+ */
140
+ onEscape?: () => void;
141
+ /**
142
+ * Whether to restore focus when trap is deactivated
143
+ * @default true
144
+ */
145
+ restoreFocus?: boolean;
146
+ /**
147
+ * Whether to auto-focus first element when activated
148
+ * @default true
149
+ */
150
+ autoFocus?: boolean;
151
+ }
152
+ /**
153
+ * Return type
154
+ */
155
+ interface UseFocusTrapReturn {
156
+ /**
157
+ * Ref to attach to the container element
158
+ */
159
+ trapRef: React.RefObject<HTMLElement | null>;
160
+ }
161
+ /**
162
+ * Hook for creating focus traps
163
+ *
164
+ * Features:
165
+ * - Traps Tab/Shift+Tab within container
166
+ * - Handles Escape key
167
+ * - Restores focus on deactivation
168
+ * - Auto-focuses first element
169
+ * - Development-time validation
170
+ *
171
+ * @example
172
+ * ```tsx
173
+ * const { trapRef } = useFocusTrap({
174
+ * isActive: isOpen,
175
+ * onEscape: () => setIsOpen(false),
176
+ * });
177
+ *
178
+ * return (
179
+ * <div ref={trapRef} role="dialog">
180
+ * <button>Close</button>
181
+ * </div>
182
+ * );
183
+ * ```
184
+ */
185
+ declare const useFocusTrap: (props: UseFocusTrapProps) => UseFocusTrapReturn;
186
+
187
+ /**
188
+ * @a13y/react - useKeyboardNavigation
189
+ * Roving tabindex keyboard navigation hook
190
+ */
191
+ /**
192
+ * Navigation orientation
193
+ */
194
+ type Orientation = 'horizontal' | 'vertical' | 'both';
195
+ /**
196
+ * Props for useKeyboardNavigation
197
+ */
198
+ interface UseKeyboardNavigationProps {
199
+ /**
200
+ * Navigation orientation
201
+ * - 'horizontal': Arrow Left/Right
202
+ * - 'vertical': Arrow Up/Down
203
+ * - 'both': All arrow keys
204
+ */
205
+ orientation: Orientation;
206
+ /**
207
+ * Whether to loop at boundaries
208
+ * @default false
209
+ */
210
+ loop?: boolean;
211
+ /**
212
+ * Callback when navigation occurs
213
+ */
214
+ onNavigate?: (index: number) => void;
215
+ /**
216
+ * Initial focused index
217
+ * @default 0
218
+ */
219
+ defaultIndex?: number;
220
+ /**
221
+ * Controlled current index
222
+ */
223
+ currentIndex?: number;
224
+ }
225
+ /**
226
+ * Item props for navigable items
227
+ */
228
+ interface NavigableItemProps {
229
+ ref: (element: HTMLElement | null) => void;
230
+ tabIndex: number;
231
+ onKeyDown: (event: React.KeyboardEvent) => void;
232
+ 'data-index': number;
233
+ }
234
+ /**
235
+ * Return type
236
+ */
237
+ interface UseKeyboardNavigationReturn {
238
+ /**
239
+ * Current focused index
240
+ */
241
+ currentIndex: number;
242
+ /**
243
+ * Navigate to specific index
244
+ */
245
+ setCurrentIndex: (index: number) => void;
246
+ /**
247
+ * Get props for navigable item
248
+ */
249
+ getItemProps: (index: number) => NavigableItemProps;
250
+ /**
251
+ * Container props
252
+ */
253
+ containerProps: {
254
+ role: 'toolbar' | 'listbox' | 'menu';
255
+ 'aria-orientation': Orientation;
256
+ };
257
+ }
258
+ /**
259
+ * Hook for keyboard navigation with roving tabindex
260
+ *
261
+ * Features:
262
+ * - Arrow key navigation
263
+ * - Home/End navigation
264
+ * - Roving tabindex pattern
265
+ * - Automatic focus management
266
+ * - Development-time validation
267
+ *
268
+ * @example
269
+ * ```tsx
270
+ * const { containerProps, getItemProps, currentIndex } =
271
+ * useKeyboardNavigation({
272
+ * orientation: 'horizontal',
273
+ * loop: true,
274
+ * });
275
+ *
276
+ * return (
277
+ * <div {...containerProps}>
278
+ * {items.map((item, index) => (
279
+ * <button key={index} {...getItemProps(index)}>
280
+ * {item.label}
281
+ * </button>
282
+ * ))}
283
+ * </div>
284
+ * );
285
+ * ```
286
+ */
287
+ declare const useKeyboardNavigation: (props: UseKeyboardNavigationProps) => UseKeyboardNavigationReturn;
288
+
289
+ /**
290
+ * @a13y/react - useAccessibleForm Hook
291
+ * Form management with accessibility built-in
292
+ */
293
+ /**
294
+ * Field-level validation function
295
+ */
296
+ type FieldValidator<T> = (value: T) => string | true;
297
+ /**
298
+ * Form-level validation function (for cross-field validation)
299
+ */
300
+ type FormValidator<T> = (values: T) => Record<string, string> | null;
301
+ /**
302
+ * Field configuration
303
+ */
304
+ interface FieldConfig<T> {
305
+ /**
306
+ * Initial value
307
+ */
308
+ initialValue: T;
309
+ /**
310
+ * Validation function (optional)
311
+ * Return true if valid, or error message if invalid
312
+ */
313
+ validate?: FieldValidator<T>;
314
+ /**
315
+ * Required field
316
+ * @default false
317
+ */
318
+ required?: boolean;
319
+ /**
320
+ * Custom required message
321
+ */
322
+ requiredMessage?: string;
323
+ }
324
+ /**
325
+ * Form configuration
326
+ */
327
+ interface FormConfig<T extends Record<string, unknown>> {
328
+ /**
329
+ * Field configurations
330
+ */
331
+ fields: {
332
+ [K in keyof T]: FieldConfig<T[K]>;
333
+ };
334
+ /**
335
+ * Form-level validation (optional)
336
+ * For cross-field validation
337
+ */
338
+ validate?: FormValidator<T>;
339
+ /**
340
+ * Called when form is successfully submitted
341
+ */
342
+ onSubmit: (values: T) => void | Promise<void>;
343
+ /**
344
+ * Auto-focus first error on validation failure
345
+ * @default true
346
+ */
347
+ autoFocusError?: boolean;
348
+ /**
349
+ * Announce errors to screen readers
350
+ * @default true
351
+ */
352
+ announceErrors?: boolean;
353
+ /**
354
+ * Validate on blur
355
+ * @default true
356
+ */
357
+ validateOnBlur?: boolean;
358
+ /**
359
+ * Validate on change (after first blur)
360
+ * @default true
361
+ */
362
+ validateOnChange?: boolean;
363
+ }
364
+ /**
365
+ * Form state
366
+ */
367
+ interface FormState<T extends Record<string, unknown>> {
368
+ /**
369
+ * Current form values
370
+ */
371
+ values: T;
372
+ /**
373
+ * Field errors
374
+ */
375
+ errors: Partial<Record<keyof T, string>>;
376
+ /**
377
+ * Fields that have been touched (blurred at least once)
378
+ */
379
+ touched: Partial<Record<keyof T, boolean>>;
380
+ /**
381
+ * Is form submitting
382
+ */
383
+ isSubmitting: boolean;
384
+ /**
385
+ * Is form valid (no errors)
386
+ */
387
+ isValid: boolean;
388
+ /**
389
+ * Has form been submitted at least once
390
+ */
391
+ hasSubmitted: boolean;
392
+ }
393
+ /**
394
+ * Field props for binding to input elements
395
+ */
396
+ interface FieldProps<T> {
397
+ name: string;
398
+ value: T;
399
+ onChange: (value: T) => void;
400
+ onBlur: () => void;
401
+ 'aria-invalid': boolean;
402
+ 'aria-describedby'?: string;
403
+ 'aria-required'?: boolean;
404
+ }
405
+ /**
406
+ * Form return value
407
+ */
408
+ interface UseAccessibleFormReturn<T extends Record<string, unknown>> {
409
+ /**
410
+ * Form state
411
+ */
412
+ state: FormState<T>;
413
+ /**
414
+ * Get props for a field
415
+ */
416
+ getFieldProps: <K extends keyof T>(name: K, options?: {
417
+ 'aria-describedby'?: string;
418
+ }) => FieldProps<T[K]>;
419
+ /**
420
+ * Set field value programmatically
421
+ */
422
+ setFieldValue: <K extends keyof T>(name: K, value: T[K]) => void;
423
+ /**
424
+ * Set field error programmatically
425
+ */
426
+ setFieldError: <K extends keyof T>(name: K, error: string) => void;
427
+ /**
428
+ * Set multiple field errors at once
429
+ */
430
+ setErrors: (errors: Partial<Record<keyof T, string>>) => void;
431
+ /**
432
+ * Validate a single field
433
+ */
434
+ validateField: <K extends keyof T>(name: K) => boolean;
435
+ /**
436
+ * Validate entire form
437
+ */
438
+ validateForm: () => boolean;
439
+ /**
440
+ * Handle form submit
441
+ */
442
+ handleSubmit: (e?: React.FormEvent) => void;
443
+ /**
444
+ * Reset form to initial values
445
+ */
446
+ reset: () => void;
447
+ /**
448
+ * Clear all errors
449
+ */
450
+ clearErrors: () => void;
451
+ /**
452
+ * Field refs for focus management
453
+ */
454
+ fieldRefs: Map<keyof T, HTMLElement>;
455
+ }
456
+ /**
457
+ * useAccessibleForm Hook
458
+ *
459
+ * Comprehensive form management with accessibility built-in:
460
+ * - Automatic error announcements to screen readers
461
+ * - Auto-focus first error field on validation failure
462
+ * - Required field validation
463
+ * - Field-level and form-level validation
464
+ * - aria-invalid and aria-describedby management
465
+ * - Touch tracking for better UX
466
+ *
467
+ * Pattern Explanation:
468
+ * - Each field gets automatic ARIA attributes
469
+ * - Errors are announced via screen reader
470
+ * - First error field receives focus on submit
471
+ * - Validation can run on blur or change
472
+ * - Required fields enforced via TypeScript
473
+ *
474
+ * @example
475
+ * ```tsx
476
+ * const form = useAccessibleForm({
477
+ * fields: {
478
+ * email: {
479
+ * initialValue: '',
480
+ * required: true,
481
+ * validate: (value) => {
482
+ * if (!value.includes('@')) return 'Invalid email';
483
+ * return true;
484
+ * },
485
+ * },
486
+ * password: {
487
+ * initialValue: '',
488
+ * required: true,
489
+ * validate: (value) => {
490
+ * if (value.length < 8) return 'Password must be at least 8 characters';
491
+ * return true;
492
+ * },
493
+ * },
494
+ * },
495
+ * onSubmit: (values) => {
496
+ * console.log('Form submitted:', values);
497
+ * },
498
+ * });
499
+ *
500
+ * return (
501
+ * <form onSubmit={form.handleSubmit}>
502
+ * <input {...form.getFieldProps('email')} type="email" />
503
+ * {form.state.errors.email && (
504
+ * <span id="email-error">{form.state.errors.email}</span>
505
+ * )}
506
+ * </form>
507
+ * );
508
+ * ```
509
+ */
510
+ declare const useAccessibleForm: <T extends Record<string, unknown>>(config: FormConfig<T>) => UseAccessibleFormReturn<T>;
511
+
512
+ /**
513
+ * @a13y/react - useFormField Hook
514
+ * Individual form field management with accessibility built-in
515
+ */
516
+ /**
517
+ * Props for useFormField
518
+ */
519
+ interface UseFormFieldProps<T = string> {
520
+ /**
521
+ * Field label (required for accessibility)
522
+ * This will be used as the accessible name
523
+ */
524
+ label: string;
525
+ /**
526
+ * Initial field value
527
+ */
528
+ initialValue?: T;
529
+ /**
530
+ * Validation function
531
+ * Return true if valid, or error message if invalid
532
+ */
533
+ validate?: (value: T) => string | true;
534
+ /**
535
+ * Is field required
536
+ * @default false
537
+ */
538
+ required?: boolean;
539
+ /**
540
+ * Custom required message
541
+ */
542
+ requiredMessage?: string;
543
+ /**
544
+ * Help text to display
545
+ */
546
+ helpText?: string;
547
+ /**
548
+ * Validate on blur
549
+ * @default true
550
+ */
551
+ validateOnBlur?: boolean;
552
+ /**
553
+ * Validate on change (after first blur)
554
+ * @default true
555
+ */
556
+ validateOnChange?: boolean;
557
+ /**
558
+ * Announce errors to screen readers
559
+ * @default true
560
+ */
561
+ announceErrors?: boolean;
562
+ /**
563
+ * Called when value changes
564
+ */
565
+ onChange?: (value: T) => void;
566
+ /**
567
+ * Called when field is blurred
568
+ */
569
+ onBlur?: () => void;
570
+ }
571
+ /**
572
+ * Return value from useFormField
573
+ */
574
+ interface UseFormFieldReturn<T = string> {
575
+ /**
576
+ * Field ID (generated)
577
+ */
578
+ id: string;
579
+ /**
580
+ * Label ID (generated)
581
+ */
582
+ labelId: string;
583
+ /**
584
+ * Error message ID (generated)
585
+ */
586
+ errorId: string;
587
+ /**
588
+ * Help text ID (generated)
589
+ */
590
+ helpTextId: string;
591
+ /**
592
+ * Current field value
593
+ */
594
+ value: T;
595
+ /**
596
+ * Current error message (if any)
597
+ */
598
+ error: string | null;
599
+ /**
600
+ * Has field been touched (blurred at least once)
601
+ */
602
+ isTouched: boolean;
603
+ /**
604
+ * Is field valid
605
+ */
606
+ isValid: boolean;
607
+ /**
608
+ * Set field value
609
+ */
610
+ setValue: (value: T) => void;
611
+ /**
612
+ * Set field error
613
+ */
614
+ setError: (error: string | null) => void;
615
+ /**
616
+ * Validate field
617
+ */
618
+ validate: () => boolean;
619
+ /**
620
+ * Clear error
621
+ */
622
+ clearError: () => void;
623
+ /**
624
+ * Reset field to initial value
625
+ */
626
+ reset: () => void;
627
+ /**
628
+ * Props for label element
629
+ */
630
+ labelProps: {
631
+ id: string;
632
+ htmlFor: string;
633
+ };
634
+ /**
635
+ * Props for input element
636
+ */
637
+ inputProps: {
638
+ id: string;
639
+ name: string;
640
+ value: T;
641
+ onChange: (value: T) => void;
642
+ onBlur: () => void;
643
+ 'aria-labelledby': string;
644
+ 'aria-describedby'?: string;
645
+ 'aria-invalid': boolean;
646
+ 'aria-required'?: boolean;
647
+ ref: React.RefObject<HTMLInputElement | null>;
648
+ };
649
+ /**
650
+ * Props for error message element
651
+ */
652
+ errorProps: {
653
+ id: string;
654
+ role: 'alert';
655
+ 'aria-live': 'polite';
656
+ };
657
+ /**
658
+ * Props for help text element
659
+ */
660
+ helpTextProps: {
661
+ id: string;
662
+ };
663
+ /**
664
+ * Field ref for focus management
665
+ */
666
+ fieldRef: React.RefObject<HTMLInputElement | null>;
667
+ }
668
+ /**
669
+ * useFormField Hook
670
+ *
671
+ * Manages a single form field with accessibility built-in:
672
+ * - Required label via TypeScript
673
+ * - Automatic ARIA attributes
674
+ * - Error announcements to screen readers
675
+ * - Help text support
676
+ * - Validation on blur/change
677
+ * - ID generation for ARIA relationships
678
+ *
679
+ * Pattern Explanation:
680
+ * - Label is required (enforced at compile-time)
681
+ * - aria-labelledby automatically connects label to input
682
+ * - aria-describedby automatically connects errors and help text
683
+ * - aria-invalid automatically set when error exists
684
+ * - Errors announced to screen readers when they appear
685
+ *
686
+ * @example
687
+ * ```tsx
688
+ * const emailField = useFormField({
689
+ * label: 'Email Address',
690
+ * required: true,
691
+ * validate: (value) => {
692
+ * if (!value.includes('@')) return 'Invalid email address';
693
+ * return true;
694
+ * },
695
+ * helpText: 'We will never share your email',
696
+ * });
697
+ *
698
+ * return (
699
+ * <div>
700
+ * <label {...emailField.labelProps}>{emailField.label}</label>
701
+ * <input
702
+ * {...emailField.inputProps}
703
+ * type="email"
704
+ * onChange={(e) => emailField.inputProps.onChange(e.target.value)}
705
+ * />
706
+ * {emailField.helpText && (
707
+ * <span {...emailField.helpTextProps}>{emailField.helpText}</span>
708
+ * )}
709
+ * {emailField.error && (
710
+ * <span {...emailField.errorProps}>{emailField.error}</span>
711
+ * )}
712
+ * </div>
713
+ * );
714
+ * ```
715
+ */
716
+ declare const useFormField: <T = string>(props: UseFormFieldProps<T>) => UseFormFieldReturn<T>;
717
+ /**
718
+ * Helper type to ensure label is provided
719
+ * Use this in component props to enforce accessible labels
720
+ */
721
+ type RequireLabel<T> = T & {
722
+ label: string;
723
+ };
724
+
725
+ export { type DialogBackdropProps, type DialogContainerProps, type DialogDescriptionProps, type DialogTitleProps, type FieldConfig, type FieldProps, type FieldValidator, type FormConfig, type FormState, type FormValidator, type NavigableItemProps, type RequireLabel, type UseAccessibleDialogProps, type UseAccessibleDialogReturn, type UseAccessibleFormReturn, type UseFocusTrapProps, type UseFocusTrapReturn, type UseFormFieldProps, type UseFormFieldReturn, type UseKeyboardNavigationProps, type UseKeyboardNavigationReturn, useAccessibleDialog, useAccessibleForm, useFocusTrap, useFormField, useKeyboardNavigation };