@sincpro/mobile-ui 1.0.1 → 2.0.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.
Files changed (168) hide show
  1. package/README.md +701 -231
  2. package/dist/Actions/Actions.Button.d.ts +15 -12
  3. package/dist/Actions/Actions.Button.js +25 -14
  4. package/dist/Actions/Actions.Dock.d.ts +20 -0
  5. package/dist/Actions/Actions.Dock.js +89 -0
  6. package/dist/Actions/Actions.FAB.d.ts +2 -1
  7. package/dist/Actions/Actions.FAB.js +5 -3
  8. package/dist/Actions/Actions.FilterButton.d.ts +10 -2
  9. package/dist/Actions/Actions.FilterButton.js +21 -6
  10. package/dist/Actions/Actions.IconButton.d.ts +4 -2
  11. package/dist/Actions/Actions.IconButton.js +7 -3
  12. package/dist/Actions/index.d.ts +6 -2
  13. package/dist/Actions/index.js +4 -1
  14. package/dist/Dialog/BottomSheet/BottomSheet.Root.js +22 -34
  15. package/dist/Dialog/Dialog.ActionSheet.d.ts +2 -1
  16. package/dist/Dialog/Dialog.ActionSheet.js +17 -21
  17. package/dist/Dialog/Dialog.Confirmation.js +8 -6
  18. package/dist/Dialog/Dialog.EditValue.js +3 -1
  19. package/dist/Dialog/Dialog.NativeSheet.d.ts +10 -0
  20. package/dist/Dialog/Dialog.NativeSheet.js +21 -0
  21. package/dist/Dialog/Dialog.Sheet.d.ts +5 -1
  22. package/dist/Dialog/Dialog.Sheet.js +30 -31
  23. package/dist/Display/Display.Accordion.js +7 -3
  24. package/dist/Display/Display.AmountSummary.d.ts +28 -0
  25. package/dist/Display/Display.AmountSummary.js +25 -0
  26. package/dist/Display/Display.Carousel.d.ts +27 -4
  27. package/dist/Display/Display.Carousel.js +112 -12
  28. package/dist/Display/Display.CopyableText.d.ts +9 -2
  29. package/dist/Display/Display.CopyableText.js +75 -7
  30. package/dist/Display/Display.ExpoImage.d.ts +8 -0
  31. package/dist/Display/Display.ExpoImage.js +15 -0
  32. package/dist/Display/Display.Icon.js +2 -1
  33. package/dist/Display/Display.Image.d.ts +10 -6
  34. package/dist/Display/Display.Image.js +39 -6
  35. package/dist/Display/Display.KeyValue.d.ts +11 -4
  36. package/dist/Display/Display.KeyValue.js +6 -4
  37. package/dist/Display/Display.ListGroup.d.ts +23 -0
  38. package/dist/Display/Display.ListGroup.js +17 -0
  39. package/dist/Display/Display.ListItem.d.ts +15 -2
  40. package/dist/Display/Display.ListItem.js +25 -8
  41. package/dist/Display/Display.Logo.d.ts +2 -2
  42. package/dist/Display/Display.Logo.js +3 -2
  43. package/dist/Display/Display.MediaCard.d.ts +2 -2
  44. package/dist/Display/Display.MediaEmbed.d.ts +2 -2
  45. package/dist/Display/Display.ProductCard.d.ts +2 -2
  46. package/dist/Display/Display.SectionHeader.d.ts +11 -1
  47. package/dist/Display/Display.SectionHeader.js +11 -2
  48. package/dist/Display/Display.Story.d.ts +3 -3
  49. package/dist/Display/Display.Story.js +5 -4
  50. package/dist/Display/Display.StoryTray.d.ts +4 -4
  51. package/dist/Display/Display.StoryTray.js +3 -2
  52. package/dist/Display/Display.StoryViewer.d.ts +3 -3
  53. package/dist/Display/Display.StoryViewer.js +6 -3
  54. package/dist/Display/Display.SwipeableRow.d.ts +3 -1
  55. package/dist/Display/Display.SwipeableRow.js +3 -3
  56. package/dist/Display/index.d.ts +13 -0
  57. package/dist/Display/index.js +6 -0
  58. package/dist/Feedback/Feedback.Banner.js +1 -1
  59. package/dist/Feedback/Feedback.DomainSplashScreen.d.ts +13 -4
  60. package/dist/Feedback/Feedback.DomainSplashScreen.js +68 -53
  61. package/dist/Feedback/Feedback.EmptyState.d.ts +2 -2
  62. package/dist/Feedback/Feedback.EmptyState.js +3 -2
  63. package/dist/Feedback/Feedback.Error.d.ts +2 -1
  64. package/dist/Feedback/Feedback.Error.js +4 -3
  65. package/dist/Feedback/Feedback.Loading.d.ts +6 -2
  66. package/dist/Feedback/Feedback.Loading.js +2 -2
  67. package/dist/Feedback/Feedback.Progress.d.ts +6 -3
  68. package/dist/Feedback/Feedback.Progress.js +29 -19
  69. package/dist/Feedback/Feedback.ProgressCircle.d.ts +3 -2
  70. package/dist/Feedback/Feedback.ProgressCircle.js +10 -7
  71. package/dist/Feedback/Feedback.Skeleton.d.ts +4 -4
  72. package/dist/Feedback/Feedback.Skeleton.js +15 -3
  73. package/dist/Feedback/Feedback.Spinner.d.ts +3 -3
  74. package/dist/Feedback/Feedback.Spinner.js +40 -9
  75. package/dist/Feedback/Feedback.Splash.d.ts +2 -2
  76. package/dist/Feedback/Feedback.Splash.js +5 -3
  77. package/dist/Feedback/Toast.context.d.ts +64 -3
  78. package/dist/Feedback/Toast.context.js +112 -31
  79. package/dist/Feedback/index.d.ts +15 -1
  80. package/dist/Feedback/index.js +4 -1
  81. package/dist/Form/Form.Checkbox.d.ts +3 -1
  82. package/dist/Form/Form.Checkbox.js +2 -2
  83. package/dist/Form/Form.DatePicker.js +3 -2
  84. package/dist/Form/Form.Dropdown.d.ts +15 -3
  85. package/dist/Form/Form.Dropdown.js +21 -6
  86. package/dist/Form/Form.ErrorSummary.d.ts +23 -0
  87. package/dist/Form/Form.ErrorSummary.js +53 -0
  88. package/dist/Form/Form.FilterButton.d.ts +1 -1
  89. package/dist/Form/Form.Input.d.ts +4 -3
  90. package/dist/Form/Form.Input.js +2 -2
  91. package/dist/Form/Form.KeyboardForm.d.ts +11 -0
  92. package/dist/Form/Form.KeyboardForm.js +9 -0
  93. package/dist/Form/Form.KeyboardToolbar.d.ts +21 -0
  94. package/dist/Form/Form.KeyboardToolbar.js +70 -0
  95. package/dist/Form/Form.Select.d.ts +2 -1
  96. package/dist/Form/Form.Select.js +26 -26
  97. package/dist/Form/index.d.ts +9 -0
  98. package/dist/Form/index.js +6 -0
  99. package/dist/Navigation/Navigation.AppBar.js +8 -6
  100. package/dist/Navigation/Navigation.BottomNav.js +10 -5
  101. package/dist/Navigation/Navigation.Header.d.ts +2 -2
  102. package/dist/Navigation/Navigation.Menu.js +10 -3
  103. package/dist/Navigation/Navigation.RowItem.d.ts +1 -1
  104. package/dist/Navigation/Navigation.RowItem.js +2 -2
  105. package/dist/Navigation/Navigation.Search.d.ts +41 -0
  106. package/dist/Navigation/Navigation.Search.js +47 -0
  107. package/dist/Navigation/Navigation.SearchBar.d.ts +5 -1
  108. package/dist/Navigation/Navigation.SearchBar.js +25 -7
  109. package/dist/Navigation/Navigation.SearchSuggestions.d.ts +14 -0
  110. package/dist/Navigation/Navigation.SearchSuggestions.js +7 -0
  111. package/dist/Navigation/Navigation.SegmentedControl.d.ts +11 -1
  112. package/dist/Navigation/Navigation.SegmentedControl.js +29 -15
  113. package/dist/Navigation/index.d.ts +13 -0
  114. package/dist/Navigation/index.js +4 -0
  115. package/dist/Typography/Typography.Heading.js +1 -1
  116. package/dist/Typography/Typography.SelectableText.d.ts +10 -0
  117. package/dist/Typography/Typography.SelectableText.js +16 -0
  118. package/dist/Typography/Typography.Text.js +4 -20
  119. package/dist/Typography/index.d.ts +3 -0
  120. package/dist/Typography/index.js +3 -0
  121. package/dist/branding.d.ts +3 -2
  122. package/dist/layouts/AuthContainer.d.ts +4 -1
  123. package/dist/layouts/AuthContainer.js +3 -3
  124. package/dist/primitives/BaseButton.d.ts +15 -12
  125. package/dist/primitives/BaseButton.js +21 -9
  126. package/dist/theme/typography.d.ts +16 -1
  127. package/dist/theme/typography.js +19 -0
  128. package/dist/utils/criteria.d.ts +56 -0
  129. package/dist/utils/criteria.js +99 -0
  130. package/dist/utils/image.d.ts +3 -0
  131. package/dist/utils/image.js +13 -0
  132. package/dist/utils/keys.d.ts +1 -0
  133. package/dist/utils/keys.js +7 -0
  134. package/dist/utils/progress.d.ts +1 -0
  135. package/dist/utils/progress.js +5 -0
  136. package/dist/utils/reorder.d.ts +4 -0
  137. package/dist/utils/reorder.js +29 -0
  138. package/dist/utils/useReducedMotion.d.ts +1 -0
  139. package/dist/utils/useReducedMotion.js +43 -0
  140. package/dist/utils/useSheetTranslation.d.ts +5 -0
  141. package/dist/utils/useSheetTranslation.js +23 -0
  142. package/dist/views/AppSplashView.d.ts +3 -2
  143. package/dist/views/AppSplashView.js +3 -2
  144. package/dist/views/AuthFormView.d.ts +6 -3
  145. package/dist/views/AuthFormView.js +2 -2
  146. package/dist/views/FormViewV2.d.ts +9 -4
  147. package/dist/views/FormViewV2.js +35 -16
  148. package/dist/views/ListViewV2.context.d.ts +3 -1
  149. package/dist/views/ListViewV2.context.js +41 -21
  150. package/dist/views/ListViewV2.d.ts +18 -7
  151. package/dist/views/ListViewV2.js +69 -35
  152. package/dist/views/types/IListView.js +6 -5
  153. package/dist/widgets/CollectionOptions.d.ts +15 -0
  154. package/dist/widgets/CollectionOptions.js +129 -0
  155. package/dist/widgets/FilterBar.d.ts +21 -0
  156. package/dist/widgets/FilterBar.js +14 -0
  157. package/dist/widgets/FilterBuilder.d.ts +10 -0
  158. package/dist/widgets/FilterBuilder.js +115 -0
  159. package/dist/widgets/HomeHeader.d.ts +2 -2
  160. package/dist/widgets/PromoModal.js +3 -1
  161. package/dist/widgets/QueryChoice.d.ts +10 -0
  162. package/dist/widgets/QueryChoice.js +13 -0
  163. package/dist/widgets/ReorderableList.d.ts +51 -0
  164. package/dist/widgets/ReorderableList.js +138 -0
  165. package/dist/widgets/ScreenHeader.d.ts +2 -2
  166. package/dist/widgets/index.d.ts +4 -0
  167. package/dist/widgets/index.js +3 -0
  168. package/package.json +18 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @sincpro/mobile-ui
