@praxiis/ui 0.0.1

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 (187) hide show
  1. package/dist/index.d.mts +52556 -0
  2. package/dist/index.d.ts +52556 -0
  3. package/dist/index.js +8753 -0
  4. package/dist/index.mjs +8777 -0
  5. package/package.json +70 -0
  6. package/src/__test-utils__/index.tsx +39 -0
  7. package/src/components/CalendarStrip/CalendarStrip.helpers.ts +106 -0
  8. package/src/components/CalendarStrip/CalendarStrip.tsx +83 -0
  9. package/src/components/CalendarStrip/CalendarStrip.types.ts +133 -0
  10. package/src/components/CalendarStrip/DayCard/DayCard.helpers.ts +44 -0
  11. package/src/components/CalendarStrip/DayCard/DayCard.tsx +71 -0
  12. package/src/components/CalendarStrip/DayCard/DayCard.types.ts +134 -0
  13. package/src/components/CalendarStrip/DayCard/index.ts +2 -0
  14. package/src/components/CalendarStrip/DayCard/useDayCardLogic.ts +45 -0
  15. package/src/components/CalendarStrip/index.ts +9 -0
  16. package/src/components/CalendarStrip/useCalendarStripLogic.ts +53 -0
  17. package/src/components/EmptyState/EmptyState.helpers.ts +104 -0
  18. package/src/components/EmptyState/EmptyState.tsx +205 -0
  19. package/src/components/EmptyState/EmptyState.types.ts +213 -0
  20. package/src/components/EmptyState/index.ts +44 -0
  21. package/src/components/EmptyState/useEmptyStateLogic.ts +131 -0
  22. package/src/components/Header/Header.helpers.ts +93 -0
  23. package/src/components/Header/Header.tsx +185 -0
  24. package/src/components/Header/Header.types.ts +153 -0
  25. package/src/components/Header/index.ts +44 -0
  26. package/src/components/Header/useHeaderLogic.ts +146 -0
  27. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.helpers.ts +50 -0
  28. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.tsx +78 -0
  29. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.types.ts +99 -0
  30. package/src/components/ScheduleItem/ScheduleItem/index.ts +16 -0
  31. package/src/components/ScheduleItem/ScheduleItem/useScheduleItemLogic.ts +31 -0
  32. package/src/components/ScheduleItem/index.ts +15 -0
  33. package/src/components/index.ts +40 -0
  34. package/src/core/index.ts +34 -0
  35. package/src/core/restyle/RestyleThemeProviderWrapper.tsx +31 -0
  36. package/src/core/restyle/index.ts +38 -0
  37. package/src/core/restyle/restylePresetRegistry.ts +195 -0
  38. package/src/core/restyle/restyleTheme.ts +1352 -0
  39. package/src/core/restyle/restyleTypes.ts +8 -0
  40. package/src/core/restyle/useRestyleTheme.ts +10 -0
  41. package/src/hooks/animations/index.ts +3 -0
  42. package/src/hooks/animations/useAnimatedValue.ts +10 -0
  43. package/src/hooks/animations/useEntranceAnimation.ts +106 -0
  44. package/src/hooks/animations/usePulseAnimation.ts +63 -0
  45. package/src/hooks/index.ts +30 -0
  46. package/src/hooks/useReducedMotion.ts +60 -0
  47. package/src/i18n/index.ts +2 -0
  48. package/src/i18n/labels/en.ts +120 -0
  49. package/src/i18n/labels/es.ts +120 -0
  50. package/src/i18n/labels/index.ts +6 -0
  51. package/src/i18n/labels/types.ts +165 -0
  52. package/src/index.tsx +215 -0
  53. package/src/primitives/actions/Button/Button.helpers.ts +243 -0
  54. package/src/primitives/actions/Button/Button.tsx +198 -0
  55. package/src/primitives/actions/Button/Button.types.ts +207 -0
  56. package/src/primitives/actions/Button/index.ts +41 -0
  57. package/src/primitives/actions/Button/useButtonLogic.ts +160 -0
  58. package/src/primitives/actions/IconButton/IconButton.helpers.ts +235 -0
  59. package/src/primitives/actions/IconButton/IconButton.tsx +177 -0
  60. package/src/primitives/actions/IconButton/IconButton.types.ts +273 -0
  61. package/src/primitives/actions/IconButton/index.ts +30 -0
  62. package/src/primitives/actions/IconButton/useIconButtonLogic.ts +172 -0
  63. package/src/primitives/actions/index.ts +20 -0
  64. package/src/primitives/content/Avatar/Avatar.helpers.ts +177 -0
  65. package/src/primitives/content/Avatar/Avatar.tsx +199 -0
  66. package/src/primitives/content/Avatar/Avatar.types.ts +222 -0
  67. package/src/primitives/content/Avatar/index.ts +46 -0
  68. package/src/primitives/content/Avatar/useAvatarLogic.ts +149 -0
  69. package/src/primitives/content/Badge/Badge.helpers.ts +175 -0
  70. package/src/primitives/content/Badge/Badge.tsx +174 -0
  71. package/src/primitives/content/Badge/Badge.types.ts +223 -0
  72. package/src/primitives/content/Badge/index.ts +40 -0
  73. package/src/primitives/content/Badge/useBadgeLogic.ts +128 -0
  74. package/src/primitives/content/Card/Card.helpers.ts +27 -0
  75. package/src/primitives/content/Card/Card.tsx +123 -0
  76. package/src/primitives/content/Card/Card.types.ts +95 -0
  77. package/src/primitives/content/Card/index.ts +20 -0
  78. package/src/primitives/content/Card/useCardLogic.ts +48 -0
  79. package/src/primitives/content/Chip/Chip.helpers.ts +304 -0
  80. package/src/primitives/content/Chip/Chip.tsx +205 -0
  81. package/src/primitives/content/Chip/Chip.types.ts +234 -0
  82. package/src/primitives/content/Chip/index.ts +47 -0
  83. package/src/primitives/content/Chip/useChipLogic.ts +167 -0
  84. package/src/primitives/content/Icon/Icon.helpers.ts +54 -0
  85. package/src/primitives/content/Icon/Icon.tsx +110 -0
  86. package/src/primitives/content/Icon/Icon.types.ts +95 -0
  87. package/src/primitives/content/Icon/index.ts +20 -0
  88. package/src/primitives/content/Icon/useIconLogic.ts +73 -0
  89. package/src/primitives/content/index.ts +45 -0
  90. package/src/primitives/feedback/ProgressBar/ProgressBar.helpers.ts +122 -0
  91. package/src/primitives/feedback/ProgressBar/ProgressBar.tsx +154 -0
  92. package/src/primitives/feedback/ProgressBar/ProgressBar.types.ts +178 -0
  93. package/src/primitives/feedback/ProgressBar/index.ts +17 -0
  94. package/src/primitives/feedback/ProgressBar/useProgressBarLogic.ts +120 -0
  95. package/src/primitives/feedback/Skeleton/Skeleton.helpers.ts +145 -0
  96. package/src/primitives/feedback/Skeleton/Skeleton.tsx +155 -0
  97. package/src/primitives/feedback/Skeleton/Skeleton.types.ts +223 -0
  98. package/src/primitives/feedback/Skeleton/index.ts +44 -0
  99. package/src/primitives/feedback/Skeleton/useSkeletonLogic.ts +125 -0
  100. package/src/primitives/feedback/Spinner/Spinner.helpers.ts +40 -0
  101. package/src/primitives/feedback/Spinner/Spinner.tsx +105 -0
  102. package/src/primitives/feedback/Spinner/Spinner.types.ts +114 -0
  103. package/src/primitives/feedback/Spinner/index.ts +18 -0
  104. package/src/primitives/feedback/Spinner/useSpinnerLogic.ts +84 -0
  105. package/src/primitives/feedback/Toast/Toast.helpers.ts +163 -0
  106. package/src/primitives/feedback/Toast/Toast.tsx +190 -0
  107. package/src/primitives/feedback/Toast/Toast.types.ts +270 -0
  108. package/src/primitives/feedback/Toast/ToastContext.tsx +96 -0
  109. package/src/primitives/feedback/Toast/ToastProvider.tsx +241 -0
  110. package/src/primitives/feedback/Toast/index.ts +59 -0
  111. package/src/primitives/feedback/Toast/useToastLogic.ts +112 -0
  112. package/src/primitives/feedback/index.ts +45 -0
  113. package/src/primitives/index.ts +158 -0
  114. package/src/primitives/inputs/Checkbox/Checkbox.helpers.ts +132 -0
  115. package/src/primitives/inputs/Checkbox/Checkbox.tsx +150 -0
  116. package/src/primitives/inputs/Checkbox/Checkbox.types.ts +106 -0
  117. package/src/primitives/inputs/Checkbox/index.ts +30 -0
  118. package/src/primitives/inputs/Checkbox/useCheckboxLogic.ts +121 -0
  119. package/src/primitives/inputs/RadioButton/RadioButton.helpers.ts +123 -0
  120. package/src/primitives/inputs/RadioButton/RadioButton.tsx +159 -0
  121. package/src/primitives/inputs/RadioButton/RadioButton.types.ts +106 -0
  122. package/src/primitives/inputs/RadioButton/index.ts +25 -0
  123. package/src/primitives/inputs/RadioButton/useRadioButtonLogic.ts +117 -0
  124. package/src/primitives/inputs/SegmentedControl/SegmentedControl.helpers.ts +174 -0
  125. package/src/primitives/inputs/SegmentedControl/SegmentedControl.tsx +224 -0
  126. package/src/primitives/inputs/SegmentedControl/SegmentedControl.types.ts +187 -0
  127. package/src/primitives/inputs/SegmentedControl/index.ts +39 -0
  128. package/src/primitives/inputs/SegmentedControl/useSegmentedControlLogic.ts +151 -0
  129. package/src/primitives/inputs/SelectSheet/SelectSheet.helpers.ts +147 -0
  130. package/src/primitives/inputs/SelectSheet/SelectSheet.tsx +247 -0
  131. package/src/primitives/inputs/SelectSheet/SelectSheet.types.ts +196 -0
  132. package/src/primitives/inputs/SelectSheet/SelectSheetOption.tsx +177 -0
  133. package/src/primitives/inputs/SelectSheet/index.ts +48 -0
  134. package/src/primitives/inputs/SelectSheet/useSelectSheetLogic.ts +309 -0
  135. package/src/primitives/inputs/Switch/Switch.helpers.ts +109 -0
  136. package/src/primitives/inputs/Switch/Switch.tsx +191 -0
  137. package/src/primitives/inputs/Switch/Switch.types.ts +154 -0
  138. package/src/primitives/inputs/Switch/index.ts +40 -0
  139. package/src/primitives/inputs/Switch/useSwitchLogic.ts +192 -0
  140. package/src/primitives/inputs/TextInput/TextInput.helpers.ts +206 -0
  141. package/src/primitives/inputs/TextInput/TextInput.tsx +392 -0
  142. package/src/primitives/inputs/TextInput/TextInput.types.ts +216 -0
  143. package/src/primitives/inputs/TextInput/index.ts +37 -0
  144. package/src/primitives/inputs/TextInput/useTextInputLogic.ts +195 -0
  145. package/src/primitives/inputs/index.ts +52 -0
  146. package/src/primitives/layout/AnimatedBox.tsx +44 -0
  147. package/src/primitives/layout/Box.tsx +71 -0
  148. package/src/primitives/layout/Divider/Divider.helpers.ts +115 -0
  149. package/src/primitives/layout/Divider/Divider.tsx +139 -0
  150. package/src/primitives/layout/Divider/Divider.types.ts +178 -0
  151. package/src/primitives/layout/Divider/index.ts +24 -0
  152. package/src/primitives/layout/Divider/useDividerLogic.ts +109 -0
  153. package/src/primitives/layout/FlatList.tsx +66 -0
  154. package/src/primitives/layout/Pressable.tsx +74 -0
  155. package/src/primitives/layout/ScrollView.tsx +63 -0
  156. package/src/primitives/layout/Stack.tsx +69 -0
  157. package/src/primitives/layout/index.ts +40 -0
  158. package/src/primitives/navigation/index.ts +6 -0
  159. package/src/primitives/overlays/Modal/Modal.helpers.ts +31 -0
  160. package/src/primitives/overlays/Modal/Modal.tsx +264 -0
  161. package/src/primitives/overlays/Modal/Modal.types.ts +193 -0
  162. package/src/primitives/overlays/Modal/index.ts +43 -0
  163. package/src/primitives/overlays/Modal/useModalLogic.ts +103 -0
  164. package/src/primitives/overlays/index.ts +12 -0
  165. package/src/primitives/typography/Text.tsx +51 -0
  166. package/src/primitives/typography/index.ts +1 -0
  167. package/src/provider/DesignSystemContext.ts +22 -0
  168. package/src/provider/DesignSystemProvider.tsx +121 -0
  169. package/src/provider/index.ts +7 -0
  170. package/src/providers/ThemeProvider/createTheme.ts +304 -0
  171. package/src/providers/ThemeProvider/defaultTheme.ts +70 -0
  172. package/src/providers/ThemeProvider/index.ts +34 -0
  173. package/src/providers/ThemeProvider/types.ts +249 -0
  174. package/src/providers/index.ts +29 -0
  175. package/src/tokens/colors.ts +371 -0
  176. package/src/tokens/index.ts +145 -0
  177. package/src/tokens/motion.ts +176 -0
  178. package/src/tokens/radii.ts +82 -0
  179. package/src/tokens/scales.ts +588 -0
  180. package/src/tokens/shadows.ts +190 -0
  181. package/src/tokens/spacing.ts +140 -0
  182. package/src/tokens/tokens.json +207 -0
  183. package/src/tokens/typography.ts +251 -0
  184. package/src/types.ts +50 -0
  185. package/src/utils/accessibility.ts +169 -0
  186. package/src/utils/index.ts +25 -0
  187. package/src/utils/platform.ts +72 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Modal Component
