@praxiis/ui 0.0.1 → 0.0.2

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.
Files changed (26) hide show
  1. package/package.json +11 -12
  2. package/src/README.md +688 -0
  3. package/src/components/CalendarStrip/CalendarStrip.a11y.ts +51 -0
  4. package/src/components/CalendarStrip/DayCard/DayCard.a11y.ts +52 -0
  5. package/src/components/EmptyState/EmptyState.a11y.ts +53 -0
  6. package/src/components/Header/Header.a11y.ts +82 -0
  7. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.a11y.ts +15 -0
  8. package/src/primitives/actions/Button/Button.a11y.ts +38 -0
  9. package/src/primitives/actions/IconButton/IconButton.a11y.ts +55 -0
  10. package/src/primitives/content/Avatar/Avatar.a11y.ts +50 -0
  11. package/src/primitives/content/Badge/Badge.a11y.ts +83 -0
  12. package/src/primitives/content/Card/Card.a11y.ts +60 -0
  13. package/src/primitives/content/Chip/Chip.a11y.ts +101 -0
  14. package/src/primitives/content/Icon/Icon.a11y.ts +43 -0
  15. package/src/primitives/feedback/ProgressBar/ProgressBar.a11y.ts +68 -0
  16. package/src/primitives/feedback/Skeleton/Skeleton.a11y.ts +46 -0
  17. package/src/primitives/feedback/Spinner/Spinner.a11y.ts +47 -0
  18. package/src/primitives/feedback/Toast/Toast.a11y.ts +75 -0
  19. package/src/primitives/inputs/Checkbox/Checkbox.a11y.ts +47 -0
  20. package/src/primitives/inputs/RadioButton/RadioButton.a11y.ts +48 -0
  21. package/src/primitives/inputs/SegmentedControl/SegmentedControl.a11y.ts +59 -0
  22. package/src/primitives/inputs/SelectSheet/SelectSheet.a11y.ts +117 -0
  23. package/src/primitives/inputs/Switch/Switch.a11y.ts +29 -0
  24. package/src/primitives/inputs/TextInput/TextInput.a11y.ts +77 -0
  25. package/src/primitives/layout/Divider/Divider.a11y.ts +55 -0
  26. package/src/primitives/overlays/Modal/Modal.a11y.ts +64 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * CalendarStrip Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the CalendarStrip container.