2
2
 
3
- **Sincpro Mobile Design System** — the UI contract between design and engineering for React Native / Expo SDK 57 applications.
3
+ **Sincpro Mobile Design System** — the UI contract between design and engineering for React Native / Expo SDK 58 applications.
4
4
 
5
5
  This package is maintained by Sincpro as part of the mobile framework ecosystem. Anyone can read the source and use the components. What Sincpro provides is the commitment behind it: stable APIs, predictable behavior, long-term support, and a team that ensures this keeps working as platforms evolve. Build on it knowing it will still be there.
6
6
 
@@ -11,7 +11,7 @@ This package is maintained by Sincpro as part of the mobile framework ecosystem.
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- npx expo install @sincpro/mobile-ui
14
+ npx expo install @sincpro/mobile-ui expo-image
15
15
  ```
16
16
 
17
17
  Wire the Tailwind preset, then import by category:
@@ -20,18 +20,14 @@ Wire the Tailwind preset, then import by category:
20
20
  // tailwind.config.js
21
21
  module.exports = {
22
22
  presets: [require("@sincpro/mobile-ui/tailwind.preset")],
23
- content: [
24
- "./App.tsx",
25
- "./src/**/*.{ts,tsx}",
26
- "./node_modules/@sincpro/mobile-ui/**/*.js",
27
- ],
23
+ content: ["./App.tsx", "./src/**/*.{ts,tsx}", "./node_modules/@sincpro/mobile-ui/**/*.js"],
28
24
  };
29
25
  ```
30
26
 
