@praxiis/ui 0.0.1 → 0.0.3
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.d.mts +3 -111
- package/dist/index.d.ts +3 -111
- package/dist/index.js +6 -200
- package/dist/index.mjs +6 -192
- package/package.json +11 -12
- package/src/README.md +688 -0
- package/src/components/CalendarStrip/CalendarStrip.a11y.ts +51 -0
- package/src/components/CalendarStrip/DayCard/DayCard.a11y.ts +52 -0
- package/src/components/EmptyState/EmptyState.a11y.ts +53 -0
- package/src/components/Header/Header.a11y.ts +82 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.a11y.ts +15 -0
- package/src/core/index.ts +1 -1
- package/src/core/restyle/index.ts +1 -1
- package/src/core/restyle/restylePresetRegistry.ts +7 -7
- package/src/index.tsx +2 -11
- package/src/primitives/actions/Button/Button.a11y.ts +38 -0
- package/src/primitives/actions/IconButton/IconButton.a11y.ts +55 -0
- package/src/primitives/content/Avatar/Avatar.a11y.ts +50 -0
- package/src/primitives/content/Badge/Badge.a11y.ts +83 -0
- package/src/primitives/content/Card/Card.a11y.ts +60 -0
- package/src/primitives/content/Chip/Chip.a11y.ts +101 -0
- package/src/primitives/content/Icon/Icon.a11y.ts +43 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.a11y.ts +68 -0
- package/src/primitives/feedback/Skeleton/Skeleton.a11y.ts +46 -0
- package/src/primitives/feedback/Spinner/Spinner.a11y.ts +47 -0
- package/src/primitives/feedback/Toast/Toast.a11y.ts +75 -0
- package/src/primitives/inputs/Checkbox/Checkbox.a11y.ts +47 -0
- package/src/primitives/inputs/RadioButton/RadioButton.a11y.ts +48 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.a11y.ts +59 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.a11y.ts +117 -0
- package/src/primitives/inputs/Switch/Switch.a11y.ts +29 -0
- package/src/primitives/inputs/TextInput/TextInput.a11y.ts +77 -0
- package/src/primitives/layout/Divider/Divider.a11y.ts +55 -0
- package/src/primitives/overlays/Modal/Modal.a11y.ts +64 -0
- package/src/providers/ThemeProvider/index.ts +0 -12
- package/src/providers/index.ts +0 -8
- package/src/providers/ThemeProvider/createTheme.ts +0 -304
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chip Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the Chip component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 4.1.2 Name, Role, Value: Provide semantic role and state
|
|
8
|
+
* - 1.3.1 Info and Relationships: Convey selected state
|
|
9
|
+
*
|
|
10
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { ChipA11yParams, ChipA11yProps } from "./Chip.types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get accessibility props for the Chip component.
|
|
17
|
+
*
|
|
18
|
+
* Implements WCAG 4.1.2 (Name, Role, Value) requirements:
|
|
19
|
+
* - accessibilityRole: "checkbox" for selectable, "button" for action/dismissible
|
|
20
|
+
* - accessibilityLabel: Required for screen readers
|
|
21
|
+
* - accessibilityHint: Optional additional context
|
|
22
|
+
* - accessibilityState: Communicates disabled and selected/checked states
|
|
23
|
+
*
|
|
24
|
+
* @param params - Accessibility parameters
|
|
25
|
+
* @returns Accessibility props for the chip
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Selectable chip
|
|
29
|
+
* const a11yProps = getChipA11y({
|
|
30
|
+
* label: "Option A",
|
|
31
|
+
* selected: true,
|
|
32
|
+
* isSelectable: true,
|
|
33
|
+
* isDismissible: false,
|
|
34
|
+
* disabled: false,
|
|
35
|
+
* });
|
|
36
|
+
* // {
|
|
37
|
+
* // accessibilityRole: "checkbox",
|
|
38
|
+
* // accessibilityLabel: "Option A",
|
|
39
|
+
* // accessibilityState: { disabled: false, checked: true }
|
|
40
|
+
* // }
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Dismissible chip
|
|
44
|
+
* const a11yProps = getChipA11y({
|
|
45
|
+
* label: "Filter",
|
|
46
|
+
* selected: false,
|
|
47
|
+
* isSelectable: false,
|
|
48
|
+
* isDismissible: true,
|
|
49
|
+
* disabled: false,
|
|
50
|
+
* });
|
|
51
|
+
* // {
|
|
52
|
+
* // accessibilityRole: "button",
|
|
53
|
+
* // accessibilityLabel: "Filter",
|
|
54
|
+
* // accessibilityHint: "Double tap to remove",
|
|
55
|
+
* // accessibilityState: { disabled: false }
|
|
56
|
+
* // }
|
|
57
|
+
*/
|
|
58
|
+
export function getChipA11y(params: ChipA11yParams): ChipA11yProps {
|
|
59
|
+
const {
|
|
60
|
+
label,
|
|
61
|
+
accessibilityLabel,
|
|
62
|
+
accessibilityHint,
|
|
63
|
+
disabled,
|
|
64
|
+
selected,
|
|
65
|
+
isSelectable,
|
|
66
|
+
isDismissible,
|
|
67
|
+
toggleHint,
|
|
68
|
+
removeHint,
|
|
69
|
+
} = params;
|
|
70
|
+
|
|
71
|
+
if (!label && process.env.NODE_ENV !== "production") {
|
|
72
|
+
console.warn("[Chip] Missing label (WCAG 4.1.2)");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const finalLabel = accessibilityLabel || label;
|
|
76
|
+
|
|
77
|
+
// Selectable chips use checkbox role
|
|
78
|
+
if (isSelectable) {
|
|
79
|
+
return {
|
|
80
|
+
accessibilityRole: "checkbox",
|
|
81
|
+
accessibilityLabel: finalLabel,
|
|
82
|
+
accessibilityHint: accessibilityHint || toggleHint,
|
|
83
|
+
accessibilityState: {
|
|
84
|
+
disabled,
|
|
85
|
+
checked: selected,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Dismissible and action chips use button role
|
|
91
|
+
return {
|
|
92
|
+
accessibilityRole: "button",
|
|
93
|
+
accessibilityLabel: finalLabel,
|
|
94
|
+
accessibilityHint:
|
|
95
|
+
accessibilityHint || (isDismissible ? removeHint : undefined),
|
|
96
|
+
accessibilityState: {
|
|
97
|
+
disabled,
|
|
98
|
+
selected: isSelectable ? selected : undefined,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the Icon component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 1.1.1 Non-text Content: Icons need text alternatives
|
|
8
|
+
* - 4.1.2 Name, Role, Value: Proper role and label required
|
|
9
|
+
*
|
|
10
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/non-text-content
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { IconA11yParams, IconA11yProps } from "./Icon.types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generate accessibility props for Icon.
|
|
17
|
+
*
|
|
18
|
+
* Icons are treated as images for accessibility purposes.
|
|
19
|
+
* Decorative icons (without accessibilityLabel) will still
|
|
20
|
+
* be announced but with no descriptive text.
|
|
21
|
+
*
|
|
22
|
+
* @param params - Accessibility parameters
|
|
23
|
+
* @returns Object to spread onto the Icon component
|
|
24
|
+
*
|
|
25
|
+
* @warning Logs console warning in dev if accessibilityLabel missing
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const a11yProps = getIconA11y({ accessibilityLabel: "Settings" });
|
|
29
|
+
* // { accessibilityRole: "image", accessibilityLabel: "Settings" }
|
|
30
|
+
*/
|
|
31
|
+
export function getIconA11y(params: IconA11yParams): IconA11yProps {
|
|
32
|
+
const { accessibilityLabel, accessibilityHint } = params;
|
|
33
|
+
|
|
34
|
+
if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
|
|
35
|
+
console.warn("[Icon] Missing accessibilityLabel (WCAG 4.1.2)");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
accessibilityRole: "image",
|
|
40
|
+
accessibilityLabel,
|
|
41
|
+
accessibilityHint,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressBar Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the ProgressBar component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 4.1.2 Name, Role, Value: Progress bars need proper role and value
|
|
8
|
+
* - The progressbar role requires min, max, and now values
|
|
9
|
+
*
|
|
10
|
+
* The progress bar uses "progressbar" role with explicit value range
|
|
11
|
+
* to provide determinate progress feedback to assistive technologies.
|
|
12
|
+
*
|
|
13
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
|
|
14
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/progressbar_role
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { clampProgress, progressToPercent } from "./ProgressBar.helpers";
|
|
18
|
+
import {
|
|
19
|
+
ProgressBarA11yParams,
|
|
20
|
+
ProgressBarA11yProps,
|
|
21
|
+
} from "./ProgressBar.types";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generate accessibility props for ProgressBar.
|
|
25
|
+
*
|
|
26
|
+
* Creates a determinate progress indicator with:
|
|
27
|
+
* - accessibilityRole: "progressbar"
|
|
28
|
+
* - accessibilityValue: { min: 0, max: 100, now: current percentage }
|
|
29
|
+
* - accessibilityLabel: Description of what's in progress
|
|
30
|
+
*
|
|
31
|
+
* @param params - Accessibility parameters
|
|
32
|
+
* @returns Object to spread onto the ProgressBar component
|
|
33
|
+
*
|
|
34
|
+
* @warning Logs console warning in dev if accessibilityLabel missing
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const a11yProps = getProgressBarA11y({
|
|
38
|
+
* progress: 0.75,
|
|
39
|
+
* accessibilityLabel: "File upload"
|
|
40
|
+
* });
|
|
41
|
+
* // {
|
|
42
|
+
* // accessibilityRole: "progressbar",
|
|
43
|
+
* // accessibilityLabel: "File upload",
|
|
44
|
+
* // accessibilityValue: { min: 0, max: 100, now: 75 }
|
|
45
|
+
* // }
|
|
46
|
+
*/
|
|
47
|
+
export function getProgressBarA11y(
|
|
48
|
+
params: ProgressBarA11yParams
|
|
49
|
+
): ProgressBarA11yProps {
|
|
50
|
+
const { progress, accessibilityLabel, fallbackLabel } = params;
|
|
51
|
+
|
|
52
|
+
if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
|
|
53
|
+
console.warn("[ProgressBar] Missing accessibilityLabel (WCAG 4.1.2)");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const clampedProgress = clampProgress(progress);
|
|
57
|
+
const progressPercent = progressToPercent(clampedProgress);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
accessibilityRole: "progressbar",
|
|
61
|
+
accessibilityLabel: accessibilityLabel || fallbackLabel,
|
|
62
|
+
accessibilityValue: {
|
|
63
|
+
min: 0,
|
|
64
|
+
max: 100,
|
|
65
|
+
now: progressPercent,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeleton Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the Skeleton component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 1.3.1 Info and Relationships: Decorative content should be hidden
|
|
8
|
+
* - 4.1.2 Name, Role, Value: Non-interactive elements need appropriate roles
|
|
9
|
+
*
|
|
10
|
+
* Skeletons are purely decorative loading placeholders and should be
|
|
11
|
+
* completely hidden from assistive technologies. The actual loading
|
|
12
|
+
* state should be communicated through other means (e.g., live regions,
|
|
13
|
+
* loading spinners with proper labels).
|
|
14
|
+
*
|
|
15
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { SkeletonA11yParams, SkeletonA11yProps } from "./Skeleton.types";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generate accessibility props for Skeleton.
|
|
22
|
+
*
|
|
23
|
+
* Skeletons are hidden from screen readers as they are decorative.
|
|
24
|
+
* Loading state should be communicated through semantic live regions
|
|
25
|
+
* or dedicated loading indicators with proper accessibility labels.
|
|
26
|
+
*
|
|
27
|
+
* @param _params - Accessibility parameters (currently unused, reserved for future)
|
|
28
|
+
* @returns Object to spread onto the Skeleton component
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const a11yProps = getSkeletonA11y({});
|
|
32
|
+
* // {
|
|
33
|
+
* // accessible: false,
|
|
34
|
+
* // accessibilityRole: "none",
|
|
35
|
+
* // accessibilityElementsHidden: true,
|
|
36
|
+
* // importantForAccessibility: "no-hide-descendants"
|
|
37
|
+
* // }
|
|
38
|
+
*/
|
|
39
|
+
export function getSkeletonA11y(_params: SkeletonA11yParams = {}): SkeletonA11yProps {
|
|
40
|
+
return {
|
|
41
|
+
accessible: false,
|
|
42
|
+
accessibilityRole: "none",
|
|
43
|
+
accessibilityElementsHidden: true,
|
|
44
|
+
importantForAccessibility: "no-hide-descendants",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the Spinner component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 4.1.2 Name, Role, Value: Spinners need proper role and label
|
|
8
|
+
* - 2.2.2 Pause, Stop, Hide: Users should be informed of loading state
|
|
9
|
+
*
|
|
10
|
+
* The spinner uses "progressbar" role with busy state to indicate
|
|
11
|
+
* that content is loading and the interface may change.
|
|
12
|
+
*
|
|
13
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { SpinnerA11yParams, SpinnerA11yProps } from "./Spinner.types";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate accessibility props for Spinner.
|
|
20
|
+
*
|
|
21
|
+
* Spinners are announced as indeterminate progress indicators.
|
|
22
|
+
* The busy state informs assistive technology that the UI is updating.
|
|
23
|
+
*
|
|
24
|
+
* @param params - Accessibility parameters
|
|
25
|
+
* @returns Object to spread onto the Spinner component
|
|
26
|
+
*
|
|
27
|
+
* @warning Logs console warning in dev if accessibilityLabel missing
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const a11yProps = getSpinnerA11y({ accessibilityLabel: "Loading messages" });
|
|
31
|
+
* // { accessibilityRole: "progressbar", accessibilityLabel: "Loading messages", accessibilityState: { busy: true } }
|
|
32
|
+
*/
|
|
33
|
+
export function getSpinnerA11y(
|
|
34
|
+
params: SpinnerA11yParams
|
|
35
|
+
): SpinnerA11yProps {
|
|
36
|
+
const { accessibilityLabel, fallbackLabel } = params;
|
|
37
|
+
|
|
38
|
+
if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
|
|
39
|
+
console.warn("[Spinner] Missing accessibilityLabel (WCAG 4.1.2)");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
accessibilityLabel: accessibilityLabel || fallbackLabel,
|
|
44
|
+
accessibilityRole: "progressbar",
|
|
45
|
+
accessibilityState: { busy: true },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the Toast component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 4.1.2 Name, Role, Value: Toasts need proper role and label
|
|
8
|
+
* - 4.1.3 Status Messages: Use aria-live regions for toast announcements
|
|
9
|
+
*
|
|
10
|
+
* The toast uses "alert" role with appropriate live region urgency:
|
|
11
|
+
* - Error: assertive (immediate announcement)
|
|
12
|
+
* - Success/Warning/Info: polite (waits for current speech to complete)
|
|
13
|
+
*
|
|
14
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/status-messages
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { ToastA11yParams, ToastA11yProps, ToastVariant } from "./Toast.types";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Determines live region urgency based on variant.
|
|
21
|
+
*
|
|
22
|
+
* @param variant - Toast variant
|
|
23
|
+
* @returns "assertive" for errors, "polite" for others
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
function getLiveRegionUrgency(
|
|
27
|
+
variant: ToastVariant
|
|
28
|
+
): "polite" | "assertive" {
|
|
29
|
+
// Errors should interrupt immediately for critical feedback
|
|
30
|
+
return variant === "error" ? "assertive" : "polite";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generate accessibility props for Toast.
|
|
35
|
+
*
|
|
36
|
+
* Toasts are announced as alerts with appropriate urgency.
|
|
37
|
+
* Error toasts interrupt immediately; others wait politely.
|
|
38
|
+
*
|
|
39
|
+
* @param params - Accessibility parameters
|
|
40
|
+
* @returns Object to spread onto the Toast component
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const a11yProps = getToastA11y({
|
|
44
|
+
* variant: "success",
|
|
45
|
+
* message: "Changes saved",
|
|
46
|
+
* });
|
|
47
|
+
* // {
|
|
48
|
+
* // accessibilityRole: "alert",
|
|
49
|
+
* // accessibilityLabel: "Success: Changes saved",
|
|
50
|
+
* // accessibilityLiveRegion: "polite"
|
|
51
|
+
* // }
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const a11yProps = getToastA11y({
|
|
55
|
+
* variant: "error",
|
|
56
|
+
* message: "Failed to save",
|
|
57
|
+
* });
|
|
58
|
+
* // {
|
|
59
|
+
* // accessibilityRole: "alert",
|
|
60
|
+
* // accessibilityLabel: "Error: Failed to save",
|
|
61
|
+
* // accessibilityLiveRegion: "assertive"
|
|
62
|
+
* // }
|
|
63
|
+
*/
|
|
64
|
+
export function getToastA11y(params: ToastA11yParams): ToastA11yProps {
|
|
65
|
+
const { variant, accessibilityLabel, message, variantPrefixes } = params;
|
|
66
|
+
|
|
67
|
+
// Build default label with translated variant prefix for context
|
|
68
|
+
const defaultLabel = `${variantPrefixes[variant]}: ${message}`;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
accessibilityLabel: accessibilityLabel ?? defaultLabel,
|
|
72
|
+
accessibilityRole: "alert",
|
|
73
|
+
accessibilityLiveRegion: getLiveRegionUrgency(variant),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the Checkbox component.
|
|
5
|
+
*
|
|
6
|
+
* WCAG References:
|
|
7
|
+
* - 4.1.2 Name, Role, Value: Checkboxes need proper role and label
|
|
8
|
+
* - 1.3.1 Info and Relationships: State must be programmatically determinable
|
|
9
|
+
*
|
|
10
|
+
* The checkbox uses "checkbox" role with checked state to indicate
|
|
11
|
+
* the current selection.
|
|
12
|
+
*
|
|
13
|
+
* @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { CheckboxA11yParams, CheckboxA11yProps } from "./Checkbox.types";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate accessibility props for Checkbox.
|
|
20
|
+
*
|
|
21
|
+
* @param params - Accessibility parameters
|
|
22
|
+
* @returns Object to spread onto the Checkbox component
|
|
23
|
+
*
|
|
24
|
+
* @warning Logs console warning in dev if both label and accessibilityLabel missing
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const a11yProps = getCheckboxA11y({
|
|
28
|
+
* label: "Accept terms",
|
|
29
|
+
* checked: true,
|
|
30
|
+
* disabled: false,
|
|
31
|
+
* });
|
|
32
|
+
* // { accessibilityRole: "checkbox", accessibilityLabel: "Accept terms", accessibilityState: { checked: true, disabled: false } }
|
|
33
|
+
*/
|
|
34
|
+
export function getCheckboxA11y(params: CheckboxA11yParams): CheckboxA11yProps {
|
|
35
|
+
const { label, accessibilityLabel, accessibilityHint, checked, disabled } = params;
|
|
36
|
+
|
|
37
|
+
if (!accessibilityLabel && !label && process.env.NODE_ENV !== "production") {
|
|
38
|
+
console.warn("[Checkbox] Missing label or accessibilityLabel (WCAG 4.1.2)");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
accessibilityLabel: accessibilityLabel || label || "Checkbox",
|
|
43
|
+
accessibilityHint,
|
|
44
|
+
accessibilityRole: "checkbox",
|
|
45
|
+
accessibilityState: { checked, disabled },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RadioButton Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Generates accessibility props for the RadioButton component.
|
|
5
|
+
* Follows WCAG 2.1 guidelines for radio button accessibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { RadioButtonA11yParams, RadioButtonA11yProps } from "./RadioButton.types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate accessibility props for RadioButton.
|
|
12
|
+
*
|
|
13
|
+
* Provides proper semantic information for screen readers:
|
|
14
|
+
* - Role: "radio" for proper radio button semantics
|
|
15
|
+
* - State: checked and disabled states
|
|
16
|
+
* - Label: Uses provided label or accessibility label
|
|
17
|
+
*
|
|
18
|
+
* @param params - Accessibility configuration
|
|
19
|
+
* @returns Accessibility props object
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const a11yProps = getRadioButtonA11y({
|
|
23
|
+
* label: "Option A",
|
|
24
|
+
* checked: true,
|
|
25
|
+
* disabled: false,
|
|
26
|
+
* });
|
|
27
|
+
* // Returns: { accessibilityRole: "radio", accessibilityState: { checked: true, disabled: false }, ... }
|
|
28
|
+
*/
|
|
29
|
+
export function getRadioButtonA11y(params: RadioButtonA11yParams): RadioButtonA11yProps {
|
|
30
|
+
const {
|
|
31
|
+
label,
|
|
32
|
+
accessibilityLabel,
|
|
33
|
+
accessibilityHint,
|
|
34
|
+
checked,
|
|
35
|
+
disabled,
|
|
36
|
+
fallbackLabel,
|
|
37
|
+
} = params;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
accessibilityLabel: accessibilityLabel || label || fallbackLabel,
|
|
41
|
+
accessibilityHint,
|
|
42
|
+
accessibilityRole: "radio",
|
|
43
|
+
accessibilityState: {
|
|
44
|
+
checked,
|
|
45
|
+
disabled,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { SegmentOption } from "./SegmentedControl.types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Accessibility props for the SegmentedControl container
|
|
5
|
+
*/
|
|
6
|
+
export function getSegmentedControlContainerA11y(
|
|
7
|
+
optionsCount: number,
|
|
8
|
+
i18n: { containerLabel: string; optionsLabel: string },
|
|
9
|
+
): {
|
|
10
|
+
accessible: boolean;
|
|
11
|
+
accessibilityRole: "radiogroup";
|
|
12
|
+
accessibilityLabel: string;
|
|
13
|
+
} {
|
|
14
|
+
return {
|
|
15
|
+
accessible: true,
|
|
16
|
+
accessibilityRole: "radiogroup",
|
|
17
|
+
accessibilityLabel: `${i18n.containerLabel} ${optionsCount} ${i18n.optionsLabel}`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Accessibility props for individual segments
|
|
23
|
+
*/
|
|
24
|
+
export function getSegmentA11y(
|
|
25
|
+
option: SegmentOption,
|
|
26
|
+
index: number,
|
|
27
|
+
totalOptions: number,
|
|
28
|
+
isSelected: boolean,
|
|
29
|
+
i18n: {
|
|
30
|
+
selectedLabel: string;
|
|
31
|
+
currentlySelectedHint: string;
|
|
32
|
+
doubleTapToSelectHint: string;
|
|
33
|
+
ofLabel: string;
|
|
34
|
+
},
|
|
35
|
+
): {
|
|
36
|
+
accessible: boolean;
|
|
37
|
+
accessibilityRole: "radio";
|
|
38
|
+
accessibilityState: {
|
|
39
|
+
selected: boolean;
|
|
40
|
+
disabled: boolean;
|
|
41
|
+
};
|
|
42
|
+
accessibilityLabel: string;
|
|
43
|
+
accessibilityHint: string;
|
|
44
|
+
} {
|
|
45
|
+
const positionText = `${index + 1} ${i18n.ofLabel} ${totalOptions}`;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
accessible: true,
|
|
49
|
+
accessibilityRole: "radio",
|
|
50
|
+
accessibilityState: {
|
|
51
|
+
selected: isSelected,
|
|
52
|
+
disabled: option.disabled ?? false,
|
|
53
|
+
},
|
|
54
|
+
accessibilityLabel: `${option.label}, ${positionText}${isSelected ? `, ${i18n.selectedLabel}` : ""}`,
|
|
55
|
+
accessibilityHint: isSelected
|
|
56
|
+
? i18n.currentlySelectedHint
|
|
57
|
+
: `${i18n.doubleTapToSelectHint} ${option.label}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelectSheet Accessibility Helpers
|
|
3
|
+
*
|
|
4
|
+
* Accessibility utilities for the SelectSheet component.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { AccessibilityProps } from "react-native";
|
|
8
|
+
|
|
9
|
+
import { SelectSheetMode, SelectSheetPosition } from "./SelectSheet.types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get accessibility props for the sheet container
|
|
13
|
+
*/
|
|
14
|
+
export function getSheetA11yProps(params: {
|
|
15
|
+
title?: string;
|
|
16
|
+
position: SelectSheetPosition;
|
|
17
|
+
accessibilityLabel?: string;
|
|
18
|
+
positionLabels: { bottom: string; top: string; center: string };
|
|
19
|
+
fallbackTitle: string;
|
|
20
|
+
}): AccessibilityProps {
|
|
21
|
+
const { title, position, accessibilityLabel, positionLabels, fallbackTitle } = params;
|
|
22
|
+
|
|
23
|
+
const positionLabel = positionLabels[position];
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
accessible: true,
|
|
27
|
+
accessibilityRole: "none",
|
|
28
|
+
accessibilityLabel:
|
|
29
|
+
accessibilityLabel || `${title || fallbackTitle} ${positionLabel}`,
|
|
30
|
+
accessibilityViewIsModal: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get accessibility props for an option row
|
|
36
|
+
*/
|
|
37
|
+
export function getOptionA11yProps(params: {
|
|
38
|
+
label: string;
|
|
39
|
+
isSelected: boolean;
|
|
40
|
+
isDisabled: boolean;
|
|
41
|
+
isMultiple: boolean;
|
|
42
|
+
index: number;
|
|
43
|
+
total: number;
|
|
44
|
+
selectedLabel: string;
|
|
45
|
+
disabledLabel: string;
|
|
46
|
+
ofLabel: string;
|
|
47
|
+
}): AccessibilityProps {
|
|
48
|
+
const { label, isSelected, isDisabled, isMultiple, index, total, selectedLabel, disabledLabel, ofLabel } = params;
|
|
49
|
+
|
|
50
|
+
const role = isMultiple ? "checkbox" : "radio";
|
|
51
|
+
const selectedText = isSelected ? `, ${selectedLabel}` : "";
|
|
52
|
+
const disabledText = isDisabled ? `, ${disabledLabel}` : "";
|
|
53
|
+
const positionText = `${index + 1} ${ofLabel} ${total}`;
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
accessible: true,
|
|
57
|
+
accessibilityRole: role,
|
|
58
|
+
accessibilityLabel: `${label}${selectedText}${disabledText}, ${positionText}`,
|
|
59
|
+
accessibilityState: {
|
|
60
|
+
checked: isSelected,
|
|
61
|
+
disabled: isDisabled,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get accessibility props for the done button
|
|
68
|
+
*/
|
|
69
|
+
export function getDoneButtonA11yProps(params: {
|
|
70
|
+
selectedCount: number;
|
|
71
|
+
mode: SelectSheetMode;
|
|
72
|
+
doneLabel: string;
|
|
73
|
+
itemsSelectedLabel: string;
|
|
74
|
+
doneHint: string;
|
|
75
|
+
}): AccessibilityProps {
|
|
76
|
+
const { selectedCount, mode, doneLabel, itemsSelectedLabel, doneHint } = params;
|
|
77
|
+
|
|
78
|
+
const countText =
|
|
79
|
+
mode === "multiple" ? ` with ${selectedCount} ${itemsSelectedLabel}` : "";
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
accessible: true,
|
|
83
|
+
accessibilityRole: "button",
|
|
84
|
+
accessibilityLabel: `${doneLabel}${countText}`,
|
|
85
|
+
accessibilityHint: doneHint,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get accessibility props for the close button
|
|
91
|
+
*/
|
|
92
|
+
export function getCloseButtonA11yProps(params: {
|
|
93
|
+
closeLabel: string;
|
|
94
|
+
closeHint: string;
|
|
95
|
+
}): AccessibilityProps {
|
|
96
|
+
return {
|
|
97
|
+
accessible: true,
|
|
98
|
+
accessibilityRole: "button",
|
|
99
|
+
accessibilityLabel: params.closeLabel,
|
|
100
|
+
accessibilityHint: params.closeHint,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get announcement for selection change
|
|
106
|
+
*/
|
|
107
|
+
export function getSelectionAnnouncement(
|
|
108
|
+
label: string,
|
|
109
|
+
isSelected: boolean,
|
|
110
|
+
isMultiple: boolean,
|
|
111
|
+
i18n: { selectedLabel: string; deselectedLabel: string },
|
|
112
|
+
): string {
|
|
113
|
+
if (isMultiple) {
|
|
114
|
+
return isSelected ? `${label} ${i18n.selectedLabel}` : `${label} ${i18n.deselectedLabel}`;
|
|
115
|
+
}
|
|
116
|
+
return `${label} ${i18n.selectedLabel}`;
|
|
117
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SwitchA11yParams, SwitchA11yProps } from "./Switch.types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get accessibility props for the Switch component.
|
|
5
|
+
*
|
|
6
|
+
* Implements WCAG 4.1.2 (Name, Role, Value) requirements:
|
|
7
|
+
* - accessibilityRole: "switch" for proper identification
|
|
8
|
+
* - accessibilityLabel: Required for screen readers (warns if missing)
|
|
9
|
+
* - accessibilityHint: Optional additional context
|
|
10
|
+
* - accessibilityState: Communicates checked and disabled states
|
|
11
|
+
*
|
|
12
|
+
* @param params - Accessibility parameters
|
|
13
|
+
* @returns Accessibility props for the switch
|
|
14
|
+
*/
|
|
15
|
+
export function getSwitchA11y(params: SwitchA11yParams): SwitchA11yProps {
|
|
16
|
+
const { label, accessibilityLabel, accessibilityHint, value, disabled } =
|
|
17
|
+
params;
|
|
18
|
+
|
|
19
|
+
if (!accessibilityLabel && !label && process.env.NODE_ENV !== "production") {
|
|
20
|
+
console.warn("[Switch] Missing label or accessibilityLabel (WCAG 4.1.2)");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
accessibilityLabel: accessibilityLabel || label || "Switch",
|
|
25
|
+
accessibilityHint,
|
|
26
|
+
accessibilityRole: "switch",
|
|
27
|
+
accessibilityState: { checked: value, disabled },
|
|
28
|
+
};
|
|
29
|
+
}
|