3
+ *
4
+ * Barrel export for the Modal dialog overlay component and related types.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { Modal, type ModalProps, type ModalSize } from '@/design-system/primitives/overlays/Modal';
9
+ *
10
+ * <Modal
11
+ * visible={isVisible}
12
+ * onClose={handleClose}
13
+ * title="Confirm Action"
14
+ * position="center"
15
+ * size="md"
16
+ * primaryAction={{ label: "Confirm", onPress: handleConfirm }}
17
+ * >
18
+ * <Text>Are you sure?</Text>
19
+ * </Modal>
20
+ * ```
21
+ */
22
+
23
+ export { Modal } from "./Modal";
24
+ export { useModalLogic } from "./useModalLogic";
25
+
26
+ export type {
27
+ ModalProps,
28
+ ModalPosition,
29
+ ModalAction,
30
+ ModalA11yParams,
31
+ ModalA11yProps,
32
+ UseModalLogicParams,
33
+ UseModalLogicReturn,
34
+ } from "./Modal.types";
35
+
36
+ export {
37
+ getAnimationType,
38
+ } from "./Modal.helpers";
39
+
40
+ export {
41
+ getModalA11y,
42
+ getModalCloseButtonA11yProps,
43
+ } from "./Modal.a11y";
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Modal Logic Hook
3
+ *
4
+ * Extracts all computational logic from the Modal component.
5
+ * Returns memoized values for optimal render performance.
6
+ *
7
+ * Responsibilities:
8
+ * - Determine animation type
9
+ * - Handle backdrop press behavior
10
+ * - Generate accessibility props
11
+ *
12
+ * The component (Modal.tsx) is a "dumb view" that only renders
13
+ * the pre-computed values from this hook.
14
+ *
15
+ * @see Modal.tsx - Component consuming this hook
16
+ * @see Modal.helpers.ts - Pure calculation functions
17
+ */
18
+
19
+ import { useCallback, useMemo } from "react";
20
+
21
+ import { useDesignSystem } from "../../../provider";
22
+ import { getModalA11y, getModalCloseButtonA11yProps } from "./Modal.a11y";
23
+ import { getAnimationType } from "./Modal.helpers";
24
+ import type { UseModalLogicParams, UseModalLogicReturn } from "./Modal.types";
25
+
26
+ /**
27
+ * Orchestrates Modal rendering logic and state management.
28
+ *
29
+ * Handles:
30
+ * - Animation type determination based on position
31
+ * - Backdrop press handling with configurable behavior
32
+ * - Accessibility props generation with modal role
33
+ *
34
+ * @param params - Configuration object for modal behavior
35
+ * @param params.onClose - Callback when modal should close
36
+ * @param params.title - Modal title text
37
+ * @param params.position - Modal position ('center' | 'bottom')
38
+ * @param params.closeOnBackdropPress - Whether to close on backdrop press
39
+ * @param params.accessibilityLabel - Custom a11y label
40
+ *
41
+ * @returns Computed values for rendering Modal
42
+ * @returns {function} handleBackdropPress - Backdrop press handler
43
+ * @returns {string} animationType - RN Modal animation type
44
+ * @returns {object} a11yProps - Accessibility props with modal role
45
+ *
46
+ * @performance This hook uses useMemo() and useCallback() for optimization
47
+ *
48
+ * @example
49
+ * const { handleBackdropPress, animationType, a11yProps } = useModalLogic({
50
+ * onClose: () => setVisible(false),
51
+ * title: "Confirm Action",
52
+ * position: "center",
53
+ * closeOnBackdropPress: true,
54
+ * });
55
+ */
56
+ export function useModalLogic({
57
+ onClose,
58
+ title,
59
+ position,
60
+ closeOnBackdropPress,
61
+ accessibilityLabel,
62
+ }: UseModalLogicParams): UseModalLogicReturn {
63
+ const { labels: t } = useDesignSystem();
64
+ const animationType = getAnimationType(position);
65
+
66
+ const handleBackdropPress = useCallback(() => {
67
+ if (closeOnBackdropPress) {
68
+ onClose();
69
+ }
70
+ }, [closeOnBackdropPress, onClose]);
71
+
72
+ const positionLabels = useMemo(() => ({
73
+ bottom: t.designSystem.modal.positionBottom,
74
+ top: t.designSystem.modal.positionTop,
75
+ center: t.designSystem.modal.positionCenter,
76
+ }), [t]);
77
+
78
+ const a11yProps = useMemo(
79
+ () => getModalA11y({
80
+ title,
81
+ position,
82
+ accessibilityLabel,
83
+ fallbackTitle: t.designSystem.modal.fallbackTitle,
84
+ positionLabels,
85
+ }),
86
+ [title, position, accessibilityLabel, t, positionLabels]
87
+ );
88
+
89
+ const closeButtonA11yProps = useMemo(
90
+ () => getModalCloseButtonA11yProps({
91
+ closeLabel: t.designSystem.modal.closeLabel,
92
+ closeHint: t.designSystem.modal.closeHint,
93
+ }),
94
+ [t]
95
+ );
96
+
97
+ return {
98
+ handleBackdropPress,
99
+ animationType,
100
+ a11yProps,
101
+ closeButtonA11yProps,
102
+ };
103
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Overlays Module
3
+ *
4
+ * Barrel export for overlay components (Modal, Sheet, etc.).
5
+ */
6
+
7
+ export {
8
+ Modal,
9
+ type ModalProps,
10
+ type ModalPosition,
11
+ type ModalAction,
12
+ } from "./Modal";
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Text Component
3
+ *
4
+ * @description Text primitive with Restyle typography integration - Atom
5
+ *
6
+ * Text is the base text component with access to all theme
7
+ * typography variants and color tokens.
8
+ *
9
+ * ## Typography Variants
10
+ * | Variant | Size | Weight | Use Case |
11
+ * |---------------|------|----------|----------|
12
+ * | displayLarge | 48px | Bold | Hero sections |
13
+ * | displayMedium | 36px | Bold | Page titles |
14
+ * | displaySmall | 30px | Bold | Section headers |
15
+ * | headingLarge | 24px | Semibold | Card titles |
16
+ * | headingMedium | 20px | Semibold | Subsections |
17
+ * | headingSmall | 17px | Semibold | List headers |
18
+ * | bodyLarge | 17px | Regular | Primary content |
19
+ * | bodyMedium | 15px | Regular | Default body |
20
+ * | bodySmall | 13px | Regular | Secondary content |
21
+ * | labelLarge | 17px | Medium | Large buttons |
22
+ * | labelMedium | 15px | Medium | Buttons, inputs |
23
+ * | labelSmall | 13px | Medium | Small buttons |
24
+ * | caption | 13px | Regular | Help text |
25
+ * | captionSmall | 11px | Regular | Timestamps |
26
+ * | overline | 11px | Semibold | Section labels |
27
+ *
28
+ * @see tokens/typography.ts - Typography token definitions
29
+ * @see core/restyle/restyleTheme.ts - Text variant configurations
30
+ *
31
+ * @example
32
+ * // Basic usage
33
+ * <Text variant="bodyMedium">Hello World</Text>
34
+ *
35
+ * @example
36
+ * // With color
37
+ * <Text variant="headingLarge" color="accentPrimary">
38
+ * Welcome
39
+ * </Text>
40
+ *
41
+ * @example
42
+ * // Responsive variant
43
+ * <Text variant={{ phone: "bodyMedium", tablet: "bodyLarge" }}>
44
+ * Responsive text
45
+ * </Text>
46
+ */
47
+
48
+ import { createText } from "@shopify/restyle";
49
+ import { RestyleTheme } from "../../core/restyle";
50
+
51
+ export const Text = createText<RestyleTheme>();
@@ -0,0 +1 @@
1
+ export * from "./Text";
@@ -0,0 +1,22 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { DesignSystemLabels } from "../i18n";
3
+ import { en } from "../i18n";
4
+
5
+ export interface DesignSystemContextValue {
6
+ labels: DesignSystemLabels;
7
+ }
8
+
9
+ export const DesignSystemContext = createContext<DesignSystemContextValue>({
10
+ labels: en,
11
+ });
12
+
13
+ /**
14
+ * Access the design system context.
15
+ *
16
+ * @example
17
+ * const { labels } = useDesignSystem();
18
+ * const loadingText = labels.designSystem.button.loadingLabel;
19
+ */
20
+ export function useDesignSystem(): DesignSystemContextValue {
21
+ return useContext(DesignSystemContext);
22
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * DesignSystemProvider
3
+ *
4
+ * Single provider for the design system. Combines:
5
+ * - Restyle theme (light/dark based on system color scheme)
6
+ * - i18n labels with built-in defaults and optional overrides
7
+ *
8
+ * @example
9
+ * // Basic — English defaults, auto light/dark
10
+ * <DesignSystemProvider>
11
+ * <App />
12
+ * </DesignSystemProvider>
13
+ *
14
+ * @example
15
+ * // Reactive to app language
16
+ * const { language } = useAppI18n();
17
+ * <DesignSystemProvider language={language}>
18
+ * <App />
19
+ * </DesignSystemProvider>
20
+ *
21
+ * @example
22
+ * // With partial overrides on top of the active language
23
+ * <DesignSystemProvider language="es" overrides={{ designSystem: { button: { loadingLabel: "Guardando..." } } }}>
24
+ * <App />
25
+ * </DesignSystemProvider>
26
+ *
27
+ * @example
28
+ * // With custom themes from outside the package
29
+ * <DesignSystemProvider lightTheme={myBrandLightTheme} darkTheme={myBrandDarkTheme}>
30
+ * <App />
31
+ * </DesignSystemProvider>
32
+ */
33
+
34
+ import { ThemeProvider } from "@shopify/restyle";
35
+ import React, { ReactNode, useMemo } from "react";
36
+ import { useColorScheme } from "react-native";
37
+ import type { RestyleTheme } from "../core/restyle";
38
+ import { restyleDarkTheme, restyleLightTheme } from "../core/restyle/restyleTheme";
39
+ import { en, es } from "../i18n";
40
+ import type { DesignSystemLabels, DesignSystemLabelsOverride } from "../i18n";
41
+ import { DesignSystemContext } from "./DesignSystemContext";
42
+
43
+ /** Languages with built-in label sets. */
44
+ export type DesignSystemLanguage = "en" | "es";
45
+
46
+ const builtInLabels: Record<DesignSystemLanguage, DesignSystemLabels> = {
47
+ en,
48
+ es,
49
+ };
50
+
51
+ export interface DesignSystemProviderProps {
52
+ children: ReactNode;
53
+ /** Active language — switches the built-in label set. Defaults to "en". */
54
+ language?: DesignSystemLanguage;
55
+ /** Partial overrides applied on top of the active language's labels. */
56
+ overrides?: DesignSystemLabelsOverride;
57
+ /**
58
+ * Custom light theme. When provided, replaces the built-in light theme.
59
+ * Must be a valid Restyle theme created with `createTheme`.
60
+ */
61
+ lightTheme?: RestyleTheme;
62
+ /**
63
+ * Custom dark theme. When provided, replaces the built-in dark theme.
64
+ * Must be a valid Restyle theme created with `createTheme`.
65
+ */
66
+ darkTheme?: RestyleTheme;
67
+ }
68
+
69
+ function deepMerge<T extends object>(
70
+ base: T,
71
+ overrides: Record<string, unknown> | undefined,
72
+ ): T {
73
+ if (!overrides) return base;
74
+ const result = { ...base };
75
+ const baseRecord = base as Record<string, unknown>;
76
+ for (const key of Object.keys(overrides)) {
77
+ const overrideVal = overrides[key];
78
+ if (
79
+ overrideVal &&
80
+ typeof overrideVal === "object" &&
81
+ !Array.isArray(overrideVal)
82
+ ) {
83
+ result[key as keyof T] = deepMerge(
84
+ (baseRecord[key] ?? {}) as Record<string, unknown>,
85
+ overrideVal as Record<string, unknown>,
86
+ ) as T[keyof T];
87
+ } else if (overrideVal !== undefined) {
88
+ result[key as keyof T] = overrideVal as T[keyof T];
89
+ }
90
+ }
91
+ return result;
92
+ }
93
+
94
+ export function DesignSystemProvider({
95
+ children,
96
+ language = "en",
97
+ overrides,
98
+ lightTheme,
99
+ darkTheme,
100
+ }: DesignSystemProviderProps) {
101
+ const isDark = useColorScheme() === "dark";
102
+
103
+ const activeTheme = isDark
104
+ ? (darkTheme ?? restyleDarkTheme)
105
+ : (lightTheme ?? restyleLightTheme);
106
+
107
+ const labels = useMemo<DesignSystemLabels>(() => {
108
+ const base = builtInLabels[language] ?? en;
109
+ return overrides ? deepMerge(base, overrides) : base;
110
+ }, [language, overrides]);
111
+
112
+ const contextValue = useMemo(() => ({ labels }), [labels]);
113
+
114
+ return (
115
+ <DesignSystemContext.Provider value={contextValue}>
116
+ <ThemeProvider theme={activeTheme}>
117
+ {children}
118
+ </ThemeProvider>
119
+ </DesignSystemContext.Provider>
120
+ );
121
+ }
@@ -0,0 +1,7 @@
1
+ export { DesignSystemProvider } from "./DesignSystemProvider";
2
+ export type {
3
+ DesignSystemLanguage,
4
+ DesignSystemProviderProps,
5
+ } from "./DesignSystemProvider";
6
+ export { useDesignSystem } from "./DesignSystemContext";
7
+ export type { DesignSystemContextValue } from "./DesignSystemContext";
@@ -0,0 +1,304 @@
1
+ /**
2
+ * El Sendero Design System - Theme Factory
3
+ *
4
+ * Create custom themes by extending the base theme with overrides.
5
+ *
6
+ * @example
7
+ * const myTheme = createTheme(defaultTheme, {
8
+ * colors: {
9
+ * accent: {
10
+ * primary: '#8B5CF6', // Purple accent
11
+ * }
12
+ * }
13
+ * });
14
+ */
15
+
16
+ import { Theme, ThemeOverrides, DeepPartial } from './types';
17
+ import { lightTheme, darkTheme } from './defaultTheme';
18
+
19
+ // =============================================================================
20
+ // DEEP MERGE UTILITY
21
+ // =============================================================================
22
+
23
+ /**
24
+ * Deep merge two objects, with source values overriding target
25
+ */
26
+ function deepMerge<T extends object>(
27
+ target: T,
28
+ source: DeepPartial<T>
29
+ ): T {
30
+ const result = { ...target };
31
+
32
+ for (const key in source) {
33
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
34
+ const sourceValue = source[key as keyof typeof source];
35
+ const targetValue = (target as Record<string, unknown>)[key];
36
+
37
+ if (
38
+ sourceValue !== null &&
39
+ typeof sourceValue === 'object' &&
40
+ !Array.isArray(sourceValue) &&
41
+ targetValue !== null &&
42
+ typeof targetValue === 'object' &&
43
+ !Array.isArray(targetValue)
44
+ ) {
45
+ result[key as keyof T] = deepMerge(
46
+ targetValue as Record<string, unknown>,
47
+ sourceValue as DeepPartial<Record<string, unknown>>
48
+ ) as T[keyof T];
49
+ } else if (sourceValue !== undefined) {
50
+ result[key as keyof T] = sourceValue as T[keyof T];
51
+ }
52
+ }
53
+ }
54
+
55
+ return result;
56
+ }
57
+
58
+ // =============================================================================
59
+ // CREATE THEME
60
+ // =============================================================================
61
+
62
+ /**
63
+ * Create a custom theme by extending a base theme with overrides.
64
+ *
65
+ * @param baseTheme - The theme to extend (usually defaultTheme)
66
+ * @param overrides - Partial theme object with your customizations
67
+ * @returns A complete Theme object with your overrides applied
68
+ *
69
+ * @example
70
+ * // Create a sage-accented theme
71
+ * const sageTheme = createTheme(defaultTheme, {
72
+ * name: 'Sage',
73
+ * colors: {
74
+ * accent: {
75
+ * primary: '#6B8F6B',
76
+ * primaryHover: '#557255',
77
+ * primaryPressed: '#445944',
78
+ * }
79
+ * }
80
+ * });
81
+ *
82
+ * @example
83
+ * // Create a more rounded theme
84
+ * const softTheme = createTheme(defaultTheme, {
85
+ * radii: {
86
+ * md: 16,
87
+ * lg: 24,
88
+ * xl: 32,
89
+ * }
90
+ * });
91
+ */
92
+ export function createTheme(
93
+ baseTheme: Theme,
94
+ overrides: ThemeOverrides
95
+ ): Theme {
96
+ return deepMerge(baseTheme, overrides);
97
+ }
98
+
99
+ // =============================================================================
100
+ // CREATE THEME PAIR
101
+ // =============================================================================
102
+
103
+ /**
104
+ * Create both light and dark versions of a custom theme.
105
+ *
106
+ * @param overrides - Overrides to apply to both light and dark themes
107
+ * @param lightOverrides - Additional overrides for light theme only
108
+ * @param darkOverrides - Additional overrides for dark theme only
109
+ * @returns Object with light and dark theme variants
110
+ *
111
+ * @example
112
+ * const { light, dark } = createThemePair(
113
+ * { name: 'MyBrand' },
114
+ * { colors: { accent: { primary: '#A68B6A' } } }, // Light-specific
115
+ * { colors: { accent: { primary: '#C4A285' } } } // Dark-specific
116
+ * );
117
+ */
118
+ export function createThemePair(
119
+ overrides: ThemeOverrides = {},
120
+ lightOverrides: ThemeOverrides = {},
121
+ darkOverrides: ThemeOverrides = {}
122
+ ): { light: Theme; dark: Theme } {
123
+ return {
124
+ light: createTheme(
125
+ createTheme(lightTheme, overrides),
126
+ lightOverrides
127
+ ),
128
+ dark: createTheme(
129
+ createTheme(darkTheme, overrides),
130
+ darkOverrides
131
+ ),
132
+ };
133
+ }
134
+
135
+ // =============================================================================
136
+ // PRESET THEMES
137
+ // =============================================================================
138
+
139
+ /**
140
+ * Horizon Default - Blue with a calming feel
141
+ */
142
+ export const horizonTheme = createThemePair({
143
+ name: 'Horizon',
144
+ });
145
+
146
+ /**
147
+ * Sage Theme - Green-focused for hope and growth
148
+ */
149
+ export const sageTheme = createThemePair(
150
+ { name: 'Sage' },
151
+ {
152
+ colors: {
153
+ accent: {
154
+ primary: '#6B8F6B',
155
+ primaryHover: '#557255',
156
+ primaryPressed: '#445944',
157
+ },
158
+ text: {
159
+ link: '#6B8F6B',
160
+ },
161
+ border: {
162
+ focus: '#6B8F6B',
163
+ },
164
+ },
165
+ },
166
+ {
167
+ colors: {
168
+ accent: {
169
+ primary: '#8FAF8F',
170
+ primaryHover: '#B3C7B3',
171
+ primaryPressed: '#6B8F6B',
172
+ },
173
+ text: {
174
+ link: '#8FAF8F',
175
+ },
176
+ border: {
177
+ focus: '#8FAF8F',
178
+ },
179
+ },
180
+ }
181
+ );
182
+
183
+ /**
184
+ * Sunset Theme - Warm coral/peach for energy and comfort
185
+ */
186
+ export const sunsetTheme = createThemePair(
187
+ { name: 'Sunset' },
188
+ {
189
+ colors: {
190
+ accent: {
191
+ primary: '#E8836B',
192
+ primaryHover: '#CC6B55',
193
+ primaryPressed: '#A85545',
194
+ },
195
+ text: {
196
+ link: '#E8836B',
197
+ },
198
+ border: {
199
+ focus: '#E8836B',
200
+ },
201
+ },
202
+ },
203
+ {
204
+ colors: {
205
+ accent: {
206
+ primary: '#FF9B85',
207
+ primaryHover: '#FFBAA8',
208
+ primaryPressed: '#E8836B',
209
+ },
210
+ text: {
211
+ link: '#FF9B85',
212
+ },
213
+ border: {
214
+ focus: '#FF9B85',
215
+ },
216
+ },
217
+ }
218
+ );
219
+
220
+ /**
221
+ * Ocean Theme - Teal/cyan accents, calming water vibes
222
+ */
223
+ export const oceanTheme = createThemePair(
224
+ {
225
+ colors: {
226
+ accent: {
227
+ primary: '#0E9AA5',
228
+ primaryHover: '#0B7E87',
229
+ primaryPressed: '#086269',
230
+ },
231
+ text: { link: '#0E9AA5' },
232
+ border: { focus: '#0E9AA5' },
233
+ },
234
+ },
235
+ {
236
+ colors: {
237
+ accent: {
238
+ primary: '#2DD4BF',
239
+ primaryHover: '#5EEAD4',
240
+ primaryPressed: '#0E9AA5',
241
+ },
242
+ text: { link: '#2DD4BF' },
243
+ border: { focus: '#2DD4BF' },
244
+ },
245
+ }
246
+ );
247
+
248
+ /**
249
+ * Lavender Theme - Soft purple accents, relaxing and spiritual
250
+ */
251
+ export const lavenderTheme = createThemePair(
252
+ { name: 'Lavender' },
253
+ {
254
+ colors: {
255
+ accent: {
256
+ primary: '#8B5CF6',
257
+ primaryHover: '#7C3AED',
258
+ primaryPressed: '#6D28D9',
259
+ },
260
+ text: { link: '#8B5CF6' },
261
+ border: { focus: '#8B5CF6' },
262
+ },
263
+ },
264
+ {
265
+ colors: {
266
+ accent: {
267
+ primary: '#A78BFA',
268
+ primaryHover: '#C4B5FD',
269
+ primaryPressed: '#8B5CF6',
270
+ },
271
+ text: { link: '#A78BFA' },
272
+ border: { focus: '#A78BFA' },
273
+ },
274
+ }
275
+ );
276
+
277
+ /**
278
+ * Rose Theme - Warm pink/rose accents, gentle and nurturing
279
+ */
280
+ export const roseTheme = createThemePair(
281
+ { name: 'Rose' },
282
+ {
283
+ colors: {
284
+ accent: {
285
+ primary: '#E11D6C',
286
+ primaryHover: '#BE185D',
287
+ primaryPressed: '#9D174D',
288
+ },
289
+ text: { link: '#E11D6C' },
290
+ border: { focus: '#E11D6C' },
291
+ },
292
+ },
293
+ {
294
+ colors: {
295
+ accent: {
296
+ primary: '#F472B6',
297
+ primaryHover: '#F9A8D4',
298
+ primaryPressed: '#E11D6C',
299
+ },
300
+ text: { link: '#F472B6' },
301
+ border: { focus: '#F472B6' },
302
+ },
303
+ }
304
+ );