31
27
  ```tsx
32
28
  import { Actions } from "@sincpro/mobile-ui/Actions";
33
29
  import { Display } from "@sincpro/mobile-ui/Display";
34
- import { Form } from "@sincpro/mobile-ui/Form";
30
+ import { Form } from "@sincpro/mobile-ui/Form";
35
31
  import { useToast, ToastProvider } from "@sincpro/mobile-ui/Feedback";
36
32
 
37
33
  export default function InvoiceScreen() {
@@ -39,10 +35,8 @@ export default function InvoiceScreen() {
39
35
  return (
40
36
  <Display.Card>
41
37
  <Display.KeyValue label="Invoice #" value="INV-0042" copyable />
42
- <Display.Monetary amount={1500.00} variant="dataLarge" />
43
- <Actions.Button onPress={() => toast.success("Invoice sent")}>
44
- Send Invoice
45
- </Actions.Button>
38
+ <Display.Monetary value={1500.0} variant="dataLarge" />
39
+ <Actions.Button onPress={() => toast.success("Invoice sent")} title="Send Invoice" />
46
40
  </Display.Card>
47
41
  );
48
42
  }
@@ -81,6 +75,385 @@ Without a design system, every developer makes local decisions: which shade of g
81
75
 
82
76
  ## Philosophy
83
77
 
78
+ **Composable behavior, customizable presentation.** Sincpro defaults are a starting
79
+ point, not a mandatory visual style. Preserve existing appearances while exposing public
80
+ APIs for fonts, sizes, shapes, content and composition. See the
81
+ [permanent design-system manifesto](docs/MANIFESTO.md) for the goal, acceptance criteria
82
+ and current limitations; full customizability is an objective, not a completed audit.
83
+
84
+ The [UX/UI evolution plan](docs/UX-UI-PLAN.md) tracks the rollout. The first native pilot
85
+ is in Storybook under `Patterns/Records/Detail`: list, detail, copy, edit, cancel and
86
+ recoverable error. It uses fictional data and local state, not persistence or a backend.
87
+
88
+ ### Accessible interaction
89
+
90
+ - Loading feedback respects the live system Reduce Motion setting; `animated={false}`
91
+ keeps `Feedback.Skeleton`, `Progress`, `Spinner` and `Loading` static explicitly.
92
+ Spinners expose a named busy state; progress exposes a percentage only when known.
93
+ Track/indicator styling and native accessibility props remain customizable.
94
+ See the [feedback contract](docs/CATALOG.md#feedback-feedback) and the
95
+ [comparison and priorities](docs/DESIGN-SYSTEM-GAPS.md).
96
+ - v2 requires `expo-image`: `Display.Image`, logos, story media and illustrations share
97
+ the Expo renderer, disk caching, automatic source recycling and Reduce Motion policy.
98
+ `Display.ExpoImage` exposes the direct renderer; `Display.Image` adds error/retry composition.
99
+ See the [breaking migration guide](docs/MIGRATION-V2.md).
100
+ - `ListViewV2.Content` exposes `listProps` for virtualization, `getItemLayout`, viewability
101
+ and `extraData`, plus `listRef` and content styling. `Root hasMore={false}` stops
102
+ pagination; `loadingMore` and refresh pause it. See the
103
+ [mobile performance and usability plan](docs/MOBILE-QUALITY.md).
104
+ - Buttons, icon actions, inputs, selection controls and navigation use their 48-point
105
+ accessible sizing by default. `accessibleSizing` was removed: there is no legacy
106
+ compact-target mode. Labels wrap and follow system scaling; presentation stays configurable.
107
+ - Supply `accessibilityLabel` for icon-only actions. Loading buttons expose busy/disabled
108
+ state and retain their accessible name.
109
+ - `Display.CopyableText` uses a familiar copy icon: tap a short value to copy it.
110
+ A transient checkmark confirms success without adding a “Copiado” block or shifting
111
+ layout; screen readers receive an announcement. Use `selectable` for longer text:
112
+ native text selection stays independent of the 48-point icon action that copies the
113
+ full value. Failures stay explicit and retryable. It accepts `disabled`, `testID`,
114
+ `accessibilityLabel`, `onCopied` and `onCopyError`. Copy targets intentionally reserve 48 points.
115
+ - `Display.KeyValue copyable` enables copying string/number values (enforced by its type).
116
+ Use `align="stacked"` for long content or `align="auto"` to stack below 360 points or
117
+ above a 1.3 font scale. Existing `align="row"` remains unchanged; React nodes are
118
+ display-only.
119
+ - `KeyValue selectable` also supports native text selection, with or without copying.
120
+ Choose the mode by the purpose of the field, not an automatic character-count threshold.
121
+ - `Typography.SelectableText` provides read-only native range selection without opening
122
+ the keyboard. It preserves typography/tokens and backs the longer-text modes above.
123
+ On iOS, `Text selectable` alone only exposes full-paragraph copying, not range handles.
124
+ - Headings and section titles expose native header semantics.
125
+ - Checkbox/Select announce control state and use accessible targets without an opt-in.
126
+ - Sheet supports accessible escape and `showCloseButton`; `SwipeableRow visibleActions`
127
+ supplies a non-gesture alternative. Keep explicit labels for essential actions.
128
+ - `Dialog/NativeSheet` is an optional Expo 58 native presentation (`@expo/ui` peer);
129
+ import it by subpath, not the root barrel. Use short action sheets for choices and a
130
+ sheet for a scoped task; do not stack sheets. Mobile-task stories demonstrate native
131
+ summaries, cancelable selection, filters applied only on confirmation and archive/undo.
132
+ - Form/List view refresh always clears the busy state on failure and accepts
133
+ `onRefreshError` for application recovery without discarding content.
134
+ - AppBar, BottomNav and SegmentedControl use accessible sizing; banners expose status
135
+ semantics, and confirmation defaults use explicit verbs.
136
+
137
+ The [audit and validation report](docs/UX-AUDIT.md) records scope, evidence and device limits.
138
+
139
+ ### Clean Sincpro grouping and customization
140
+
141
+ `Display.ListGroup` groups related settings or a short list on one surface, with a title,
142
+ optional description/footer/action and hairline separators only between rows. Choose
143
+ `surface="plain"` for grouping by proximity instead of a rounded container.
144
+
145
+ ```tsx
146
+ <Display.ListGroup
147
+ title="Cuenta"
148
+ footer="Puedes cambiar estas preferencias en cualquier momento."
149
+ contentClassName="rounded-lg bg-bg-muted"
150
+ >
151
+ <Display.ListItem
152
+ variant="mobile"
153
+ title="Idioma"
154
+ value="Español"
155
+ chevron
156
+ onPress={openLanguageChoices}
157
+ />
158
+ <Display.ListItem
159
+ variant="mobile"
160
+ title="Avisos importantes"
161
+ trailing={notificationControl}
162
+ />
163
+ </Display.ListGroup>
164
+ ```
165
+
166
+ - `ListItem variant="mobile"` opts into wrapping, a 48-point target,
167
+ tonal row press feedback and adaptive accessories. `layout="auto"` stacks values/controls
168
+ below the text on narrow screens or enlarged text; use `layout="stacked"` for narrow
169
+ embedded containers. Navigation values are also exposed as native accessibility values.
170
+ - New mobile rows default to `density="compact"`: regular 14-point titles/values,
171
+ 13-point supporting text and 8-point vertical padding. `density="comfortable"` restores
172
+ the earlier PR's 17/16-point reading presentation and 12-point padding.
173
+ Legacy rows (`variant="default"`) keep their original appearance.
174
+ - Put a toggle/checkbox in a **non-pressable** row; do not nest a second action inside a
175
+ pressable row. The leading/trailing slots remain consumer-owned.
176
+ - `Typography.Text variant="bodyReadable"` and `Typography.SelectableText` with the same
177
+ variant opt into 17 points while retaining `configureFonts` and system font scaling.
178
+ The 14-point `body` and existing ListItem appearance remain unchanged.
179
+ - Customize per instance with `className`, `contentClassName`, slots, surface and separators.
180
+ Rows also accept `style`, `titleTextProps`, `subtitleTextProps` and `valueTextProps`.
181
+ Text props accept typography variants, weights, font families and native style arrays.
182
+ `ListGroup.headerProps` forwards header styling/text props; `footerTextProps` controls
183
+ string footers. Default groups use a 16-point section heading and 13-point description/footer.
184
+ Group `style`, `contentStyle` and `separatorStyle` control spacing, surface and dividers.
185
+ Headers expose `style`, `contentStyle`, `actionStyle` and `actionTextProps` as well.
186
+ For example, `titleTextProps={{ style: { fontFamily: "MyLoadedFont", fontSize: 15 } }}`
187
+ changes only one row, without copying its focus, accessibility or press behavior.
188
+ Customize branding centrally with `buildThemeFromBrand`/`extendTheme`, `setActiveTheme`
189
+ and `themeToVars` on the host, plus `configureFonts` for already-loaded font families.
190
+ Do not introduce a competing palette or hardcode colors in every screen.
191
+ - This group is not virtualized: use FlatList/SectionList for large or unbounded collections.
192
+
193
+ Stories: `Patterns/Forms/Settings` shows Sincpro defaults, long content, customized
194
+ surfaces and text roles. `Foundations/Typography/CompactHierarchy` and
195
+ `ComfortableHierarchy` show the same content at two densities, not two mandatory screens.
196
+ Customization is not a complete visual approval.
197
+
198
+ ### Compose collections, not a prescribed screen
199
+
200
+ `Patterns/Collections/Browsing` shows the same local data as rows, cards and
201
+ long-label variants. It combines icons, bounded 56-point artwork, readable text, metrics,
202
+ independent open/save actions and a native detail sheet. These are recipes to adapt,
203
+ not new domain-specific screen components or a web toolbar squeezed onto a phone.
204
+
205
+ ```tsx
206
+ import { FilterBar } from "@sincpro/mobile-ui/widgets";
207
+
208
+ <FilterBar
209
+ filters={filterOptions}
210
+ onFilterPress={toggleFilter}
211
+ onClearFilters={clearFilters}
212
+ onOpenFilters={openFilterSheet}
213
+ resultsLabel={resultsLabel}
214
+ search={{
215
+ value: query,
216
+ onChangeText: setQuery,
217
+ accessibilityLabel: "Buscar recursos",
218
+ clearAccessibilityLabel: "Limpiar búsqueda",
219
+ }}
220
+ />;
221
+ ```
222
+
223
+ Each filter supplies `id`, `label`, `selected` and optional `disabled`. State, matching,
224
+ result counts and advanced-filter drafts belong to the host. Clearing filters does not
225
+ silently clear the search query. Search, advanced action, result label and clear action
226
+ are optional; customize labels, `className`, theme and `layout="scroll|wrap|auto"`.
227
+ Auto wraps above font scale 1.3, otherwise it scrolls horizontally. Quick chips apply
228
+ immediately; the recipe's advanced sheet applies only on confirmation.
229
+
230
+ `Actions.FilterButton variant="mobile"` adds readable wrapping labels and 48-point targets.
231
+ `FilterButtonGroup layout="scroll|wrap"` supports both layouts. `Navigation.SearchBar`
232
+ uses accessible sizing by default in v2, with no legacy sizing flag.
233
+ When placing these controls in a host ScrollView, use `keyboardShouldPersistTaps="handled"`
234
+ so a filter can act on the first tap while the keyboard is open.
235
+ Use virtualized lists for production-sized collections.
236
+
237
+ Dependencies follow the latest stable version compatible with Expo 58, not unbounded
238
+ upgrades. Validate with `CI=1 npx expo install --check` in addition to the quality gates.
239
+
240
+ ### Controlled reordering without a closed row design
241
+
242
+ Optional import: `@sincpro/mobile-ui/widgets/ReorderableList`. Install
243
+ `react-native-sortables@^1.10.1` in the host; it uses the host's Gesture Handler 3.3+,
244
+ Reanimated 4.7+ and Worklets setup. Wrap the app in `GestureHandlerRootView`.
245
+ The adapter is deliberately **not** in the generic widgets/root barrels: applications
246
+ that do not use drag-and-drop do not need to resolve this optional peer.
247
+
248
+ ```tsx
249
+ import ReorderableList from "@sincpro/mobile-ui/widgets/ReorderableList";
250
+
251
+ <ReorderableList
252
+ items={sections}
253
+ keyExtractor={(item) => item.id}
254
+ getItemLabel={(item) => item.name}
255
+ onChange={(next) => setSections(next)}
256
+ renderItem={({ item }) => <MySectionContent section={item} />}
257
+ itemStyle={{ borderRadius: 16, padding: 8 }}
258
+ />;
259
+ ```
260
+
261
+ - A one-column, **non-virtualized short-list** adapter with variable-height content.
262
+ It does not own screen scrolling, persistence, confirmation or domain rules.
263
+ Do not use it as an unbounded feed, kanban board, file drop zone or cross-app drag API.
264
+ - Only the dedicated handle starts drag. Default arrows and the handle's native
265
+ increment/decrement actions offer alternatives; content can retain buttons, inputs
266
+ or any custom layout. Drag/drop uses insertion, not swapping.
267
+ - `onChange(next, { key, fromIndex, toIndex, source })` delivers an immutable order
268
+ (`source` is `drag` or `button`). Update controlled state synchronously; persist later,
269
+ showing save failures/retry in the host. A drop in the original position emits no change.
270
+ Draft/apply/cancel/undo can be built by owning that state, without replacing the gesture.
271
+ - Keys must be unique non-empty strings, stable across renders, never array indices.
272
+ Content updates with the same keys preserve current objects. Insert/remove/external
273
+ reorder or disabling **during** drag cancels the gesture, retains host data and shows
274
+ an accessible error. `onReorderRejected` reports `data-changed` or `disabled`.
275
+ The gesture engine remounts on rejection; keep persistent item state in the host.
276
+ - `renderItem` controls content. `renderControls` receives `dragHandle`, movement
277
+ callbacks and boundary flags; keep that handle and an accessible non-drag alternative
278
+ when replacing controls. `renderHandleContent` replaces **noninteractive** handle
279
+ contents only, keeping the gesture, accessible name and adjustable actions.
280
+ - Root `className`/`style`, `itemStyle`, `contentStyle`, `controlsStyle`, `handleStyle`
281
+ and `actionStyle` accept local layout/shape/spacing. Child content owns typography.
282
+ `labels` localizes controls, announcements and conflict errors.
283
+ - `sortableProps` forwards engine options such as activation delay, row gap, decorations,
284
+ animation settings and autoscroll, while the adapter owns identity, single-column
285
+ insertion and controlled callbacks. Provide a stable Reanimated `scrollableRef` to
286
+ the **actual Animated.ScrollView** for autoscroll; do not nest another scroll container
287
+ or attach the ref to a different view. Native scroll props/ref belong to the host.
288
+ Custom motion, targets and colors still require accessibility validation.
289
+
290
+ `Patterns/Collections/Reordering` demonstrates default, custom content/controls, long
291
+ rows and an external-update conflict. Only this recipe uses the new dependency;
292
+ existing form/list/query templates keep their behavior.
293
+
294
+ ### Keyboard-aware forms and field navigation
295
+
296
+ Expo 58 does not automatically make arbitrary form layouts keyboard-safe.
297
+ The host must include `react-native-keyboard-controller` and its `KeyboardProvider` at
298
+ the app root. Use a development build when the runtime does not include that native module.
299
+
300
+ ```tsx
301
+ <Form.KeyboardForm contentContainerStyle={{ padding: 16, gap: 16 }}>
302
+ {fields}
303
+ </Form.KeyboardForm>
304
+ ```
305
+
306
+ `KeyboardForm` owns the scroll and scopes editable inputs with `KeyboardToolbar.Group`.
307
+ Its toolbar provides previous/next/dismiss actions, including phone/number keyboards.
308
+ The scroll reserves the measured toolbar height plus 24 points (normally 72 total);
309
+ it grows with enlarged text. Override `bottomOffset` for additional accessories.
310
+ `toolbar={false}` keeps automatic avoidance without the toolbar.
311
+ Customize `toolbarLabels` or compose `Form.KeyboardToolbar` with an existing aware scroll,
312
+ using `onHeightChange` to reserve its measured height. The toolbar compensates for space
313
+ below its parent, such as a safe area or form footer, instead of floating over the caret.
314
+ The default toolbar uses compact icons; `toolbarProps={{ showDoneLabel: true }}` opts into
315
+ a text dismiss action. `toolbarProps` also accepts `className`, `style`, `actionStyle`,
316
+ `labelStyle`, `iconSize` and `renderActionContent(action, { label, disabled })`.
317
+ The content slot replaces only non-interactive content: the toolbar retains focus actions,
318
+ accessible labels and disabled states. `toolbarLabels` remains supported;
319
+ `toolbarProps` takes precedence. Font size/family and shape can be changed without
320
+ reimplementing keyboard behavior.
321
+ Do not nest this component inside another vertical ScrollView.
322
+
323
+ For existing templates, opt into `FormViewV2.Content keyboardToolbar` or
324
+ `AuthFormView keyboardToolbar` / `AuthContainer keyboardToolbar`; defaults stay unchanged.
325
+ `Form.Input` forwards its native ref for explicit return-key navigation:
326
+ `onSubmitEditing={() => nextRef.current?.focus()}` with `submitBehavior="submit"`.
327
+ `KeyboardForm` forwards its ref to the native aware scroll for methods such as
328
+ `assureFocusedInputVisible()` after validation/layout changes.
329
+ The three templates accept `keyboardFormProps` (excluding `children` and `toolbar`) for
330
+ the same ref, offsets, toolbar customization, scroll callbacks and content styling.
331
+ `keyboardToolbar` remains the explicit opt-in; omitting it preserves the original
332
+ template layout without a toolbar.
333
+ Multiline Return remains a newline; dismissing the keyboard does not submit the form.
334
+ Story: `Patterns/Forms/Keyboard navigation`.
335
+
336
+ `Form.ErrorSummary` presents ordered `{ id, label, message }` errors with
337
+ `onSelectError(id)`: the host focuses the corresponding native input ref with `.focus()`.
338
+ Use the same messages inline, keep controlled field drafts mounted, and place the summary
339
+ inside `KeyboardForm`. The first appearance is announced; updates while errors remain do
340
+ not repeatedly interrupt typing. Set `announce={false}` if the host already announces.
341
+ `renderHeader` and `renderErrorContent` replace presentation, not the named selection
342
+ action. Root/item/text props and styles remain customizable.
343
+ Story: `Patterns/Forms/Validation` includes real input refs and custom content.
344
+
345
+ `ListViewV2.Content` accepts native `keyboardShouldPersistTaps` and `keyboardDismissMode`
346
+ across populated, empty and error states. Handled row/retry actions receive the first tap
347
+ while search has focus (`"handled"` by default); pass `"never"` for the old dismiss-first
348
+ behavior. Drag-dismiss remains opt-in (`keyboardDismissMode="on-drag"`).
349
+ This changes interaction, not the list's typography or layout.
350
+
351
+ ```tsx
352
+ <FormViewV2.Content
353
+ keyboardToolbar
354
+ keyboardFormProps={{
355
+ ref: scrollRef,
356
+ toolbarProps: {
357
+ style: { borderRadius: 16 },
358
+ iconSize: 18,
359
+ showDoneLabel: true,
360
+ labelStyle: { fontFamily: "MyFont", fontSize: 13 },
361
+ },
362
+ }}
363
+ >
364
+ {fields}
365
+ </FormViewV2.Content>
366
+ ```
367
+
368
+ Default list rows now honor the template's `onPressItem` and `readonly` settings;
369
+ direct `Navigation.RowItem` usage can still supply the legacy callback on `item`.
370
+
371
+ ### Structured queries, not just quick chips
372
+
373
+ `widgets.FilterBuilder` edits a controlled `CriteriaGroup`: nested `all`/`any`/`none`
374
+ groups and configured conditions. Supply fields with their label, type, permitted
375
+ operators and arity (`none`, `one`, `range`); choice values are explicit. Numbers remain
376
+ editable strings until validated/committed, date fields use the native picker, and invalid
377
+ conditions produce visible errors. The mobile editor opens one group at a time instead of
378
+ squeezing an arbitrarily indented desktop tree into the screen.
379
+
380
+ `widgets.CollectionOptions` is a full-screen, keyboard-aware workspace:
381
+
382
+ ```tsx
383
+ <CollectionOptions
384
+ visible={open}
385
+ value={query}
386
+ fields={filterFields}
387
+ sortFields={sortFields}
388
+ groupFields={groupFields}
389
+ savedQueries={savedQueries}
390
+ onApply={applyQuery}
391
+ onClose={closeOptions}
392
+ onSave={saveQuery}
393
+ />
394
+ ```
395
+
396
+ `CollectionQuery` contains `search`, `filter`, ordered `sort` entries and ordered
397
+ `groupBy` field IDs. Order and grouping priorities have explicit move/remove actions;
398
+ dragging is not required. Loading a saved configuration changes the draft only;
399
+ Apply commits all dimensions together, Cancel discards the draft. Save errors keep the
400
+ draft/name for retry. The host owns saving/loading and closes the workspace after Apply.
401
+
402
+ The DS does not fetch models, evaluate filters, sort a page as if it were the full dataset,
403
+ persist favorites or translate this tree into a backend query language. Relational paths,
404
+ remote model discovery and a JSON editor are not implemented here. The web builder is a
405
+ semantic reference, not a component copied into mobile.
406
+
407
+ `Navigation.SearchBar suggestions={{ items, onSelect, loading, error, onRetry }}` shows
408
+ host-supplied autocomplete while focused. Choosing a suggestion calls `onSelect` without
409
+ silently rewriting the query. A host may complete a title or create a field-specific
410
+ condition; it owns matching, request cancellation and stale responses.
411
+
412
+ `Navigation.Search` composes that existing field with horizontally scrolling chips,
413
+ advanced filters in a `Dialog/Sheet`, recent searches and suggestions:
414
+
415
+ ```tsx
416
+ <Navigation.Search.Root>
417
+ <Navigation.Search.Field value={query} onChangeText={setQuery} />
418
+ <Navigation.Search.Filters>
419
+ <Actions.FilterButton active title="Todos" onPress={() => {}} variant="mobile" />
420
+ </Navigation.Search.Filters>
421
+ <Navigation.Search.FilterSheet
422
+ activeCount={activeFilterCount}
423
+ onApply={applyFilters}
424
+ onClear={clearDraftFilters}
425
+ >
426
+ <FilterFields />
427
+ </Navigation.Search.FilterSheet>
428
+ <Navigation.Search.History items={recent} onRemove={removeRecent} onSelect={setQuery} />
429
+ </Navigation.Search.Root>
430
+ ```
431
+
432
+ It composes presentation only; the host owns query/filter state, history persistence and
433
+ results. Keep `ListViewV2.Header.Search` when its built-in debounced `onSearch` behavior is
434
+ preferred. Use `Navigation.AppBar` for back/context and `Navigation.Steps` for sequential
435
+ flows rather than adding breadcrumb trails to compact mobile headers.
436
+
437
+ Story: `Patterns/Collections/Query configuration` exercises actual local filtering,
438
+ multiple sorting priorities, grouping levels and saving/loading configurations.
439
+
440
+ ### Native E2E
441
+
442
+ With Maestro installed and an iOS simulator running Expo Go, start an isolated test server:
443
+
444
+ ```bash
445
+ EXPO_PUBLIC_STORYBOOK_TEST=1 npx expo start --port 8082
446
+ make storybook-e2e
447
+ ```
448
+
449
+ This disables Storybook's on-device chrome for tests without changing the normal server.
450
+ The flows verify copy, edit, cancel, failed-save recovery and long-content/dark interaction.
451
+ `STORYBOOK_URL` can override the default `exp://127.0.0.1:8082`.
452
+ Maestro analytics are disabled by the Makefile target. These flows target Expo Go on iOS;
453
+ Android needs a connected device and its appropriate app ID/host configuration.
454
+ Tests stop/relaunch Expo Go and switch stories; run them on a dedicated test simulator.
455
+ Enlarged-text screenshots are accessibility stress tests, not the default typography.
456
+
84
457
  ### Maintained for the long term