5
+ *
6
+ * WCAG References:
7
+ * - 1.3.1 Info and Relationships: Date range context must be conveyed to AT
8
+ * - 4.1.2 Name, Role, Value: Landmark regions need descriptive labels
9
+ *
10
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
11
+ */
12
+
13
+ /**
14
+ * Generate accessibility props for the CalendarStrip container.
15
+ *
16
+ * Accepts pre-translated strings so the function stays pure and i18n-agnostic.
17
+ *
18
+ * @example
19
+ * getCalendarStripA11y({
20
+ * label: t.calendar.datePicker.label,
21
+ * selectedLabel: t.calendar.datePicker.selected,
22
+ * daysShownLabel: t.calendar.datePicker.daysShown,
23
+ * selectedDate: new Date(2026, 1, 19),
24
+ * dateCount: 7,
25
+ * });
26
+ * // { accessible: true, accessibilityRole: "list", accessibilityLabel: "Date picker, Feb 19 selected, 7 days shown" }
27
+ */
28
+ export function getCalendarStripA11y({
29
+ label,
30
+ selectedLabel,
31
+ daysShownLabel,
32
+ selectedDate,
33
+ dateCount,
34
+ }: {
35
+ label: string;
36
+ selectedLabel: string;
37
+ daysShownLabel: string;
38
+ selectedDate: Date;
39
+ dateCount: number;
40
+ }) {
41
+ const formatted = selectedDate.toLocaleDateString([], {
42
+ month: "short",
43
+ day: "numeric",
44
+ });
45
+
46
+ return {
47
+ accessible: true,
48
+ accessibilityRole: "list" as const,
49
+ accessibilityLabel: `${label}, ${formatted} ${selectedLabel}, ${dateCount} ${daysShownLabel}`,
50
+ };
51
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * DayCard Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the DayCard component.
5
+ *
6
+ * WCAG References:
7
+ * - 1.3.1 Info and Relationships: Date context must be conveyed to AT
8
+ * - 4.1.2 Name, Role, Value: Interactive elements need role, label, and state
9
+ *
10
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
11
+ */
12
+
13
+ import { DayCardA11yParams, DayCardA11yProps } from "./DayCard.types";
14
+
15
+ /**
16
+ * Generate accessibility props for a DayCard.
17
+ *
18
+ * Combines day, month and selection state into a descriptive label
19
+ * so screen readers announce the full date and its status.
20
+ *
21
+ * @param params - Date context and state for the card
22
+ * @returns Props to spread onto the DayCard Pressable
23
+ *
24
+ * @example
25
+ * const a11yProps = getDayCardA11y({
26
+ * date: new Date(2026, 1, 19),
27
+ * isSelected: true,
28
+ * isToday: false,
29
+ * dayLabel: "Thu",
30
+ * monthLabel: "Feb",
31
+ * });
32
+ * // {
33
+ * // accessible: true,
34
+ * // accessibilityRole: "button",
35
+ * // accessibilityLabel: "Thu 19 Feb, selected",
36
+ * // accessibilityState: { selected: true },
37
+ * // }
38
+ */
39
+ export function getDayCardA11y(params: DayCardA11yParams): DayCardA11yProps {
40
+ const { date, isSelected, isToday, dayLabel, monthLabel, todayLabel, selectedLabel } = params;
41
+
42
+ const parts: string[] = [dayLabel, String(date.getDate()), monthLabel];
43
+ if (isToday) parts.push(todayLabel);
44
+ if (isSelected) parts.push(selectedLabel);
45
+
46
+ return {
47
+ accessible: true,
48
+ accessibilityRole: "button",
49
+ accessibilityLabel: parts.join(" "),
50
+ accessibilityState: { selected: isSelected },
51
+ };
52
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * EmptyState Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the EmptyState component.
5
+ *
6
+ * WCAG References:
7
+ * - 1.3.1 Info and Relationships: Content structure must be conveyed
8
+ * - 4.1.2 Name, Role, Value: Informational content needs appropriate labels
9
+ *
10
+ * EmptyState components communicate important status information to users
11
+ * and should be accessible to screen readers with proper labels.
12
+ *
13
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships
14
+ */
15
+
16
+ import { EmptyStateA11yParams, EmptyStateA11yProps } from "./EmptyState.types";
17
+
18
+ /**
19
+ * Generate accessibility props for EmptyState.
20
+ *
21
+ * Combines title and description into a comprehensive accessibility label
22
+ * for screen readers. The empty state is treated as an informational text
23
+ * region.
24
+ *
25
+ * @param params - Accessibility parameters
26
+ * @returns Object to spread onto the EmptyState container
27
+ *
28
+ * @example
29
+ * const a11yProps = getEmptyStateA11y({
30
+ * title: "No items found",
31
+ * description: "Try adjusting your search"
32
+ * });
33
+ * // {
34
+ * // accessible: true,
35
+ * // accessibilityRole: "text",
36
+ * // accessibilityLabel: "No items found. Try adjusting your search"
37
+ * // }
38
+ */
39
+ export function getEmptyStateA11y(params: EmptyStateA11yParams): EmptyStateA11yProps {
40
+ const { title, description, accessibilityLabel } = params;
41
+
42
+ // Use custom label if provided, otherwise combine title and description
43
+ let label = accessibilityLabel;
44
+ if (!label) {
45
+ label = description ? `${title}. ${description}` : title;
46
+ }
47
+
48
+ return {
49
+ accessible: true,
50
+ accessibilityRole: "text",
51
+ accessibilityLabel: label,
52
+ };
53
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Header Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the Header component.
5
+ *
6
+ * WCAG References:
7
+ * - 1.3.1 Info and Relationships: Headers need proper role
8
+ * - 2.4.6 Headings and Labels: Descriptive labels
9
+ * - 4.1.2 Name, Role, Value: Back button needs label
10
+ *
11
+ * The header uses "header" role to indicate a page/section heading.
12
+ * The back button uses "button" role with translated labels.
13
+ *
14
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/headings-and-labels
15
+ */
16
+
17
+ import { AccessibilityRole } from "react-native";
18
+ import {
19
+ HeaderA11yParams,
20
+ HeaderA11yProps,
21
+ HeaderBackButtonA11yParams,
22
+ } from "./Header.types";
23
+
24
+ /**
25
+ * Generate accessibility props for the back button.
26
+ *
27
+ * @param params - Back button accessibility parameters
28
+ * @returns Object to spread onto the back button
29
+ *
30
+ * @warning Logs console warning in dev if accessibilityLabel missing
31
+ *
32
+ * @example
33
+ * const a11yProps = getHeaderBackButtonA11y({
34
+ * accessibilityLabel: "Go back",
35
+ * accessibilityHint: "Returns to previous screen",
36
+ * });
37
+ */
38
+ export function getHeaderBackButtonA11y(
39
+ params: HeaderBackButtonA11yParams,
40
+ ): HeaderA11yProps {
41
+ const { accessibilityLabel, accessibilityHint } = params;
42
+
43
+ if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
44
+ console.warn(
45
+ "[Header] Missing accessibilityLabel for back button (WCAG 4.1.2)",
46
+ );
47
+ }
48
+
49
+ return {
50
+ accessibilityRole: "button" as AccessibilityRole,
51
+ accessibilityLabel,
52
+ accessibilityHint,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Generate accessibility props for the header container.
58
+ *
59
+ * @param params - Header accessibility parameters
60
+ * @returns Object to spread onto the header container
61
+ *
62
+ * @warning Logs console warning in dev if accessibilityLabel missing
63
+ *
64
+ * @example
65
+ * const a11yProps = getHeaderA11y({
66
+ * accessibilityLabel: "Settings page header",
67
+ * });
68
+ * // { accessibilityRole: "header", accessibilityLabel: "Settings page header" }
69
+ */
70
+ export function getHeaderA11y(params: HeaderA11yParams): HeaderA11yProps {
71
+ const { accessibilityLabel, accessibilityHint } = params;
72
+
73
+ if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
74
+ console.warn("[Header] Missing accessibilityLabel (WCAG 4.1.2)");
75
+ }
76
+
77
+ return {
78
+ accessibilityRole: "header",
79
+ accessibilityLabel,
80
+ accessibilityHint,
81
+ };
82
+ }
@@ -0,0 +1,15 @@
1
+ import type {
2
+ ScheduleItemA11yParams,
3
+ ScheduleItemA11yProps,
4
+ } from "./ScheduleItem.types";
5
+
6
+ export function getScheduleItemA11y(
7
+ params: ScheduleItemA11yParams,
8
+ ): ScheduleItemA11yProps {
9
+ const { title, timeRange, onPress } = params;
10
+ return {
11
+ accessible: true,
12
+ accessibilityRole: onPress ? "button" : "text",
13
+ accessibilityLabel: `${title}, ${timeRange}`,
14
+ };
15
+ }
@@ -0,0 +1,38 @@
1
+ import { ButtonA11yParams, ButtonA11yProps } from "./Button.types";
2
+
3
+ /**
4
+ * Get accessibility props for the Button component.
5
+ *
6
+ * Implements WCAG 4.1.2 (Name, Role, Value) requirements:
7
+ * - accessibilityRole: "button" for proper identification
8
+ * - accessibilityLabel: Required for screen readers (warns if missing)
9
+ * - accessibilityHint: Optional additional context
10
+ * - accessibilityState: Communicates disabled and busy states
11
+ *
12
+ * @param params - Accessibility parameters
13
+ * @returns Accessibility props for the button
14
+ */
15
+ export function getButtonA11y(params: ButtonA11yParams): ButtonA11yProps {
16
+ const { title, accessibilityLabel, accessibilityHint, disabled, isLoading } =
17
+ params;
18
+
19
+ if (!title && process.env.NODE_ENV !== "production") {
20
+ console.warn("[Button] Missing title (WCAG 4.1.2)");
21
+ }
22
+
23
+ if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
24
+ console.warn(
25
+ "[Button] Missing accessibilityLabel. Using title as fallback (WCAG 4.1.2)"
26
+ );
27
+ }
28
+
29
+ return {
30
+ accessibilityRole: "button",
31
+ accessibilityLabel: accessibilityLabel || title,
32
+ accessibilityHint,
33
+ accessibilityState: {
34
+ disabled: disabled || isLoading,
35
+ busy: isLoading,
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * IconButton Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the IconButton component.
5
+ *
6
+ * WCAG References:
7
+ * - 4.1.2 Name, Role, Value: Buttons need proper role and label
8
+ * - 1.1.1 Non-text Content: Icon-only buttons require text alternatives
9
+ *
10
+ * Since IconButton has no visible text, accessibilityLabel is
11
+ * REQUIRED (not optional like text buttons).
12
+ *
13
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
14
+ */
15
+
16
+ import {
17
+ IconButtonA11yParams,
18
+ IconButtonA11yProps,
19
+ } from "./IconButton.types";
20
+
21
+ /**
22
+ * Generate accessibility props for IconButton.
23
+ *
24
+ * @param params - Accessibility parameters
25
+ * @returns Object to spread onto the IconButton component
26
+ *
27
+ * @warning Logs console warning in dev if accessibilityLabel missing.
28
+ * IconButton MUST have an accessibilityLabel since there's no visible text.
29
+ *
30
+ * @example
31
+ * const a11yProps = getIconButtonA11y({
32
+ * accessibilityLabel: "Add to favorites",
33
+ * disabled: false,
34
+ * isLoading: false,
35
+ * });
36
+ */
37
+ export function getIconButtonA11y(
38
+ params: IconButtonA11yParams
39
+ ): IconButtonA11yProps {
40
+ const { accessibilityLabel, accessibilityHint, disabled, isLoading } = params;
41
+
42
+ if (!accessibilityLabel && process.env.NODE_ENV !== "production") {
43
+ console.warn("[IconButton] Missing accessibilityLabel (WCAG 4.1.2)");
44
+ }
45
+
46
+ return {
47
+ accessibilityLabel,
48
+ accessibilityHint,
49
+ accessibilityRole: "button",
50
+ accessibilityState: {
51
+ disabled: disabled || isLoading,
52
+ busy: isLoading,
53
+ },
54
+ };
55
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Avatar Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the Avatar component.
5
+ *
6
+ * WCAG References:
7
+ * - 1.1.1 Non-text Content: Images need text alternatives
8
+ * - 4.1.2 Name, Role, Value: Provide meaningful labels
9
+ *
10
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/non-text-content
11
+ */
12
+
13
+ import { AvatarA11yParams, AvatarA11yProps } from "./Avatar.types";
14
+
15
+ /**
16
+ * Generate accessibility props for Avatar.
17
+ *
18
+ * @param params - Name, custom label, status, and pre-translated strings
19
+ * @returns Object to spread onto the Avatar component
20
+ *
21
+ * @example
22
+ * const a11yProps = getAvatarA11y({
23
+ * name: "John Doe",
24
+ * status: "online",
25
+ * fallbackLabel: "User avatar",
26
+ * statusLabels: { online: "online", offline: "offline", busy: "busy", away: "away" },
27
+ * });
28
+ * // {
29
+ * // accessible: true,
30
+ * // accessibilityRole: "image",
31
+ * // accessibilityLabel: "John Doe, online"
32
+ * // }
33
+ */
34
+ export function getAvatarA11y(params: AvatarA11yParams): AvatarA11yProps {
35
+ const { name, accessibilityLabel, status, fallbackLabel, statusLabels } = params;
36
+
37
+ // Build label: custom label > name > fallback
38
+ let label = accessibilityLabel || name || fallbackLabel;
39
+
40
+ // Append status if present
41
+ if (status !== "none") {
42
+ label = `${label}, ${statusLabels[status]}`;
43
+ }
44
+
45
+ return {
46
+ accessible: true,
47
+ accessibilityRole: "image",
48
+ accessibilityLabel: label,
49
+ };
50
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Badge Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the Badge component.
5
+ *
6
+ * WCAG References:
7
+ * - 1.3.1 Info and Relationships: Convey meaning through accessible labels
8
+ * - 4.1.2 Name, Role, Value: Provide meaningful labels
9
+ *
10
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships
11
+ */
12
+
13
+ import {
14
+ BadgeA11yParams,
15
+ BadgeA11yProps,
16
+ } from "./Badge.types";
17
+
18
+ /**
19
+ * Generate accessibility props for Badge.
20
+ *
21
+ * @param params - Label, dot mode, color, custom label, and pre-translated strings
22
+ * @returns Object to spread onto the Badge component
23
+ *
24
+ * @example
25
+ * const a11yProps = getBadgeA11y({
26
+ * label: "New",
27
+ * dot: false,
28
+ * color: "primary",
29
+ * fallbackLabel: "badge",
30
+ * statusIndicatorLabel: "status indicator",
31
+ * colorLabels: { primary: "indicator", success: "success", warning: "warning", error: "error", info: "information" },
32
+ * });
33
+ * // {
34
+ * // accessible: true,
35
+ * // accessibilityRole: "text",
36
+ * // accessibilityLabel: "New"
37
+ * // }
38
+ *
39
+ * @example
40
+ * const a11yProps = getBadgeA11y({
41
+ * dot: true,
42
+ * color: "success",
43
+ * fallbackLabel: "badge",
44
+ * statusIndicatorLabel: "status indicator",
45
+ * colorLabels: { primary: "indicator", success: "success", warning: "warning", error: "error", info: "information" },
46
+ * });
47
+ * // {
48
+ * // accessible: true,
49
+ * // accessibilityRole: "text",
50
+ * // accessibilityLabel: "success status indicator"
51
+ * // }
52
+ */
53
+ export function getBadgeA11y(params: BadgeA11yParams): BadgeA11yProps {
54
+ const { label, dot, color, accessibilityLabel, fallbackLabel, statusIndicatorLabel, colorLabels } = params;
55
+
56
+ // Use custom label if provided
57
+ if (accessibilityLabel) {
58
+ return {
59
+ accessible: true,
60
+ accessibilityRole: "text",
61
+ accessibilityLabel,
62
+ };
63
+ }
64
+
65
+ // Dot mode: describe as status indicator
66
+ if (dot) {
67
+ return {
68
+ accessible: true,
69
+ accessibilityRole: "text",
70
+ accessibilityLabel: `${colorLabels[color as string] ?? String(color)} ${statusIndicatorLabel}`,
71
+ };
72
+ }
73
+
74
+ // Text/number label
75
+ const labelText =
76
+ label !== undefined && label !== null ? String(label) : fallbackLabel;
77
+
78
+ return {
79
+ accessible: true,
80
+ accessibilityRole: "text",
81
+ accessibilityLabel: labelText,
82
+ };
83
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Card Accessibility Helpers
3
+ *
4
+ * Generates accessibility props for the Card component.
5
+ *
6
+ * WCAG References:
7
+ * - 4.1.2 Name, Role, Value: Pressable cards need proper role and label
8
+ *
9
+ * Cards have conditional accessibility based on pressable state:
10
+ * - Non-pressable: role="none" (content container only)
11
+ * - Pressable: role="button" with required label
12
+ *
13
+ * @see https://www.w3.org/WAI/WCAG21/Understanding/name-role-value
14
+ */
15
+
16
+ import { CardA11yParams, CardA11yProps } from "./Card.types";
17
+
18
+ /**
19
+ * Generate accessibility props for Card.
20
+ *
21
+ * @param params - Accessibility parameters
22
+ * @returns Object to spread onto the Card component
23
+ *
24
+ * @warning Logs console warning in dev if pressable card missing accessibilityLabel
25
+ *
26
+ * @example
27
+ * // Non-pressable card
28
+ * const a11yProps = getCardA11y({ disabled: false });
29
+ * // { accessibilityRole: "none" }
30
+ *
31
+ * @example
32
+ * // Pressable card
33
+ * const a11yProps = getCardA11y({
34
+ * accessibilityLabel: "View product",
35
+ * disabled: false,
36
+ * onPress: handlePress,
37
+ * });
38
+ * // { accessibilityRole: "button", accessibilityLabel: "View product", accessibilityState: { disabled: false } }
39
+ */
40
+ export function getCardA11y(params: CardA11yParams): CardA11yProps {
41
+ const { accessibilityLabel, disabled, onPress } = params;
42
+
43
+ if (!accessibilityLabel && onPress && process.env.NODE_ENV !== "production") {
44
+ console.warn(
45
+ "[Card] Missing accessibilityLabel for pressable card (WCAG 4.1.2)"
46
+ );
47
+ }
48
+
49
+ const isPressable = !!onPress;
50
+
51
+ return {
52
+ accessibilityRole: isPressable ? "button" : "none",
53
+ accessibilityLabel: accessibilityLabel || undefined,
54
+ accessibilityState: isPressable
55
+ ? {
56
+ disabled,
57
+ }
58
+ : undefined,
59
+ };
60
+ }
@@ -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
+ }