85
458
 
86
459
  Enterprise software lives for years. The screens built with this DS will be in production long after the initial project is delivered. Sincpro's commitment is to maintain API stability, patch what breaks on platform upgrades, and evolve the system without forcing rewrites on consumers.
@@ -131,10 +504,10 @@ const { control } = useForm<{ email: string; amount: number }>();
131
504
  Queued toast system with promise support — covers the full loading → success / error lifecycle:
132
505
 
133
506
  ```tsx
134
- import { ToastProvider, useToast } from "@sincpro/mobile-ui/Feedback";
507
+ import { Toast, ToastProvider, useToast } from "@sincpro/mobile-ui/Feedback";
135
508
 
136
509
  // 1. Wrap the app once (createAppShell does this automatically)
137
- <ToastProvider position="top">{children}</ToastProvider>
510
+ <ToastProvider position="top">{children}</ToastProvider>;
138
511
 
139
512
  // 2. Call from anywhere in the tree
140
513
  const toast = useToast();
@@ -142,10 +515,64 @@ toast.success("Invoice saved");
142
515
  toast.promise(sendInvoice(), {
143
516
  loading: "Sending…",
144
517
  success: "Invoice sent",
145
- error: "Failed to send",
518
+ error: "Failed to send",
519
+ });
520
+
521
+ // Compose custom content while keeping the shared toast surface and lifecycle
522
+ toast.show({
523
+ render: ({ dismiss, update }) => (
524
+ <>
525
+ <Toast.Icon variant="loading" />
526
+ <Toast.Content>
527
+ <Toast.Title>Syncing catalog</Toast.Title>
528
+ <Toast.Description>Downloading product images…</Toast.Description>
529
+ </Toast.Content>
530
+ <Toast.Action label="Details" onPress={() => update({ message: "Still syncing…" })} />
531
+ <Toast.Close onPress={dismiss} />
532
+ </>
533
+ ),
534
+ variant: "loading",
146
535
  });
147
536
  ```
148
537
 
538
+ Toast cards can also be dismissed with an upward or downward swipe. Custom render content keeps
539
+ the shared card surface, safe-area placement, lifecycle, and swipe behavior.
540
+
541
+ Temporary toasts now show a close action by default too; the `dismissible: false` option explicitly
542
+ removes it. Escape dismisses the default surface. Enter/exit animation follows live
543
+ Reduce Motion; loading uses the common accessible spinner. `Toast.Root` accepts native
544
+ view props/styles; `Toast.Action` and `Toast.Close` accept native pressable props/styles.
545
+ Custom renderers must include their own visible close alternative when appropriate.
546
+
547
+ ### Composable mobile navigation and loading
548
+
549
+ `Display.Carousel` supports short, non-virtualized collections with named 48-point
550
+ previous/next controls, localized page labels and optional controlled `index` /
551
+ `onIndexChange`. Rejected controlled swipes restore the accepted page. Measured trailing
552
+ space lets the last narrow card reach its snap position.
553
+ `renderControls({ index, count, canPrevious, canNext, previous, next, goTo })` replaces the
554
+ controls without replacing index validation or scrolling. Custom controls own their
555
+ accessible labels/disabled state and must provide a non-swipe alternative.
556
+ Use `itemWidth` / `gap` for snap geometry; `itemStyle`, root native props/styles,
557
+ `contentContainerStyle`, dot styles and `paginationTextProps` customize presentation.
558
+ Data keys must be unique, non-empty strings. Use a virtualized list for large datasets.
559
+ Stories: `Components/Data display/Carousel` and `Carousel Customized`.
560
+
561
+ `Feedback.DomainSplashScreen` waits for actual `isLoading={false}`, respecting
562
+ `minDuration`, rather than fabricating progress or finishing after a timer.
563
+ Children remain mounted, preserving drafts, but are inaccessible/non-interactive while
564
+ loading. `onComplete` runs once per completed cycle; timers/animations are cleaned up.
565
+ `renderLoading({ domainName, isLoading, reducedMotion })`, `containerStyle` and
566
+ `overlayStyle` let developers/AI own the visuals without replacing that lifecycle.
567
+ Do not use splash completion as a data-loading signal.
568
+ Story: `Patterns/Feedback/Loading lifecycle`.
569
+
570
+ The shared `useReducedMotion` hook makes one native preference query/subscription for
571
+ all active consumers and cancels stale queries when they leave. RN sheets, menus,
572
+ confirmation/edit dialogs, dropdowns, story viewer, accordion, dock and collection/promo
573
+ overlays follow the live setting; drag interactions remain available.
574
+ This does not certify arbitrary custom renderers or the optional native Sheet engine.
575
+
149
576
  ### Bottom Sheet and Dialogs
150
577
 
151
578
  ```tsx
@@ -155,7 +582,7 @@ import { ConfirmationProvider, useConfirmationContext } from "@sincpro/mobile-ui
155
582
  // Controlled sheet — built on RN Modal, no native peer deps
156
583
  <Sheet visible={open} onClose={() => setOpen(false)} title="Options">
157
584
  <Actions.Button onPress={confirm}>Confirm</Actions.Button>
158
- </Sheet>
585
+ </Sheet>;
159
586
 
160
587
  // Imperative confirmation
161
588
  const { confirm } = useConfirmationContext();
@@ -168,10 +595,16 @@ const agreed = await confirm({ title: "Delete invoice?", message: "This cannot b
168
595
  import { Wizard, useWizard } from "@sincpro/mobile-ui/views/Wizard";
169
596
 
170
597
  <Wizard initialStep="customer">
171
- <Wizard.Step id="customer"><CustomerStep /></Wizard.Step>
172
- <Wizard.Step id="items"><ItemsStep /></Wizard.Step>
173
- <Wizard.Step id="review"><ReviewStep /></Wizard.Step>
174
- </Wizard>
598
+ <Wizard.Step id="customer">
599
+ <CustomerStep />
600
+ </Wizard.Step>
601
+ <Wizard.Step id="items">
602
+ <ItemsStep />
603
+ </Wizard.Step>
604
+ <Wizard.Step id="review">
605
+ <ReviewStep />
606
+ </Wizard.Step>
607
+ </Wizard>;
175
608
 
176
609
  // Inside any step
177
610
  const { go, back } = useWizard();
@@ -189,171 +622,201 @@ import { Display } from "@sincpro/mobile-ui/Display";
189
622
  <Display.Badge variant="success">Paid</Display.Badge>
190
623
  <Display.Monetary amount={1500.00} variant="dataLarge" />
191
624
  <Display.DataTable columns={cols} rows={rows} />
625
+
626
+ <Display.AmountSummary.Root currencySymbol="Bs">
627
+ <Display.AmountSummary.Row label="Subtotal" value={120} />
628
+ <Display.AmountSummary.Row label="Delivery" value={8.5} />
629
+ <Display.AmountSummary.Total label="Total" value={128.5} />
630
+ </Display.AmountSummary.Root>
192
631
  ```
193
632
 
633
+ `AmountSummary` formats and arranges amounts; the app supplies labels, calculations and
634
+ business states. `KeyValue copyable` remains the reusable copy affordance for identifiers.
635
+ `Actions.Dock` defaults to a fixed bottom action bar. Swipe up on its handle or press
636
+ “Más opciones” to reveal additional content above it; the primary action remains fixed.
637
+ The dedicated 48-point handle owns the touch from its start, so an ancestor scroll view
638
+ cannot steal it. Swipe up opens a native modal; swipe down on the panel handle closes it.
639
+ The expanded panel blocks and dims the underlying screen, while the primary action remains
640
+ outside its scrolling content. Back, accessible escape, the collapse button and backdrop
641
+ close it (`dismissOnBackdrop={false}` disables only backdrop dismissal).
642
+ Use `expanded` / `onExpandedChange` for host-controlled state, or `defaultExpanded` for
643
+ uncontrolled initial state. Wrap the app in `SafeAreaProvider`. Keep the collapsed fixed
644
+ bar outside scroll views, within the screen's safe area, and reserve space below list content.
645
+ Inside `FormViewV2.Footer`, set `placement="footer"` to reuse its sticky layout.
646
+ The panel fits its content up to `maxExpandedHeightRatio` (default `0.6`) and available screen
647
+ space; overflow scrolls and the modal avoids the keyboard.
648
+
649
+ This changes the Dock's former inline expansion behavior. See
650
+ [the migration guide](docs/MOBILE_UX_MIGRATION.md) before adopting it in core or host apps.
651
+
194
652
  ---
195
653
 
196
654
  ## Component Catalog
197
655
 
198
656
  ### Actions
199
657
 
200
- | Component | Description |
201
- |-----------|-------------|
202
- | `Actions.Button` | Primary action — gradient, outline, flat, link variants; optional confirm dialog |
203
- | `Actions.IconButton` | Icon-only tap target — ghost, primary, secondary, tertiary variants |
204
- | `Actions.FAB` | Floating Action Button — fixed position, optional label |
205
- | `Actions.FilterButton` | Chip-style active/inactive toggle |
206
- | `Actions.FilterButtonGroup` | Horizontal scroll container for filter chips |
658
+ | Component | Description |
659
+ | --------------------------- | -------------------------------------------------------------------------------- |
660
+ | `Actions.Button` | Primary action — gradient, outline, flat, link variants; optional confirm dialog |
661
+ | `Actions.IconButton` | Icon-only tap target — ghost, primary, secondary, tertiary variants |
662
+ | `Actions.FAB` | Floating Action Button — fixed position, optional label |
663
+ | `Actions.Dock` | Fixed primary action with swipe-expandable content and persistent CTA |
664
+ | `Actions.FilterButton` | Chip-style active/inactive toggle |
665
+ | `Actions.FilterButtonGroup` | Horizontal scroll container for filter chips |
207
666
 
208
667
  ### Display
209
668
 
210
- | Component | Description |
211
- |-----------|-------------|
212
- | `Display.Badge` | Status badge — success, warning, danger, info, neutral |
213
- | `Display.Card` | Surface container with optional shadow and padding |
214
- | `Display.KeyValue` | Label + value row, optional clipboard copy |
215
- | `Display.Stat` | KPI cell — label, value, optional trend indicator |
216
- | `Display.StatCard` | Card-wrapped KPI |
217
- | `Display.Monetary` | Formatted monetary amount using mono typography |
218
- | `Display.PriceTag` | Currency-formatted price with size options |
219
- | `Display.DataTable` | Scrollable table for structured data |
220
- | `Display.ListItem` | Row with icon, label, sublabel, optional action |
221
- | `Display.Chip` | Compact label tag |
222
- | `Display.Avatar` / `AvatarGroup` | User avatar, stacked group |
223
- | `Display.Icon` | Vector icon wrapper (`@expo/vector-icons`) |
224
- | `Display.StatusDot` | Colored presence / state indicator |
225
- | `Display.Divider` | Horizontal separator |
226
- | `Display.GradientSurface` | Container with linear gradient background |
227
- | `Display.Timeline` | Vertical event timeline |
228
- | `Display.SwipeableRow` | Row with swipe-to-reveal actions |
229
- | `Display.Tooltip` | Overlay tooltip |
230
- | `Display.CopyableText` | Text with one-tap clipboard copy |
231
- | `Display.Logo` | Brand logo (falls back to `setBranding` value) |
232
- | `Display.Accordion` | Collapsible content section |
233
- | `Display.Carousel` | Horizontal swipe carousel |
234
- | `Display.Story` / `StoryTray` / `StoryViewer` | Stories UI pattern |
235
- | `Display.MediaCard` | Card with image or video embed |
236
- | `Display.Pattern` | Decorative background pattern |
669
+ | Component | Description |
670
+ | --------------------------------------------- | ------------------------------------------------------ |
671
+ | `Display.Badge` | Status badge — success, warning, danger, info, neutral |
672
+ | `Display.Card` | Surface container with optional shadow and padding |
673
+ | `Display.KeyValue` | Label + value row, optional clipboard copy |
674
+ | `Display.AmountSummary` | Composable monetary rows and total |
675
+ | `Display.Stat` | KPI cell — label, value, optional trend indicator |
676
+ | `Display.StatCard` | Card-wrapped KPI |
677
+ | `Display.Monetary` | Formatted monetary amount using mono typography |
678
+ | `Display.PriceTag` | Currency-formatted price with size options |
679
+ | `Display.DataTable` | Scrollable table for structured data |
680
+ | `Display.ListItem` | Row with icon, label, sublabel, optional action |
681
+ | `Display.Chip` | Compact label tag |
682
+ | `Display.Avatar` / `AvatarGroup` | User avatar, stacked group |
683
+ | `Display.Icon` | Vector icon wrapper (`@expo/vector-icons`) |
684
+ | `Display.StatusDot` | Colored presence / state indicator |
685
+ | `Display.Divider` | Horizontal separator |
686
+ | `Display.GradientSurface` | Container with linear gradient background |
687
+ | `Display.Timeline` | Vertical event timeline |
688
+ | `Display.SwipeableRow` | Row with swipe-to-reveal actions |
689
+ | `Display.Tooltip` | Overlay tooltip |
690
+ | `Display.CopyableText` | Text with one-tap clipboard copy |
691
+ | `Display.Logo` | Brand logo (falls back to `setBranding` value) |
692
+ | `Display.Accordion` | Collapsible content section |
693
+ | `Display.Carousel` | Controlled short carousel with non-swipe alternatives |
694
+ | `Display.Story` / `StoryTray` / `StoryViewer` | Stories UI pattern |
695
+ | `Display.MediaCard` | Card with image or video embed |
696
+ | `Display.Pattern` | Decorative background pattern |
237
697
 
238
698
  ### Form
239
699
 
240
- | Component | Description |
241
- |-----------|-------------|
242
- | `Form.TextInput` | Text field with label, error, and helper text |
243
- | `Form.PasswordInput` | Text input with show/hide toggle |
244
- | `Form.PinInput` | PIN code entry |
245
- | `Form.MonetaryInput` | Currency input with formatting |
246
- | `Form.QuantitySelector` | Integer increment / decrement |
247
- | `Form.FractionalQuantityInput` | Decimal quantity input |
248
- | `Form.Select` | Dropdown select (modal-based) |
249
- | `Form.Dropdown` | Inline dropdown menu |
250
- | `Form.RadioButton` | Single-select radio option |
251
- | `Form.Checkbox` | Multi-select checkbox |
252
- | `Form.Switch` | Toggle switch |
253
- | `Form.Slider` | Range slider (`@react-native-community/slider` peer) |
254
- | `Form.DatePicker` | Date picker (`@react-native-community/datetimepicker` peer) |
255
- | `Form.SearchBar` | Search input with clear button |
256
- | `Form.ControlledInput` | `TextInput` + react-hook-form |
257
- | `Form.ControlledRadioButton` | `RadioButton` + react-hook-form |
258
- | `Form.ControlledCheckbox` | `Checkbox` + react-hook-form |
259
- | `Form.ControlledDropdown` | `Dropdown` + react-hook-form |
260
- | `Form.ControlledMonetaryInput` | `MonetaryInput` + react-hook-form |
700
+ | Component | Description |
701
+ | --------------------------------------- | ------------------------------------------------------------------- |
702
+ | `Form.TextInput` | Text field with label, error, and helper text |
703
+ | `Form.PasswordInput` | Text input with show/hide toggle |
704
+ | `Form.PinInput` | PIN code entry |
705
+ | `Form.MonetaryInput` | Currency input with formatting |
706
+ | `Form.QuantitySelector` | Integer increment / decrement |
707
+ | `Form.FractionalQuantityInput` | Decimal quantity input |
708
+ | `Form.Select` | Dropdown select (modal-based) |
709
+ | `Form.Dropdown` | Inline dropdown menu |
710
+ | `Form.ErrorSummary` | Ordered validation errors linked to native field focus |
711
+ | `Form.RadioButton` | Single-select radio option |
712
+ | `Form.Checkbox` | Multi-select checkbox |
713
+ | `Form.Switch` | Toggle switch |
714
+ | `Form.Slider` | Range slider (`@react-native-community/slider` peer) |
715
+ | `Form.DatePicker` | Date picker (`@react-native-community/datetimepicker` peer) |
716
+ | `Form.SearchBar` | Search input with clear button |
717
+ | `Form.ControlledInput` | `TextInput` + react-hook-form |
718
+ | `Form.ControlledRadioButton` | `RadioButton` + react-hook-form |
719
+ | `Form.ControlledCheckbox` | `Checkbox` + react-hook-form |
720
+ | `Form.ControlledDropdown` | `Dropdown` + react-hook-form |
721
+ | `Form.ControlledMonetaryInput` | `MonetaryInput` + react-hook-form |
261
722
  | `Actions.Button` / `IconButton` / `FAB` | Action components (preferred over deprecated `Form.Button` aliases) |
262
723
 
263
724
  ### Feedback
264
725
 
265
- | Component | Description |
266
- |-----------|-------------|
267
- | `Feedback.Spinner` | Animated loading spinner |
268
- | `Feedback.Loading` | Full-screen loading overlay |
269
- | `Feedback.Skeleton` | Content placeholder shimmer |
270
- | `Feedback.Progress` | Linear progress bar |
271
- | `Feedback.ProgressCircle` | Circular progress indicator |
272
- | `Feedback.Banner` | Contextual notification banner |
273
- | `Feedback.Empty` / `EmptyState` | Empty list or page placeholder |
274
- | `Feedback.Error` | Error state with optional retry |
275
- | `Feedback.PermissionCard` | Permission request UI |
276
- | `ToastProvider` / `useToast` | Queued toast notification system |
277
- | `ErrorBoundary` | React error boundary wrapper |
726
+ | Component | Description |
727
+ | ------------------------------- | -------------------------------- |
728
+ | `Feedback.Spinner` | Animated loading spinner |
729
+ | `Feedback.Loading` | Full-screen loading overlay |
730
+ | `Feedback.DomainSplashScreen` | Customizable real-load lifecycle |
731
+ | `Feedback.Skeleton` | Content placeholder shimmer |
732
+ | `Feedback.Progress` | Linear progress bar |
733
+ | `Feedback.ProgressCircle` | Circular progress indicator |
734
+ | `Feedback.Banner` | Contextual notification banner |
735
+ | `Feedback.Empty` / `EmptyState` | Empty list or page placeholder |
736
+ | `Feedback.Error` | Error state with optional retry |
737
+ | `Feedback.PermissionCard` | Permission request UI |
738
+ | `ToastProvider` / `useToast` | Queued toast notification system |
739
+ | `ErrorBoundary` | React error boundary wrapper |
278
740
 
279
741
  ### Navigation
280
742
 
281
- | Component | Description |
282
- |-----------|-------------|
283
- | `Navigation.AppBar` | Top navigation bar with action slots |
284
- | `Navigation.BottomNav` | Bottom tab bar |
285
- | `Navigation.Header` | Screen header |
286
- | `Navigation.HeaderToolbar` | Header with left, center, right slots |
287
- | `Navigation.CollapsingHeader` | Header that collapses on scroll |
288
- | `Navigation.FloatingBar` | Floating action bar |
289
- | `Navigation.SearchBar` | Navigation-level search input |
290
- | `Navigation.SegmentedControl` | Segmented control switcher |
291
- | `Navigation.Tabs` | Tab navigation container |
292
- | `Navigation.Steps` | Step indicator / breadcrumb |
293
- | `Navigation.Menu` / `MenuSection` | Dropdown menu with sections |
743
+ | Component | Description |
744
+ | --------------------------------- | ------------------------------------------------- |
745
+ | `Navigation.AppBar` | Top navigation bar with action slots |
746
+ | `Navigation.BottomNav` | Bottom tab bar |
747
+ | `Navigation.Header` | Screen header |
748
+ | `Navigation.HeaderToolbar` | Header with left, center, right slots |
749
+ | `Navigation.CollapsingHeader` | Header that collapses on scroll |
750
+ | `Navigation.FloatingBar` | Floating action bar |
751
+ | `Navigation.SearchBar` | Navigation-level search input |
752
+ | `Navigation.Search` | Compound field, filters, history and filter sheet |
753
+ | `Navigation.SegmentedControl` | Segmented control switcher |
754
+ | `Navigation.Tabs` | Tab navigation container |
755
+ | `Navigation.Steps` | Step indicator / breadcrumb |
756
+ | `Navigation.Menu` / `MenuSection` | Dropdown menu with sections |
294
757
 
295
758
  ### Dialog
296
759
 
297
- | Component | Description |
298
- |-----------|-------------|
299
- | `Dialog.BottomSheet` | Bottom drawer (RN Modal, no native peer deps) |
300
- | `Dialog.ActionSheet` | Action sheet with options list |
301
- | `Dialog.Confirmation` | Confirm / cancel modal |
302
- | `Dialog.EditValue` | Inline edit value modal |
303
- | `Sheet` (direct import) | Controlled sheet via `Dialog/Dialog.Sheet` |
304
- | `ConfirmationProvider` / `useConfirmationContext` | Imperative confirmation API |
760
+ | Component | Description |
761
+ | ------------------------------------------------- | --------------------------------------------- |
762
+ | `Dialog.BottomSheet` | Bottom drawer (RN Modal, no native peer deps) |
763
+ | `Dialog.ActionSheet` | Action sheet with options list |
764
+ | `Dialog.Confirmation` | Confirm / cancel modal |
765
+ | `Dialog.EditValue` | Inline edit value modal |
766
+ | `Sheet` (direct import) | Controlled sheet via `Dialog/Dialog.Sheet` |
767
+ | `ConfirmationProvider` / `useConfirmationContext` | Imperative confirmation API |
305
768
 
306
769
  ### Typography
307
770
 
308
- | Component | Description |
309
- |-----------|-------------|
310
- | `Typography.Text` | Main text component — accepts `variant` prop from the full scale |
311
- | `Typography.Heading` | Heading shorthand |
771
+ | Component | Description |
772
+ | -------------------- | ---------------------------------------------------------------- |
773
+ | `Typography.Text` | Main text component — accepts `variant` prop from the full scale |
774
+ | `Typography.Heading` | Heading shorthand |
312
775
 
313
776
  ### Primitives
314
777
 
315
- | Component | Description |
316
- |-----------|-------------|
317
- | `Box` | NativeWind layout container |
318
- | `Row` | Horizontal flex |
319
- | `Stack` | Vertical flex |
320
- | `Spacer` | Flexible space filler |
321
- | `Pressable` | Animated press target |
322
- | `AspectRatio` | Aspect-ratio container |
778
+ | Component | Description |
779
+ | ------------- | --------------------------- |
780
+ | `Box` | NativeWind layout container |
781
+ | `Row` | Horizontal flex |
782
+ | `Stack` | Vertical flex |
783
+ | `Spacer` | Flexible space filler |
784
+ | `Pressable` | Animated press target |
785
+ | `AspectRatio` | Aspect-ratio container |
323
786
 
324
787
  ### Layouts
325
788
 
326
- | Component | Description |
327
- |-----------|-------------|
328
- | `Container` | Padded layout container |
329
- | `ScrollContainer` | Scrollable layout wrapper |
330
- | `GradientContainer` | Container with gradient background |
331
- | `PlainLayout` | Unstyled layout for custom screens |
789
+ | Component | Description |
790
+ | -------------------- | ------------------------------------- |
791
+ | `Container` | Padded layout container |
792
+ | `ScrollContainer` | Scrollable layout wrapper |
793
+ | `GradientContainer` | Container with gradient background |
794
+ | `PlainLayout` | Unstyled layout for custom screens |
332
795
  | `TabNavigatorLayout` | Layout for tab-based screen structure |
333
796
 
334
797
  ### Views
335
798
 
336
- | Component | Description |
337
- |-----------|-------------|
338
- | `ListViewV2` | Paginated list with filtering and empty states |
339
- | `FormViewV2` | Dynamic form view with field management |
340
- | `AuthFormView` | Authentication form layout |
799
+ | Component | Description |
800
+ | ---------------------- | ------------------------------------------------- |
801
+ | `ListViewV2` | Paginated list with filtering and empty states |
802
+ | `FormViewV2` | Dynamic form view with field management |
803
+ | `AuthFormView` | Authentication form layout |
341
804
  | `Wizard` / `useWizard` | Multi-step wizard with guards and lifecycle hooks |
342
- | `AppSplashView` | Branded splash screen with logo and loading state |
805
+ | `AppSplashView` | Branded splash screen with logo and loading state |
343
806
 
344
807
  ### Widgets
345
808
 
346
- | Component | Description |
347
- |-----------|-------------|
348
- | `ScreenHeader` | Composite screen header widget |
349
- | `MenuGrid` / `MenuCard` | Grid and card-based navigation menus |
350
- | `QuickActions` | Quick action icon grid |
351
- | `PromoBanner` | Swipeable promotional carousel |
352
- | `PromoModal` | Promotional modal overlay |
353
- | `HomeHeader` | Home screen hero header |
354
- | `GeoPermissionCard` | Geolocation permission request card |
355
- | `TimeZoneSelector` | Timezone picker widget |
356
- | `JSONViewer` | Collapsible JSON tree (debug / dev tools) |
809
+ | Component | Description |
810
+ | ----------------------- | ----------------------------------------- |
811
+ | `ScreenHeader` | Composite screen header widget |
812
+ | `MenuGrid` / `MenuCard` | Grid and card-based navigation menus |
813
+ | `QuickActions` | Quick action icon grid |
814
+ | `PromoBanner` | Swipeable promotional carousel |
815
+ | `PromoModal` | Promotional modal overlay |
816
+ | `HomeHeader` | Home screen hero header |
817
+ | `GeoPermissionCard` | Geolocation permission request card |
818
+ | `TimeZoneSelector` | Timezone picker widget |
819
+ | `JSONViewer` | Collapsible JSON tree (debug / dev tools) |
357
820
 
358
821
  ---
359
822
 
@@ -374,7 +837,7 @@ setActiveTheme(MY_DARK_THEME);
374
837
 
375
838
  // Inject CSS variables into a subtree
376
839
  const themeStyle = vars(themeToVars(MY_DARK_THEME));
377
- <View style={themeStyle}>{children}</View>
840
+ <View style={themeStyle}>{children}</View>;
378
841
  ```
379
842
 
380
843
  ### Reading the theme in components
@@ -384,7 +847,9 @@ import { useTheme } from "@sincpro/mobile-ui/theme";
384
847
 
385
848
  function PriceCard() {
386
849
  const theme = useTheme();
387
- return <View style={{ backgroundColor: theme.bg.card, borderColor: theme.border.default }} />;
850
+ return (
851
+ <View style={{ backgroundColor: theme.bg.card, borderColor: theme.border.default }} />
852
+ );
388
853
  }
389
854
  ```
390
855
 
@@ -395,33 +860,33 @@ import { extendTheme } from "@sincpro/mobile-ui/theme";
395
860
 
396
861
  // Deep-merges on top of the DS base theme
397
862
  const MY_THEME = extendTheme({
398
- accent: "#00C357",
399
- primary: "#0B0B0B",
863
+ accent: "#00C357",
864
+ primary: "#0B0B0B",
400
865
  secondary: "#6B6963",
401
866
  bg: {
402
867
  page: "#FAFAFA",
403
868
  card: "#FFFFFF",
404
869
  },
405
870
  text: {
406
- accent: "#006E2E", // text on accent-tinted surfaces
871
+ accent: "#006E2E", // text on accent-tinted surfaces
407
872
  },
408
873
  });
409
874
  ```
410
875
 
411
876
  ### ThemeTokens reference
412
877
 
413
- | Group | Keys |
414
- |-------|------|
415
- | `primary / secondary / accent` | Main brand color scales |
416
- | `bg.*` | `page, card, muted, accent, hover, disabled, popover` |
417
- | `text.*` | `primary, secondary, tertiary, muted, accent, inverse, disabled, onPrimary, onSecondary, onAccent, onDanger, onSuccess` |
418
- | `icon.*` | `primary, secondary, tertiary, inverse, disabled` |
419
- | `border.*` | `default, light, strong, focus` |
420
- | `success / warning / danger / info` | Semantic state colors |
421
- | `*Light` | Tinted backgrounds for each state |
422
- | `ring / input` | Focus ring and input border |
423
- | `gradient.*` | `primary, accent` — arrays of color stops |
424
- | `shadow.*` | `sm, md, lg` — React Native shadow objects |
878
+ | Group | Keys |
879
+ | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
880
+ | `primary / secondary / accent` | Main brand color scales |
881
+ | `bg.*` | `page, card, muted, accent, hover, disabled, popover` |
882
+ | `text.*` | `primary, secondary, tertiary, muted, accent, inverse, disabled, onPrimary, onSecondary, onAccent, onDanger, onSuccess` |
883
+ | `icon.*` | `primary, secondary, tertiary, inverse, disabled` |
884
+ | `border.*` | `default, light, strong, focus` |
885
+ | `success / warning / danger / info` | Semantic state colors |
886
+ | `*Light` | Tinted backgrounds for each state |
887
+ | `ring / input` | Focus ring and input border |
888
+ | `gradient.*` | `primary, accent` — arrays of color stops |
889
+ | `shadow.*` | `sm, md, lg` — React Native shadow objects |
425
890
 
426
891
  ---
427
892
 
@@ -445,16 +910,16 @@ import { Typography } from "@sincpro/mobile-ui/Typography";
445
910
  <Typography.Text variant="button">Action Label</Typography.Text>
446
911
  ```
447
912
 
448
- | Group | Variants |
449
- |-------|----------|
450
- | Display | `display` |
451
- | Headings | `h1` `h2` `h3` `h4` `h5` `h6` |
452
- | Body | `bodyLarge` `body` `bodySmall` |
453
- | Labels | `label` `labelSmall` |
454
- | Data (mono) | `data` `dataLarge` |
455
- | Meta (mono) | `overline` `caption` `captionSmall` |
456
- | Buttons | `button` `buttonSmall` `buttonLarge` |
457
- | Legacy | `title` `subtitle` |
913
+ | Group | Variants |
914
+ | ----------- | ------------------------------------ |
915
+ | Display | `display` |
916
+ | Headings | `h1` `h2` `h3` `h4` `h5` `h6` |
917
+ | Body | `bodyLarge` `body` `bodySmall` |
918
+ | Labels | `label` `labelSmall` |
919
+ | Data (mono) | `data` `dataLarge` |
920
+ | Meta (mono) | `overline` `caption` `captionSmall` |
921
+ | Buttons | `button` `buttonSmall` `buttonLarge` |
922
+ | Legacy | `title` `subtitle` |
458
923
 
459
924
  ### FontRole
460
925
 
@@ -475,9 +940,9 @@ import { configureFonts, resetFonts } from "@sincpro/mobile-ui/theme/typography"
475
940
 
476
941
  configureFonts({
477
942
  titleRegular: "Satoshi-Regular",
478
- titleMedium: "Satoshi-Medium",
479
- title: "Satoshi-Bold",
480
- titleBlack: "Satoshi-Black",
943
+ titleMedium: "Satoshi-Medium",
944
+ title: "Satoshi-Bold",
945
+ titleBlack: "Satoshi-Black",
481
946
  });
482
947
 
483
948
  // Reset to defaults — useful in tests and Storybook stories
@@ -486,15 +951,15 @@ resetFonts();
486
951
 
487
952
  ### Font size scale
488
953
 
489
- | Token | `px` | Token | `px` |
490
- |-------|------|-------|------|
491
- | `xs` | 10 | `4xl` | 28 |
492
- | `sm` | 13 | `5xl` | 32 |
493
- | `base` | 14 | `6xl` | 36 |
494
- | `lg` | 16 | `7xl` | 48 |
495
- | `xl` | 18 | `8xl` | 64 |
496
- | `2xl` | 20 | | |
497
- | `3xl` | 24 | | |
954
+ | Token | `px` | Token | `px` |
955
+ | ------ | ---- | ----- | ---- |
956
+ | `xs` | 10 | `4xl` | 28 |
957
+ | `sm` | 13 | `5xl` | 32 |
958
+ | `base` | 14 | `6xl` | 36 |
959
+ | `lg` | 16 | `7xl` | 48 |
960
+ | `xl` | 18 | `8xl` | 64 |
961
+ | `2xl` | 20 | | |
962
+ | `3xl` | 24 | | |
498
963
 
499
964
  ### Custom text styles
500
965
 
@@ -506,7 +971,7 @@ const errorLabel = createTextStyle("labelSmall", { color: "#FF4D1F" });
506
971
 
507
972
  // Fully custom
508
973
  const amountStyle = createCustomTextStyle("4xl", "monoMedium", {
509
- lineHeight: "none",
974
+ lineHeight: "none",
510
975
  letterSpacing: "tight",
511
976
  });
512
977
  ```
@@ -521,22 +986,22 @@ Centralized animation constants so motion feels consistent across every screen
521
986
  import { duration, easing, spring } from "@sincpro/mobile-ui/tokens";
522
987
 
523
988
  // Durations (ms)
524
- duration.instant // 0 — immediate state swap, no transition
525
- duration.fast // 120 — micro-interactions (button press, toggle)
526
- duration.normal // 200 — standard transitions (modal, drawer)
527
- duration.slow // 320 — large layout shifts
528
- duration.slower // 480 — onboarding, emphasis animations
989
+ duration.instant; // 0 — immediate state swap, no transition
990
+ duration.fast; // 120 — micro-interactions (button press, toggle)
991
+ duration.normal; // 200 — standard transitions (modal, drawer)
992
+ duration.slow; // 320 — large layout shifts
993
+ duration.slower; // 480 — onboarding, emphasis animations
529
994
 
530
995
  // Bezier easing curves
531
- easing.standard // general purpose
532
- easing.decelerate // elements entering the screen
533
- easing.accelerate // elements leaving the screen
534
- easing.emphasized // expressive, attention-drawing transitions
996
+ easing.standard; // general purpose
997
+ easing.decelerate; // elements entering the screen
998
+ easing.accelerate; // elements leaving the screen
999
+ easing.emphasized; // expressive, attention-drawing transitions
535
1000
 
536
1001
  // Reanimated spring presets
537
- spring.default // balanced bounce
538
- spring.gentle // smooth, no overshoot
539
- spring.snappy // fast with slight bounce
1002
+ spring.default; // balanced bounce
1003
+ spring.gentle; // smooth, no overshoot
1004
+ spring.snappy; // fast with slight bounce
540
1005
  ```
541
1006
 
542
1007
  Usage with Reanimated:
@@ -546,7 +1011,7 @@ import { withTiming, withSpring } from "react-native-reanimated";
546
1011
  import { duration, easing, spring } from "@sincpro/mobile-ui/tokens";
547
1012
 
548
1013
  const opacity = withTiming(1, { duration: duration.normal, easing: easing.decelerate });
549
- const scale = withSpring(1, spring.snappy);
1014
+ const scale = withSpring(1, spring.snappy);
550
1015
  ```
551
1016
 
552
1017
  ---
@@ -577,19 +1042,15 @@ The preset ships the full token class set (`bg-primary`, `text-text-secondary`,
577
1042
  ```js
578
1043
  module.exports = {
579
1044
  presets: [require("@sincpro/mobile-ui/tailwind.preset")],
580
- content: [
581
- "./App.tsx",
582
- "./src/**/*.{ts,tsx}",
583
- "./node_modules/@sincpro/mobile-ui/**/*.js",
584
- ],
1045
+ content: ["./App.tsx", "./src/**/*.{ts,tsx}", "./node_modules/@sincpro/mobile-ui/**/*.js"],
585
1046
  theme: {
586
1047
  extend: {
587
1048
  // Override font families to match loaded font names
588
1049
  fontFamily: {
589
- light: ["Inter_300Light"],
590
- regular: ["Inter_400Regular"],
591
- medium: ["Inter_500Medium"],
592
- semibold: ["Inter_600SemiBold"],
1050
+ light: ["Inter_300Light"],
1051
+ regular: ["Inter_400Regular"],
1052
+ medium: ["Inter_500Medium"],
1053
+ semibold: ["Inter_600SemiBold"],
593
1054
  extrabold: ["Inter_800ExtraBold"],
594
1055
  },
595
1056
  },
@@ -614,13 +1075,13 @@ import "@sincpro/mobile-ui/theme/globals.css";
614
1075
  Best tree-shaking; makes the component's category visible at the call site:
615
1076
 
616
1077
  ```tsx
617
- import { Display } from "@sincpro/mobile-ui/Display";
618
- import { Form } from "@sincpro/mobile-ui/Form";
619
- import { Feedback } from "@sincpro/mobile-ui/Feedback";
1078
+ import { Display } from "@sincpro/mobile-ui/Display";
1079
+ import { Form } from "@sincpro/mobile-ui/Form";
1080
+ import { Feedback } from "@sincpro/mobile-ui/Feedback";
620
1081
  import { Navigation } from "@sincpro/mobile-ui/Navigation";
621
- import { Dialog } from "@sincpro/mobile-ui/Dialog";
1082
+ import { Dialog } from "@sincpro/mobile-ui/Dialog";
622
1083
  import { Typography } from "@sincpro/mobile-ui/Typography";
623
- import { Actions } from "@sincpro/mobile-ui/Actions";
1084
+ import { Actions } from "@sincpro/mobile-ui/Actions";
624
1085
  ```
625
1086
 
626
1087
  ### Theme and tokens
@@ -636,18 +1097,18 @@ For components outside a namespace:
636
1097
 
637
1098
  ```tsx
638
1099
  import Sheet from "@sincpro/mobile-ui/Dialog/Dialog.Sheet";
639
- import { ConfirmationProvider } from "@sincpro/mobile-ui/Dialog";
640
- import { ToastProvider, useToast } from "@sincpro/mobile-ui/Feedback";
641
- import { Wizard, useWizard } from "@sincpro/mobile-ui/views/Wizard";
1100
+ import { ConfirmationProvider } from "@sincpro/mobile-ui/Dialog";
1101
+ import { ToastProvider, useToast } from "@sincpro/mobile-ui/Feedback";
1102
+ import { Wizard, useWizard } from "@sincpro/mobile-ui/views/Wizard";
642
1103
  ```
643
1104
 
644
1105
  ### Layout primitives
645
1106
 
646
1107
  ```tsx
647
- import { Box, Row, Stack, Spacer } from "@sincpro/mobile-ui/primitives";
648
- import Container from "@sincpro/mobile-ui/layouts/Container";
649
- import PlainLayout from "@sincpro/mobile-ui/layouts/PlainLayout";
650
- import TabNavigatorLayout from "@sincpro/mobile-ui/layouts/TabNavigatorLayout";
1108
+ import { Box, Row, Stack, Spacer } from "@sincpro/mobile-ui/primitives";
1109
+ import Container from "@sincpro/mobile-ui/layouts/Container";
1110
+ import PlainLayout from "@sincpro/mobile-ui/layouts/PlainLayout";
1111
+ import TabNavigatorLayout from "@sincpro/mobile-ui/layouts/TabNavigatorLayout";
651
1112
  ```
652
1113
 
653
1114
  ---
@@ -663,26 +1124,35 @@ make storybook-android # expo run:android
663
1124
 
664
1125
  Stories are organized into four groups:
665
1126
 
666
- | Group | Contents |
667
- |-------|----------|
668
- | **Foundations** | Tokens, typography scale, color palette, motion |
669
- | **Components** | All compound component namespaces |
670
- | **Patterns** | Composed examples (list screen, form screen, auth flow) |
671
- | **Legacy** | Deprecated components kept for backwards compatibility |
1127
+ | Group | Contents |
1128
+ | --------------- | ------------------------------------------------------- |
1129
+ | **Foundations** | Tokens, typography scale, color palette, motion |
1130
+ | **Components** | All compound component namespaces |
1131
+ | **Patterns** | Composed examples (list screen, form screen, auth flow) |
1132
+ | **Legacy** | Deprecated components kept for backwards compatibility |
672
1133
 
673
1134
  Stories live in `.rnstorybook/stories/**/*.stories.tsx`. The Storybook harness is excluded from the published package (`files: ["dist", ...]`) and never lands in consumer bundles.
674
1135
 
1136
+ UX is a cross-cutting quality, not another catalog category. Component states live with
1137
+ their component (`Actions/Button`, `Data Display/CopyableText`, `Inputs/Select`, `Feedback`);
1138
+ task compositions live under named `Patterns/Forms`, `Patterns/Records`,
1139
+ `Patterns/Collections`, `Patterns/Views` and `Patterns/Profiles`.
1140
+ The former `Patterns/UX`, top-level `Views` and `Templates` story IDs changed;
1141
+ package exports did not. The taxonomy is regression-tested.
1142
+ Stories that own a full-screen scroll set `parameters.ownsScroll = true`; the canvas does
1143
+ not wrap them in a second vertical scroll. Other canvases use keyboard-aware scrolling.
1144
+
675
1145
  ---
676
1146
 
677
1147
  ## Development
678
1148
 
679
- | Command | What it does |
680
- |---------|-------------|
681
- | `make build` | `tsc` + `tsc-alias` → `dist/` (JS + type declarations) |
682
- | `make format` | Auto-fix: ESLint + Prettier + type-check |
683
- | `make verify-format` | CI gate — runs `format`, fails if any file changed |
684
- | `make storybook` | Start Expo dev server for Storybook |
685
- | `make publish` | Bump version + `npm publish` |
1149
+ | Command | What it does |
1150
+ | -------------------- | ------------------------------------------------------ |
1151
+ | `make build` | `tsc` + `tsc-alias` → `dist/` (JS + type declarations) |
1152
+ | `make format` | Auto-fix: ESLint + Prettier + type-check |
1153
+ | `make verify-format` | CI gate — runs `format`, fails if any file changed |
1154
+ | `make storybook` | Start Expo dev server for Storybook |
1155
+ | `make publish` | Bump version + `npm publish` |
686
1156
 
687
1157
  After building, copy the dist to consumers in the local monorepo (no symlinks setup):
688
1158