@hzblj/kara-components 0.9.0 → 0.11.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 (281) hide show
  1. package/CHANGELOG.md +72 -28
  2. package/README.md +8 -8
  3. package/lib/module/atoms/activity-indicator/activity-indicator.android.js +6 -1
  4. package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -1
  5. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +2 -0
  6. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -1
  7. package/lib/module/atoms/avatar/avatar.js +5 -0
  8. package/lib/module/atoms/avatar/avatar.js.map +1 -1
  9. package/lib/module/atoms/button/button-tokens.js +2 -2
  10. package/lib/module/atoms/button/button-tokens.js.map +1 -1
  11. package/lib/module/atoms/button/button.android.js +6 -2
  12. package/lib/module/atoms/button/button.android.js.map +1 -1
  13. package/lib/module/atoms/button/button.ios.js +7 -2
  14. package/lib/module/atoms/button/button.ios.js.map +1 -1
  15. package/lib/module/atoms/card/card.android.js +5 -3
  16. package/lib/module/atoms/card/card.android.js.map +1 -1
  17. package/lib/module/atoms/card/card.ios.js +7 -3
  18. package/lib/module/atoms/card/card.ios.js.map +1 -1
  19. package/lib/module/atoms/checkbox/checkbox.android.js +1 -1
  20. package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
  21. package/lib/module/atoms/color-picker/color-picker.android.js +32 -5
  22. package/lib/module/atoms/color-picker/color-picker.android.js.map +1 -1
  23. package/lib/module/atoms/fab/fab.android.js +9 -1
  24. package/lib/module/atoms/fab/fab.android.js.map +1 -1
  25. package/lib/module/atoms/fab/fab.ios.js +9 -3
  26. package/lib/module/atoms/fab/fab.ios.js.map +1 -1
  27. package/lib/module/atoms/glass-view/glass-view.android.js +1 -1
  28. package/lib/module/atoms/glass-view/glass-view.android.js.map +1 -1
  29. package/lib/module/atoms/h-stack/h-stack.ios.js +5 -3
  30. package/lib/module/atoms/h-stack/h-stack.ios.js.map +1 -1
  31. package/lib/module/atoms/icon-button/icon-button.android.js +9 -0
  32. package/lib/module/atoms/icon-button/icon-button.android.js.map +1 -1
  33. package/lib/module/atoms/icon-button/icon-button.ios.js +26 -6
  34. package/lib/module/atoms/icon-button/icon-button.ios.js.map +1 -1
  35. package/lib/module/atoms/image/image.js +4 -4
  36. package/lib/module/atoms/image/image.js.map +1 -1
  37. package/lib/module/atoms/pressable/pressable.js +52 -28
  38. package/lib/module/atoms/pressable/pressable.js.map +1 -1
  39. package/lib/module/atoms/progress/progress.android.js +7 -0
  40. package/lib/module/atoms/progress/progress.android.js.map +1 -1
  41. package/lib/module/atoms/progress/progress.ios.js +6 -1
  42. package/lib/module/atoms/progress/progress.ios.js.map +1 -1
  43. package/lib/module/atoms/segmented-control/segmented-control.android.js +1 -1
  44. package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
  45. package/lib/module/atoms/segmented-control/segmented-control.ios.js +1 -1
  46. package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
  47. package/lib/module/atoms/skeleton/skeleton.js +20 -6
  48. package/lib/module/atoms/skeleton/skeleton.js.map +1 -1
  49. package/lib/module/atoms/surface/surface.android.js +5 -3
  50. package/lib/module/atoms/surface/surface.android.js.map +1 -1
  51. package/lib/module/atoms/surface/surface.ios.js +7 -3
  52. package/lib/module/atoms/surface/surface.ios.js.map +1 -1
  53. package/lib/module/atoms/switch/switch.android.js +1 -1
  54. package/lib/module/atoms/switch/switch.android.js.map +1 -1
  55. package/lib/module/atoms/text/text.android.js +5 -3
  56. package/lib/module/atoms/text/text.android.js.map +1 -1
  57. package/lib/module/atoms/text/text.ios.js +6 -4
  58. package/lib/module/atoms/text/text.ios.js.map +1 -1
  59. package/lib/module/atoms/v-stack/v-stack.ios.js +5 -3
  60. package/lib/module/atoms/v-stack/v-stack.ios.js.map +1 -1
  61. package/lib/module/atoms/z-stack/z-stack.ios.js +5 -3
  62. package/lib/module/atoms/z-stack/z-stack.ios.js.map +1 -1
  63. package/lib/module/glyph/glyph.android.js +2 -0
  64. package/lib/module/glyph/glyph.android.js.map +1 -1
  65. package/lib/module/glyph/glyph.ios.js +5 -2
  66. package/lib/module/glyph/glyph.ios.js.map +1 -1
  67. package/lib/module/molecules/collapsible/collapsible.android.js +18 -8
  68. package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
  69. package/lib/module/molecules/labeled-content/labeled-content.js +4 -1
  70. package/lib/module/molecules/labeled-content/labeled-content.js.map +1 -1
  71. package/lib/module/molecules/link/link.js +4 -1
  72. package/lib/module/molecules/link/link.js.map +1 -1
  73. package/lib/module/molecules/list-item/list-item.js +4 -1
  74. package/lib/module/molecules/list-item/list-item.js.map +1 -1
  75. package/lib/module/molecules/navigation-bar/navigation-bar.android.js +1 -1
  76. package/lib/module/molecules/navigation-bar/navigation-bar.android.js.map +1 -1
  77. package/lib/module/molecules/navigation-bar/navigation-bar.ios.js +1 -1
  78. package/lib/module/molecules/navigation-bar/navigation-bar.ios.js.map +1 -1
  79. package/lib/module/molecules/rn-list-item/rn-list-item.js +10 -2
  80. package/lib/module/molecules/rn-list-item/rn-list-item.js.map +1 -1
  81. package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js +3 -4
  82. package/lib/module/molecules/scroll-edge-container/scroll-edge-container.android.js.map +1 -1
  83. package/lib/module/molecules/stepper/stepper.ios.js +6 -2
  84. package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
  85. package/lib/module/motion.js +19 -0
  86. package/lib/module/motion.js.map +1 -0
  87. package/lib/module/organisms/fab-menu/fab-menu.android.js +8 -5
  88. package/lib/module/organisms/fab-menu/fab-menu.android.js.map +1 -1
  89. package/lib/module/organisms/fab-menu/fab-menu.ios.js +5 -2
  90. package/lib/module/organisms/fab-menu/fab-menu.ios.js.map +1 -1
  91. package/lib/module/organisms/snackbar/snackbar.js +142 -17
  92. package/lib/module/organisms/snackbar/snackbar.js.map +1 -1
  93. package/lib/module/organisms/swipe-actions/swipe-actions.android.js +1 -1
  94. package/lib/module/organisms/swipe-actions/swipe-actions.android.js.map +1 -1
  95. package/lib/module/use-stack-modifiers.js +10 -8
  96. package/lib/module/use-stack-modifiers.js.map +1 -1
  97. package/lib/typescript/atoms/activity-indicator/activity-indicator.types.d.ts +3 -1
  98. package/lib/typescript/atoms/avatar/avatar.types.d.ts +7 -2
  99. package/lib/typescript/atoms/badge/badge.types.d.ts +1 -1
  100. package/lib/typescript/atoms/blur-view/blur-view.types.d.ts +6 -6
  101. package/lib/typescript/atoms/button/button-tokens.d.ts +1 -1
  102. package/lib/typescript/atoms/button/button.types.d.ts +9 -7
  103. package/lib/typescript/atoms/card/card.types.d.ts +3 -3
  104. package/lib/typescript/atoms/checkbox/checkbox.types.d.ts +2 -2
  105. package/lib/typescript/atoms/chip/chip.types.d.ts +1 -1
  106. package/lib/typescript/atoms/color-picker/color-picker.types.d.ts +4 -3
  107. package/lib/typescript/atoms/date-picker/date-picker.types.d.ts +4 -4
  108. package/lib/typescript/atoms/divider/divider.types.d.ts +2 -2
  109. package/lib/typescript/atoms/fab/fab.types.d.ts +10 -3
  110. package/lib/typescript/atoms/glass-view/glass-view.types.d.ts +5 -5
  111. package/lib/typescript/atoms/h-stack/h-stack.types.d.ts +8 -5
  112. package/lib/typescript/atoms/icon/icon.types.d.ts +4 -4
  113. package/lib/typescript/atoms/icon-button/icon-button.types.d.ts +14 -3
  114. package/lib/typescript/atoms/image/image.d.ts +2 -2
  115. package/lib/typescript/atoms/island/island.types.d.ts +4 -4
  116. package/lib/typescript/atoms/list/list.types.d.ts +5 -5
  117. package/lib/typescript/atoms/picker/picker.types.d.ts +1 -1
  118. package/lib/typescript/atoms/pressable/pressable.types.d.ts +23 -7
  119. package/lib/typescript/atoms/progress/progress.types.d.ts +3 -1
  120. package/lib/typescript/atoms/rn-flat-list/rn-flat-list.types.d.ts +2 -2
  121. package/lib/typescript/atoms/rn-icon/rn-icon.types.d.ts +1 -1
  122. package/lib/typescript/atoms/rn-scroll-view/rn-scroll-view.types.d.ts +2 -2
  123. package/lib/typescript/atoms/rn-section-list/rn-section-list.types.d.ts +2 -2
  124. package/lib/typescript/atoms/rn-text/rn-text.types.d.ts +2 -2
  125. package/lib/typescript/atoms/rn-view/rn-view.types.d.ts +3 -3
  126. package/lib/typescript/atoms/scroll-view/scroll-view.types.d.ts +3 -3
  127. package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +3 -3
  128. package/lib/typescript/atoms/skeleton/skeleton.types.d.ts +2 -1
  129. package/lib/typescript/atoms/spacer/spacer.types.d.ts +1 -1
  130. package/lib/typescript/atoms/surface/surface.types.d.ts +3 -3
  131. package/lib/typescript/atoms/switch/switch.types.d.ts +1 -1
  132. package/lib/typescript/atoms/text/text.types.d.ts +3 -3
  133. package/lib/typescript/atoms/text-field/text-field.types.d.ts +4 -4
  134. package/lib/typescript/atoms/v-stack/v-stack.types.d.ts +4 -4
  135. package/lib/typescript/atoms/z-stack/z-stack.types.d.ts +4 -4
  136. package/lib/typescript/glyph/glyph-button.types.d.ts +1 -1
  137. package/lib/typescript/glyph/glyph.types.d.ts +3 -1
  138. package/lib/typescript/molecules/collapsible/collapsible.types.d.ts +1 -1
  139. package/lib/typescript/molecules/empty-state/empty-state.types.d.ts +1 -1
  140. package/lib/typescript/molecules/label/label.types.d.ts +1 -1
  141. package/lib/typescript/molecules/labeled-content/labeled-content.types.d.ts +6 -4
  142. package/lib/typescript/molecules/link/link.types.d.ts +2 -1
  143. package/lib/typescript/molecules/list-item/list-item.types.d.ts +6 -4
  144. package/lib/typescript/molecules/navigation-bar/navigation-bar.types.d.ts +8 -8
  145. package/lib/typescript/molecules/pager/pager.types.d.ts +2 -2
  146. package/lib/typescript/molecules/radio-group/radio-group.types.d.ts +2 -2
  147. package/lib/typescript/molecules/rn-list-item/rn-list-item.types.d.ts +1 -1
  148. package/lib/typescript/molecules/rn-tooltip/rn-tooltip.types.d.ts +7 -7
  149. package/lib/typescript/molecules/scroll-edge-container/scroll-edge-container.types.d.ts +3 -3
  150. package/lib/typescript/molecules/search-bar/search-bar.types.d.ts +2 -2
  151. package/lib/typescript/molecules/stepper/stepper.ios.d.ts +1 -1
  152. package/lib/typescript/molecules/stepper/stepper.types.d.ts +2 -2
  153. package/lib/typescript/molecules/tooltip/tooltip.types.d.ts +6 -6
  154. package/lib/typescript/motion.d.ts +8 -0
  155. package/lib/typescript/organisms/action-sheet/action-sheet.types.d.ts +3 -3
  156. package/lib/typescript/organisms/alert/alert.types.d.ts +3 -3
  157. package/lib/typescript/organisms/context-menu/context-menu.types.d.ts +9 -9
  158. package/lib/typescript/organisms/fab-menu/fab-menu.types.d.ts +10 -5
  159. package/lib/typescript/organisms/form/form.types.d.ts +1 -1
  160. package/lib/typescript/organisms/menu/menu.types.d.ts +2 -2
  161. package/lib/typescript/organisms/sheet/sheet.types.d.ts +12 -12
  162. package/lib/typescript/organisms/snackbar/snackbar.types.d.ts +39 -6
  163. package/lib/typescript/organisms/swipe-actions/swipe-actions.types.d.ts +5 -5
  164. package/lib/typescript/templates/screen/screen.types.d.ts +2 -2
  165. package/lib/typescript/use-stack-modifiers.d.ts +6 -5
  166. package/package.json +4 -4
  167. package/src/atoms/activity-indicator/activity-indicator.android.tsx +7 -1
  168. package/src/atoms/activity-indicator/activity-indicator.ios.tsx +9 -1
  169. package/src/atoms/activity-indicator/activity-indicator.types.ts +3 -1
  170. package/src/atoms/avatar/avatar.tsx +5 -1
  171. package/src/atoms/avatar/avatar.types.ts +7 -2
  172. package/src/atoms/badge/badge.types.ts +1 -1
  173. package/src/atoms/blur-view/blur-view.types.ts +6 -6
  174. package/src/atoms/button/button-tokens.ts +2 -2
  175. package/src/atoms/button/button.android.tsx +5 -3
  176. package/src/atoms/button/button.ios.tsx +10 -2
  177. package/src/atoms/button/button.types.ts +9 -7
  178. package/src/atoms/card/card.android.tsx +6 -4
  179. package/src/atoms/card/card.ios.tsx +7 -3
  180. package/src/atoms/card/card.types.ts +9 -8
  181. package/src/atoms/checkbox/checkbox.android.tsx +1 -1
  182. package/src/atoms/checkbox/checkbox.types.ts +2 -2
  183. package/src/atoms/chip/chip.types.ts +1 -1
  184. package/src/atoms/color-picker/color-picker.android.tsx +26 -6
  185. package/src/atoms/color-picker/color-picker.types.ts +4 -3
  186. package/src/atoms/date-picker/date-picker.types.ts +4 -4
  187. package/src/atoms/divider/divider.types.ts +2 -2
  188. package/src/atoms/fab/fab.android.tsx +5 -3
  189. package/src/atoms/fab/fab.ios.tsx +8 -4
  190. package/src/atoms/fab/fab.types.ts +10 -3
  191. package/src/atoms/glass-view/glass-view.android.tsx +1 -1
  192. package/src/atoms/glass-view/glass-view.types.ts +5 -5
  193. package/src/atoms/h-stack/h-stack.ios.tsx +6 -4
  194. package/src/atoms/h-stack/h-stack.types.ts +18 -14
  195. package/src/atoms/icon/icon.types.ts +4 -4
  196. package/src/atoms/icon-button/icon-button.android.tsx +16 -3
  197. package/src/atoms/icon-button/icon-button.ios.tsx +24 -5
  198. package/src/atoms/icon-button/icon-button.types.ts +14 -3
  199. package/src/atoms/image/image.tsx +3 -3
  200. package/src/atoms/island/island.types.ts +4 -4
  201. package/src/atoms/list/list.types.ts +5 -5
  202. package/src/atoms/picker/picker.types.ts +1 -1
  203. package/src/atoms/pressable/pressable.tsx +50 -21
  204. package/src/atoms/pressable/pressable.types.ts +35 -17
  205. package/src/atoms/progress/progress.android.tsx +5 -3
  206. package/src/atoms/progress/progress.ios.tsx +5 -2
  207. package/src/atoms/progress/progress.types.ts +3 -1
  208. package/src/atoms/rn-flat-list/rn-flat-list.types.ts +2 -2
  209. package/src/atoms/rn-icon/rn-icon.types.ts +1 -1
  210. package/src/atoms/rn-scroll-view/rn-scroll-view.types.ts +2 -2
  211. package/src/atoms/rn-section-list/rn-section-list.types.ts +2 -2
  212. package/src/atoms/rn-text/rn-text.types.ts +2 -2
  213. package/src/atoms/rn-view/rn-view.types.ts +3 -3
  214. package/src/atoms/scroll-view/scroll-view.types.ts +3 -3
  215. package/src/atoms/segmented-control/segmented-control.android.tsx +1 -1
  216. package/src/atoms/segmented-control/segmented-control.ios.tsx +1 -1
  217. package/src/atoms/segmented-control/segmented-control.types.ts +3 -3
  218. package/src/atoms/skeleton/skeleton.tsx +18 -4
  219. package/src/atoms/skeleton/skeleton.types.ts +2 -1
  220. package/src/atoms/spacer/spacer.types.ts +1 -1
  221. package/src/atoms/surface/surface.android.tsx +6 -4
  222. package/src/atoms/surface/surface.ios.tsx +7 -3
  223. package/src/atoms/surface/surface.types.ts +11 -10
  224. package/src/atoms/switch/switch.android.tsx +1 -1
  225. package/src/atoms/switch/switch.types.ts +1 -1
  226. package/src/atoms/text/text.android.tsx +6 -4
  227. package/src/atoms/text/text.ios.tsx +6 -4
  228. package/src/atoms/text/text.types.ts +24 -23
  229. package/src/atoms/text-field/text-field.types.ts +4 -4
  230. package/src/atoms/v-stack/v-stack.ios.tsx +6 -4
  231. package/src/atoms/v-stack/v-stack.types.ts +18 -17
  232. package/src/atoms/z-stack/z-stack.ios.tsx +6 -4
  233. package/src/atoms/z-stack/z-stack.types.ts +16 -15
  234. package/src/glyph/glyph-button.types.ts +1 -1
  235. package/src/glyph/glyph.android.tsx +14 -2
  236. package/src/glyph/glyph.ios.tsx +13 -3
  237. package/src/glyph/glyph.types.ts +3 -1
  238. package/src/molecules/collapsible/collapsible.android.tsx +27 -7
  239. package/src/molecules/collapsible/collapsible.types.ts +1 -1
  240. package/src/molecules/empty-state/empty-state.types.ts +1 -1
  241. package/src/molecules/label/label.types.ts +1 -1
  242. package/src/molecules/labeled-content/labeled-content.tsx +12 -1
  243. package/src/molecules/labeled-content/labeled-content.types.ts +6 -4
  244. package/src/molecules/link/link.tsx +2 -1
  245. package/src/molecules/link/link.types.ts +2 -1
  246. package/src/molecules/list-item/list-item.tsx +20 -2
  247. package/src/molecules/list-item/list-item.types.ts +6 -4
  248. package/src/molecules/navigation-bar/navigation-bar.android.tsx +1 -1
  249. package/src/molecules/navigation-bar/navigation-bar.ios.tsx +1 -1
  250. package/src/molecules/navigation-bar/navigation-bar.types.ts +8 -8
  251. package/src/molecules/pager/pager.types.ts +2 -2
  252. package/src/molecules/radio-group/radio-group.types.ts +2 -2
  253. package/src/molecules/rn-list-item/rn-list-item.tsx +18 -3
  254. package/src/molecules/rn-list-item/rn-list-item.types.ts +1 -1
  255. package/src/molecules/rn-tooltip/rn-tooltip.types.ts +7 -7
  256. package/src/molecules/scroll-edge-container/scroll-edge-container.android.tsx +3 -2
  257. package/src/molecules/scroll-edge-container/scroll-edge-container.types.ts +3 -3
  258. package/src/molecules/search-bar/search-bar.types.ts +2 -2
  259. package/src/molecules/stepper/stepper.ios.tsx +8 -2
  260. package/src/molecules/stepper/stepper.types.ts +2 -2
  261. package/src/molecules/tooltip/tooltip.types.ts +6 -6
  262. package/src/motion.ts +13 -0
  263. package/src/organisms/action-sheet/action-sheet.types.ts +3 -3
  264. package/src/organisms/alert/alert.types.ts +3 -3
  265. package/src/organisms/context-menu/context-menu.types.ts +9 -9
  266. package/src/organisms/fab-menu/fab-menu.android.tsx +9 -5
  267. package/src/organisms/fab-menu/fab-menu.ios.tsx +6 -2
  268. package/src/organisms/fab-menu/fab-menu.types.ts +10 -5
  269. package/src/organisms/form/form.types.ts +1 -1
  270. package/src/organisms/menu/menu.types.ts +2 -2
  271. package/src/organisms/sheet/sheet.types.ts +12 -12
  272. package/src/organisms/snackbar/snackbar.tsx +143 -19
  273. package/src/organisms/snackbar/snackbar.types.ts +42 -6
  274. package/src/organisms/swipe-actions/swipe-actions.android.tsx +1 -1
  275. package/src/organisms/swipe-actions/swipe-actions.types.ts +5 -5
  276. package/src/templates/screen/screen.types.ts +2 -2
  277. package/src/use-stack-modifiers.ts +20 -7
  278. package/lib/module/platform-support.js +0 -19
  279. package/lib/module/platform-support.js.map +0 -1
  280. package/lib/typescript/platform-support.d.ts +0 -8
  281. package/src/platform-support.ts +0 -20
@@ -1,13 +1,13 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { MenuAction } from '../menu';
3
3
  /**
4
- * A list of actions to pick one from: SwiftUI's confirmation dialog on iOS, a `Sheet` of Material rows — icon and label
5
- * — under the title on Android. A native atom — it renders inside `<Screen>` or `<Island>`.
4
+ * A list of actions to pick one from: SwiftUI's confirmation dialog on iOS, a `Sheet` of Material rows (icon and label)
5
+ * under the title on Android. A native atom: it renders inside `<Screen>` or `<Island>`.
6
6
  */
7
7
  export type ActionSheetProps = {
8
8
  /** The choices, top to bottom. Picking one closes the sheet. */
9
9
  actions: MenuAction[];
10
- /** The sheet closed — after a pick too — so turn `visible` off. */
10
+ /** The sheet closed (after a pick too), so turn `visible` off. */
11
11
  onDismiss: () => void;
12
12
  /** The heading above the actions. Android always shows it; on iOS the system decides, and may hide it. */
13
13
  title: string;
@@ -1,18 +1,18 @@
1
1
  import type { ReactNode } from 'react';
2
2
  /**
3
3
  * A modal alert with a confirm button and Cancel: SwiftUI's alert on iOS, Material's `AlertDialog` on Android. A native
4
- * atom — it renders inside `<Screen>` or `<Island>`.
4
+ * atom: it renders inside `<Screen>` or `<Island>`.
5
5
  */
6
6
  export type AlertProps = {
7
7
  /** The confirm button's label. `OK` by default. */
8
8
  confirmLabel?: string;
9
- /** Marks the confirm button destructive — the system's destructive role on iOS, the destructive colour on Android. */
9
+ /** Marks the confirm button destructive: the system's destructive role on iOS, the destructive colour on Android. */
10
10
  destructive?: boolean;
11
11
  /** The body text, under the title. */
12
12
  message?: string;
13
13
  /** The confirm button was pressed. `onDismiss` follows, as it does for every way the alert closes. */
14
14
  onConfirm?: () => void;
15
- /** The alert closed — either button, or on Android a tap outside or back — so turn `visible` off. */
15
+ /** The alert closed (either button, or on Android a tap outside or back), so turn `visible` off. */
16
16
  onDismiss: () => void;
17
17
  /** The alert's heading. */
18
18
  title: string;
@@ -2,7 +2,7 @@ import type { ResponsiveViewSx } from '@hzblj/kara-sx';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { MenuAction } from '../menu';
4
4
  /**
5
- * A context menu on a React Native subtree — a card in a list, say — opened by a long press. iOS: a
5
+ * A context menu on a React Native subtree (a card in a list, say), opened by a long press. iOS: a
6
6
  * `UIContextMenuInteraction`, with an optional React Native `preview` lifted while the menu is open and a callback for
7
7
  * a tap on it. Android, where Material has no lifted preview: a ripple and the long-press haptic, then a Material
8
8
  * dropdown where the finger is, with the trigger highlighted while it is open. The trigger is ordinary React Native layout, so it
@@ -13,33 +13,33 @@ export type ContextMenuProps = ResponsiveViewSx & {
13
13
  actions: MenuAction[];
14
14
  /** The trigger, laid out as usual. */
15
15
  children: ReactNode;
16
- /** iOS: the user tapped the lifted preview, committing to it — open the detail the card stands for. */
16
+ /** iOS: the user tapped the lifted preview, committing to it. Open the detail the card stands for. */
17
17
  onPreviewPress?: () => void;
18
18
  /**
19
- * iOS: lifted in place of the trigger while the menu is open — a larger version of the card, say. A snapshot of the
19
+ * iOS: lifted in place of the trigger while the menu is open (a larger version of the card, say). A snapshot of the
20
20
  * trigger when unset. Mounted only once a finger has rested on the trigger, so a list of cards does not pay for
21
21
  * every preview up front.
22
22
  */
23
23
  preview?: ReactNode;
24
24
  /**
25
25
  * iOS: how the preview leaves once tapped, before `onPreviewPress`. `zoom`: its photo grows straight into the pushed
26
- * screen, the way the App Store expands a preview — needs a `ZoomTransition.Source` (`@hzblj/kara-ui/router`) in the
27
- * trigger, a `preview` and iOS 18, and shrinks back like `dismiss` otherwise. `pop`: UIKit's default, the preview
26
+ * screen, the way the App Store expands a preview. It needs a `ZoomTransition.Source` (`@hzblj/kara-ui/router`) in
27
+ * the trigger, a `preview` and iOS 18, and shrinks back like `dismiss` otherwise. `pop`: UIKit's default, the preview
28
28
  * flies out. `dismiss`: it shrinks back onto the trigger first. Default: `zoom` when the trigger holds a
29
29
  * `ZoomTransition.Source`, `pop` otherwise.
30
30
  */
31
31
  previewTransition?: 'dismiss' | 'pop' | 'zoom';
32
- /** Test identifier — on the native view around the trigger on iOS, on the trigger's `Pressable` on Android. */
32
+ /** Test identifier: on the native view around the trigger on iOS, on the trigger's `Pressable` on Android. */
33
33
  testID?: string;
34
34
  };
35
- /** A region of a `ContextMenu`'s trigger that keeps its own long press — see `ContextMenu.Exclusion`. */
35
+ /** A region of a `ContextMenu`'s trigger that keeps its own long press (see `ContextMenu.Exclusion`). */
36
36
  export type ContextMenuExclusionProps = {
37
- /** The region — a button inside the card, say. */
37
+ /** The region: a button inside the card, say. */
38
38
  children: ReactNode;
39
39
  };
40
40
  export type ContextMenuComponent = ((props: ContextMenuProps) => ReactNode) & {
41
41
  /**
42
- * A region of the trigger that must not open the menu — a button inside the card. On iOS the interaction covers the
42
+ * A region of the trigger that must not open the menu: a button inside the card. On iOS the interaction covers the
43
43
  * whole trigger, so a long press on that button would otherwise open the card's menu.
44
44
  */
45
45
  Exclusion: (props: ContextMenuExclusionProps) => ReactNode;
@@ -2,16 +2,21 @@ import type { ReactNode } from 'react';
2
2
  import type { IconName } from '../../atoms/icon';
3
3
  import type { MenuAction } from '../menu';
4
4
  /**
5
- * A floating action button that unfolds into its actions. Android: Material 3 Expressive's `FloatingActionButtonMenu`
6
- * — the button turns into a close and the actions rise above it as labelled pills. iOS: the same button as the label
7
- * of a SwiftUI `Menu`. A native atom — it renders inside `<Screen>` or `<Island>`.
5
+ * A floating action button that unfolds into its actions. Android: Material 3 Expressive's `FloatingActionButtonMenu`,
6
+ * where the button turns into a close and the actions rise above it as labelled pills. iOS: the same button as the
7
+ * label of a SwiftUI `Menu`. A native atom: it renders inside `<Screen>` or `<Island>`.
8
8
  */
9
9
  export type FabMenuProps = {
10
+ /**
11
+ * What VoiceOver and TalkBack say for the button, since an icon has no words of its own: "Create". Required for that
12
+ * reason.
13
+ */
14
+ accessibilityLabel: string;
10
15
  /** The actions the button unfolds into. */
11
16
  actions: MenuAction[];
12
- /** The closed button's icon — one of the app's. A plus by default. */
17
+ /** The closed button's icon, one of the app's. A plus by default. */
13
18
  icon?: IconName;
14
- /** Test identifier, forwarded to the SwiftUI `Menu`. iOS only — Android ignores it. */
19
+ /** Test identifier, forwarded to the SwiftUI `Menu`. iOS only; Android ignores it. */
15
20
  testID?: string;
16
21
  };
17
22
  export type FabMenuComponent = (props: FabMenuProps) => ReactNode;
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  /**
3
- * A settings form — SwiftUI `Form` with its grouped sections, Material cards on Android. On iOS it scrolls like a
3
+ * A settings form: SwiftUI `Form` with its grouped sections, Material cards on Android. On iOS it scrolls like a
4
4
  * `List`, so it needs a height from outside: a `Screen`, or an `Island` with `matchContents={false}` and a height. In
5
5
  * an island that sizes itself to its content it has no height and shows nothing.
6
6
  */
@@ -3,7 +3,7 @@ import type { IconName } from '../../atoms/icon';
3
3
  /** One action of a `Menu`, an `ActionSheet`, a `ContextMenu` or a `FabMenu`. */
4
4
  export type MenuAction = {
5
5
  /**
6
- * Draws the action in the destructive colour — the system's destructive role on iOS. `FabMenu` ignores it on
6
+ * Draws the action in the destructive colour (the system's destructive role on iOS). `FabMenu` ignores it on
7
7
  * Android.
8
8
  */
9
9
  destructive?: boolean;
@@ -19,7 +19,7 @@ export type MenuAction = {
19
19
  };
20
20
  /**
21
21
  * A text button that opens a list of actions: a SwiftUI `Menu` on iOS, a Material dropdown anchored to it on Android.
22
- * A native atom — it renders inside `<Screen>` or `<Island>`.
22
+ * A native atom: it renders inside `<Screen>` or `<Island>`.
23
23
  */
24
24
  export type MenuProps = {
25
25
  /** The menu's actions, top to bottom. */
@@ -7,12 +7,12 @@ import type { NavigationBarItem, NavigationBarSearch } from '../../molecules/nav
7
7
  export type SheetDetent = 'auto' | number;
8
8
  /**
9
9
  * `center` is iOS's own, `leading` Material's. `large` puts the title above the content in a large face, and once
10
- * that scrolls under the bar the bar's own title fades in — centred on iOS, leading on Android.
10
+ * that scrolls under the bar the bar's own title fades in (centred on iOS, leading on Android).
11
11
  */
12
12
  export type SheetTitleStyle = 'center' | 'large' | 'leading';
13
13
  /**
14
14
  * The sheet's header: a `NavigationBar` with the title and items, and anything else that should stay put above the
15
- * content (`children` — a segmented control, a search hint). On iOS 26 a scrollable sheet's content scrolls under it
15
+ * content (`children`: a segmented control, a search hint). On iOS 26 a scrollable sheet's content scrolls under it
16
16
  * with the system's scroll edge effect; elsewhere it sits above the content.
17
17
  */
18
18
  export type SheetHeaderProps = {
@@ -24,24 +24,24 @@ export type SheetHeaderProps = {
24
24
  leadingItems?: NavigationBarItem[];
25
25
  /** A search field in place of the title (iOS). */
26
26
  search?: NavigationBarSearch;
27
- /** The title — in the bar, or above the content with a `large` `titleStyle`. */
27
+ /** The title: in the bar, or above the content with a `large` `titleStyle`. */
28
28
  title?: string;
29
29
  /** `center` by default. */
30
30
  titleStyle?: SheetTitleStyle;
31
- /** In screen order, toward the trailing edge — the close item, by convention. */
31
+ /** In screen order, toward the trailing edge: the close item, by convention. */
32
32
  trailingItems?: NavigationBarItem[];
33
33
  };
34
34
  /** Imperative control of a `Sheet`, through its `ref`. */
35
35
  export type SheetRef = {
36
36
  /** Dismisses it without calling `onDismiss`, which reports the user's dismissals only. */
37
37
  dismiss: () => Promise<void>;
38
- /** Presents at a detent — the first by default. */
38
+ /** Presents at a detent, the first by default. */
39
39
  present: (detentIndex?: number) => Promise<void>;
40
40
  /** Moves to another detent. */
41
41
  resize: (detentIndex: number) => Promise<void>;
42
42
  };
43
43
  /**
44
- * A native sheet — `UISheetPresentationController` on iOS, Material's bottom sheet on Android — with an optional
44
+ * A native sheet (`UISheetPresentationController` on iOS, Material's bottom sheet on Android) with an optional
45
45
  * native header (`<Sheet.Header>` as a direct child). The React Native tier: it needs no host, and must not sit inside
46
46
  * one; native atoms in its content go in an `Island`.
47
47
  */
@@ -50,7 +50,7 @@ export type SheetProps = {
50
50
  bg?: ColorToken;
51
51
  /** The content. A `<Sheet.Header>` among the direct children becomes the native header instead. */
52
52
  children: ReactNode;
53
- /** Where the sheet can rest, smallest first. `['auto']` — fit the content — by default. */
53
+ /** Where the sheet can rest, smallest first. `['auto']` (fit the content) by default. */
54
54
  detents?: SheetDetent[];
55
55
  /** Dims what is behind. On by default. */
56
56
  dimmed?: boolean;
@@ -62,9 +62,9 @@ export type SheetProps = {
62
62
  footer?: ReactElement;
63
63
  /** The drag handle. On by default. */
64
64
  grabber?: boolean;
65
- /** The user dismissed it — `visible` going false does not call this. */
65
+ /** The user dismissed it; `visible` going false does not call this. */
66
66
  onDismiss: () => void;
67
- /** The sheet moved to another detent — its index in `detents`. */
67
+ /** The sheet moved to another detent: its index in `detents`. */
68
68
  onDetentChange?: (detentIndex: number) => void;
69
69
  /** The sheet finished presenting. */
70
70
  onPresent?: () => void;
@@ -72,7 +72,7 @@ export type SheetProps = {
72
72
  ref?: Ref<SheetRef>;
73
73
  /**
74
74
  * The content is a scroll view (`<Sheet.ScrollView>`) the sheet drives: dragging past its top resizes the sheet,
75
- * and a header scrolls with the iOS 26 edge effect. Off by default — set it with a scrolling body.
75
+ * and a header scrolls with the iOS 26 edge effect. Off by default; set it with a scrolling body.
76
76
  */
77
77
  scrollable?: boolean;
78
78
  /** Test identifier, forwarded to the native sheet. */
@@ -85,7 +85,7 @@ export type SheetScrollHandler = (event: NativeSyntheticEvent<NativeScrollEvent>
85
85
  export type SheetScrollContextValue = {
86
86
  /** Render it first in the content. `null` unless the header's `titleStyle` is `large`. */
87
87
  largeTitle: ReactNode;
88
- /** Forward the scroll view's scroll events to it — it tracks the large title. Unset when there is none. */
88
+ /** Forward the scroll view's scroll events to it; it tracks the large title. Unset when there is none. */
89
89
  onScroll?: SheetScrollHandler;
90
90
  /** The room an overlaid header takes, to add to the content's top padding. `0` where the header sits above it. */
91
91
  topInset: number;
@@ -97,7 +97,7 @@ export type SheetScrollContextValue = {
97
97
  export type SheetScrollViewProps = Pick<ScrollViewProps, 'keyboardShouldPersistTaps'> & {
98
98
  /** The scrolling content, after the large title if there is one. */
99
99
  children: ReactNode;
100
- /** Styles the scrolling content — padding and gap belong here, as with `RnScrollView`. */
100
+ /** Styles the scrolling content: padding and gap belong here, as with `RnScrollView`. */
101
101
  content?: ResponsiveViewSx;
102
102
  /** Called on every scroll event, after the sheet's own listener. */
103
103
  onScroll?: SheetScrollHandler;
@@ -1,16 +1,49 @@
1
- import type { ReactNode } from 'react';
1
+ import type { SpaceSize } from '@hzblj/kara-theme';
2
+ import type { ComponentProps, ReactNode } from 'react';
3
+ import type Animated from 'react-native-reanimated';
4
+ type LayoutAnimation = ComponentProps<typeof Animated.View>['entering'];
5
+ /** How it comes and goes. */
6
+ export type SnackbarAnimation = 'fade' | 'scale' | 'slide';
7
+ /** How long it stays: Material's short (4 s) and long (10 s), never, or a number of milliseconds. */
8
+ export type SnackbarDuration = 'indefinite' | 'long' | 'short' | number;
9
+ /** The edge of its parent it sits on. */
10
+ export type SnackbarPosition = 'bottom' | 'top';
2
11
  /**
3
- * A short message laid over the bottom of its parent, with an optional action. The React Native tier, drawn the same on
4
- * both platforms: it needs no host, and it never hides itself.
12
+ * A short message laid over an edge of its parent, with an optional action at its trailing end. The React Native tier,
13
+ * drawn the same on both platforms (iOS has no snackbar of its own), so it needs no host. Material's options are all
14
+ * here: a duration, a dismiss button, the action on a line of its own. It is announced to VoiceOver and TalkBack when
15
+ * it appears.
5
16
  */
6
17
  export type SnackbarProps = {
7
- /** The action button's text, on the trailing side. No button without it. */
18
+ /** The action button's text, at the trailing end. No button without it. */
8
19
  actionLabel?: string;
20
+ /** Puts the action under the message, at its trailing end: Material's layout for a long message or action. */
21
+ actionOnNewLine?: boolean;
22
+ /**
23
+ * How it comes and goes. `scale` is Material's own (a fade and a scale from 80 %), `slide` rises from its edge as it
24
+ * fades in, `fade` only fades. `scale` on Android and `slide` on iOS by default. With motion reduced, it fades.
25
+ */
26
+ animation?: SnackbarAnimation;
27
+ /** A close button at the trailing end, Material's dismiss action; it calls `onDismiss`. */
28
+ dismissible?: boolean;
29
+ /** How long it stays up before `onDismiss` is called. `indefinite` by default: it never hides itself. */
30
+ duration?: SnackbarDuration;
31
+ /** A Reanimated entering animation of your own, e.g. `FadeInDown.springify()`. It wins over `animation`. */
32
+ entering?: LayoutAnimation;
33
+ /** A Reanimated exiting animation of your own. It wins over `animation`. */
34
+ exiting?: LayoutAnimation;
9
35
  /** The message. */
10
36
  message: string;
11
- /** The action was pressed. The snackbar stays up — turn `visible` off to hide it. */
37
+ /** Between the snackbar and its edge, a space token. `s24` by default; raise it above a tab bar. */
38
+ offset?: SpaceSize;
39
+ /** The action was pressed. The snackbar stays up; turn `visible` off to hide it. */
12
40
  onAction?: () => void;
13
- /** Shows the snackbar while true, fading it in and out. It sets no timer of its own. */
41
+ /** It asks to go: its `duration` ran out, or its dismiss button was pressed. Turn `visible` off to hide it. */
42
+ onDismiss?: () => void;
43
+ /** The edge of its parent it sits on. `bottom` by default, Material's place for it. */
44
+ position?: SnackbarPosition;
45
+ /** Shows the snackbar while true, animating it in and out. */
14
46
  visible: boolean;
15
47
  };
16
48
  export type SnackbarComponent = (props: SnackbarProps) => ReactNode;
49
+ export {};
@@ -5,25 +5,25 @@ import type { IconName } from '../../atoms/icon';
5
5
  export type SwipeAction = {
6
6
  /** Fills the button with `fill.destructive.primary` unless `tint` says otherwise. */
7
7
  destructive?: boolean;
8
- /** One of the app's icons, on the button — a round one on iOS 26, as in Mail, a filled stadium on Android. */
8
+ /** One of the app's icons, on the button: a round one on iOS 26, as in Mail, a filled stadium on Android. */
9
9
  icon: IconName;
10
10
  /**
11
- * The action's name, written under the button — before iOS 26 a row of the usual height shows the icon alone and
11
+ * The action's name, written under the button; before iOS 26 a row of the usual height shows the icon alone and
12
12
  * keeps the label for VoiceOver. Actions are keyed by it, so keep labels unique within one row.
13
13
  */
14
14
  label: string;
15
15
  /** Called when the button is pressed. The row closes by itself. */
16
16
  onPress: () => void;
17
17
  /**
18
- * The button's fill — `fill.destructive.primary` for a destructive action, `fill.accent.primary` otherwise. The icon
18
+ * The button's fill: `fill.destructive.primary` for a destructive action, `fill.accent.primary` otherwise. The icon
19
19
  * on it is white, so pick a fill that holds white in both appearances.
20
20
  */
21
21
  tint?: ColorToken;
22
22
  };
23
23
  /**
24
- * A list row with buttons behind it, uncovered by a swipe — SwiftUI's `swipeActions` on iOS, and on Android, where
24
+ * A list row with buttons behind it, uncovered by a swipe. SwiftUI's `swipeActions` on iOS, and on Android, where
25
25
  * Compose has none, kara's own: the row slides on an anchored drag and the buttons show in the strip it leaves. One
26
- * row is open at a time on both; a tap on an open row closes it. A native organism — it belongs inside a `List`.
26
+ * row is open at a time on both; a tap on an open row closes it. A native organism: it belongs inside a `List`.
27
27
  */
28
28
  export type SwipeActionsProps = {
29
29
  /**
@@ -1,7 +1,7 @@
1
1
  import type { Sx } from '@hzblj/kara-sx';
2
2
  import type { ReactNode } from 'react';
3
3
  /**
4
- * The full-bleed native host a screen starts with — a SwiftUI or Compose `Host` filling its parent, so native atoms
4
+ * The full-bleed native host a screen starts with: a SwiftUI or Compose `Host` filling its parent, so native atoms
5
5
  * render anywhere inside it. The background runs to the edges; the content keeps clear of the status bar where the
6
6
  * screen covers it (no navigation bar above), on Android as SwiftUI's safe area does on iOS.
7
7
  */
@@ -12,7 +12,7 @@ export type ScreenProps = {
12
12
  children?: ReactNode;
13
13
  /** Keeps the content where it is when the keyboard opens, instead of avoiding it. Android reads it once, on mount. */
14
14
  ignoreKeyboardInset?: boolean;
15
- /** Test identifier — on the host view on iOS, on the view around it on Android. */
15
+ /** Test identifier, on the host view on iOS and on the view around it on Android. */
16
16
  testID?: string;
17
17
  };
18
18
  export type ScreenComponent = (props: ScreenProps) => ReactNode;
@@ -1,8 +1,9 @@
1
1
  import { type ModifierConfig } from '@expo/ui/jetpack-compose/modifiers';
2
- import { type ResponsiveSx } from '@hzblj/kara-sx';
2
+ import { type AccessibilityProps, type ResponsiveSx } from '@hzblj/kara-sx';
3
3
  /**
4
- * A Compose stack's modifiers in the order a pressable one needs: its frame, shape and background, then the press,
5
- * then its padding. Compose clips a `clickable` to what comes after it, so with the padding first the ripple would stop
6
- * at the padding and a row pressed by its edge would light up only in the middle.
4
+ * A Compose stack's modifiers in the order a pressable one needs: its frame, shape and background, then the press, its
5
+ * semantics, then its padding. Compose clips a `clickable` to what comes after it, so with the padding first the ripple
6
+ * would stop at the padding and a row pressed by its edge would light up only in the middle. The semantics come after
7
+ * the click, because a label that replaces the children's clears whatever semantics are inside it.
7
8
  */
8
- export declare const useStackModifiers: (sx: ResponsiveSx, onPress?: () => void, testID?: string) => ModifierConfig[];
9
+ export declare const useStackModifiers: (props: ResponsiveSx & AccessibilityProps, onPress?: () => void, testID?: string) => ModifierConfig[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzblj/kara-components",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "The atoms, molecules, organisms and templates of @hzblj/kara-ui — real SwiftUI and Jetpack Compose views through @expo/ui.",
5
5
  "author": "Jan Blazej",
6
6
  "license": "MIT",
@@ -59,9 +59,9 @@
59
59
  ]
60
60
  },
61
61
  "dependencies": {
62
- "@hzblj/kara-native": "^0.9.0",
63
- "@hzblj/kara-sx": "^0.9.0",
64
- "@hzblj/kara-theme": "^0.9.0",
62
+ "@hzblj/kara-native": "^0.11.0",
63
+ "@hzblj/kara-sx": "^0.11.0",
64
+ "@hzblj/kara-theme": "^0.11.0",
65
65
  "sf-symbols-typescript": "^2.2.0"
66
66
  },
67
67
  "peerDependencies": {
@@ -1,5 +1,6 @@
1
1
  import {Host, LoadingIndicator} from '@expo/ui/jetpack-compose'
2
2
  import {fillMaxSize, testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
3
+ import {useAccessibilityModifiers} from '@hzblj/kara-sx'
3
4
  import {useAppTheme} from '@hzblj/kara-theme'
4
5
  import {useMemo} from 'react'
5
6
  import {StyleSheet} from 'react-native'
@@ -17,12 +18,17 @@ const styles = StyleSheet.create({
17
18
  * anywhere and takes its place in the first frame.
18
19
  */
19
20
  export const ActivityIndicator: ActivityIndicatorComponent = ({
21
+ accessibilityLabel,
20
22
  color = 'fill.accent.primary',
21
23
  size = 'small',
22
24
  testID,
23
25
  }: ActivityIndicatorProps) => {
24
26
  const theme = useAppTheme()
25
- const modifiers = useMemo(() => (testID ? [fillMaxSize(), testIdModifier(testID)] : [fillMaxSize()]), [testID])
27
+ const accessibilityModifiers = useAccessibilityModifiers({accessibilityLabel})
28
+ const modifiers = useMemo(
29
+ () => [fillMaxSize(), ...(testID ? [testIdModifier(testID)] : []), ...accessibilityModifiers],
30
+ [accessibilityModifiers, testID]
31
+ )
26
32
 
27
33
  return (
28
34
  <Host colorScheme={theme.dark ? 'dark' : 'light'} seedColor={theme.materialSeed} style={styles[size]}>
@@ -5,11 +5,19 @@ import type {ActivityIndicatorComponent, ActivityIndicatorProps} from './activit
5
5
 
6
6
  /** React Native's spinner, which on iOS is UIKit's own `UIActivityIndicatorView`. */
7
7
  export const ActivityIndicator: ActivityIndicatorComponent = ({
8
+ accessibilityLabel,
8
9
  color = 'fill.accent.primary',
9
10
  size = 'small',
10
11
  testID,
11
12
  }: ActivityIndicatorProps) => {
12
13
  const theme = useAppTheme()
13
14
 
14
- return <RNActivityIndicator color={theme.color(color)} size={size} testID={testID} />
15
+ return (
16
+ <RNActivityIndicator
17
+ accessibilityLabel={accessibilityLabel}
18
+ color={theme.color(color)}
19
+ size={size}
20
+ testID={testID}
21
+ />
22
+ )
15
23
  }
@@ -4,11 +4,13 @@ import type {ReactNode} from 'react'
4
4
  export type ActivityIndicatorSize = 'large' | 'small'
5
5
 
6
6
  /**
7
- * That something is loading, for as long as it takes — UIKit's spinner on iOS, Material 3's expressive
7
+ * That something is loading, for as long as it takes: UIKit's spinner on iOS, Material 3's expressive
8
8
  * `LoadingIndicator` on Android, its shapes morphing as it turns. A React Native view on iOS and a host of its own on
9
9
  * Android, so it renders anywhere. For how far along something is, use `Progress`.
10
10
  */
11
11
  export type ActivityIndicatorProps = {
12
+ /** What is loading, for VoiceOver and TalkBack: "Loading photos". Unset, the platform's own word ("In progress"). */
13
+ accessibilityLabel?: string
12
14
  /** Its colour. `fill.accent.primary` by default. */
13
15
  color?: ColorToken
14
16
  /** `small` sits in a row or a button, `large` holds a screen that is loading. `small` by default. */
@@ -17,11 +17,15 @@ const VARIANT = {
17
17
  sm: 'caption',
18
18
  } as const
19
19
 
20
- export const Avatar: AvatarComponent = ({initials, size = 'md', source, testID}: AvatarProps) => {
20
+ export const Avatar: AvatarComponent = ({accessibilityLabel, initials, size = 'md', source, testID}: AvatarProps) => {
21
21
  const diameter = DIAMETER[size]
22
22
 
23
23
  return (
24
24
  <Box
25
+ aria-hidden={accessibilityLabel === undefined}
26
+ accessibilityLabel={accessibilityLabel}
27
+ accessibilityRole={accessibilityLabel === undefined ? undefined : 'image'}
28
+ accessible={accessibilityLabel !== undefined}
25
29
  align="center"
26
30
  bg="fill.tertiary"
27
31
  borderRadius="full"
@@ -3,11 +3,16 @@ import type {ImageSourcePropType} from 'react-native'
3
3
 
4
4
  export type AvatarSize = 'lg' | 'md' | 'sm'
5
5
 
6
- /** A round avatar: the image in `source`, or `initials` without one. The React Native tier — it needs no host. */
6
+ /** A round avatar: the image in `source`, or `initials` without one. The React Native tier: it needs no host. */
7
7
  export type AvatarProps = {
8
+ /**
9
+ * Who it is, for VoiceOver and TalkBack: "Jan Blazej". Unset, the avatar is decorative and hidden from them, as one
10
+ * beside the person's name should be.
11
+ */
12
+ accessibilityLabel?: string
8
13
  /** Shown in place of the image when there is no `source`. */
9
14
  initials?: string
10
- /** The diameter — `sm` is `s24`, `md` `s40`, `lg` `s64`. `md` by default. */
15
+ /** The diameter: `sm` is `s24`, `md` `s40`, `lg` `s64`. `md` by default. */
11
16
  size?: AvatarSize
12
17
  /** The image, cropped to the circle. */
13
18
  source?: ImageSourcePropType
@@ -2,7 +2,7 @@ import type {ReactNode} from 'react'
2
2
 
3
3
  export type BadgeTone = 'accent' | 'destructive' | 'neutral' | 'success'
4
4
 
5
- /** A small status label in a capsule — a SwiftUI `HStack` in the tone's fill on iOS, Material's `Badge` on Android. */
5
+ /** A small status label in a capsule: a SwiftUI `HStack` in the tone's fill on iOS, Material's `Badge` on Android. */
6
6
  export type BadgeProps = {
7
7
  /** The text inside. */
8
8
  label: string
@@ -5,16 +5,16 @@ import type {ReactNode} from 'react'
5
5
  export type {BlurMaterial, BlurVibrancy} from '@hzblj/kara-native/swift-ui'
6
6
 
7
7
  /**
8
- * One of the system's materials behind React Native content — what sheets, bars, menus and HUDs are made of, in
8
+ * One of the system's materials (what sheets, bars, menus and HUDs are made of) behind React Native content, in
9
9
  * SwiftUI's five thicknesses. iOS: UIKit's `system*Material` blur, with optional vibrancy for the content. Android:
10
- * what lies behind, blurred and saturated (Android 12+), under a Material tonal surface as thick as the material —
11
- * `surface` at `ultraThin` up to `surfaceContainerHighest` at `ultraThick`; below Android 12 the surface alone. The
10
+ * what lies behind, blurred and saturated (Android 12+), under a Material tonal surface as thick as the material
11
+ * (`surface` at `ultraThin` up to `surfaceContainerHighest` at `ultraThick`); below Android 12 the surface alone. The
12
12
  * React Native tier: it needs no host, so it suits a list cell. Shape it with `borderRadius`; it clips its children.
13
13
  */
14
14
  export type BlurViewProps = ResponsiveViewSx & {
15
15
  /**
16
- * Pins the material to light or dark whatever the app's appearance — a dark HUD over a light screen. Unset, it follows
17
- * the app theme. Android draws the opposite appearance with Material's inverse surface.
16
+ * Pins the material to light or dark whatever the app's appearance (a dark HUD over a light screen). Unset, it
17
+ * follows the app theme. Android draws the opposite appearance with Material's inverse surface.
18
18
  */
19
19
  appearance?: 'dark' | 'light'
20
20
  /** React Native content, over the material. */
@@ -24,7 +24,7 @@ export type BlurViewProps = ResponsiveViewSx & {
24
24
  /** Test identifier, forwarded to the view. */
25
25
  testID?: string
26
26
  /**
27
- * iOS: draws the children vibrant at this level, the way iOS draws its own labels and fills on a material — their
27
+ * iOS: draws the children vibrant at this level, the way iOS draws its own labels and fills on a material. Their
28
28
  * colour becomes the material's vibrant one and their alpha the mask. Android has no vibrancy: the children keep
29
29
  * their colours there.
30
30
  */
@@ -9,7 +9,7 @@ type ButtonTokens = {
9
9
  }
10
10
 
11
11
  const TOKENS = {
12
- accent: {content: 'content.on.bg', fill: 'fill.accent.primary', shadow: 'btn.accent'},
12
+ accent: {content: 'content.on.accent', fill: 'fill.accent.primary', shadow: 'btn.accent'},
13
13
  destructive: {content: 'content.on.destructive', fill: 'fill.destructive.primary', shadow: 'btn.destructive'},
14
14
  glass: {content: 'content.primary', fill: 'btn.bg.glass.base', shadow: 'btn.glass'},
15
15
  secondary: {content: 'content.primary', fill: 'btn.bg.secondary.base', shadow: 'btn.secondary'},
@@ -81,4 +81,4 @@ const currentHex = (token: ColorToken, theme: Theme): unknown => {
81
81
  * content colour over a pale one (`fill.accent.base`) — the light container tokens pair with it in both appearances.
82
82
  */
83
83
  export const contentOnTint = (tint: ColorToken, theme: Theme): ColorToken =>
84
- (luminance(currentHex(tint, theme)) ?? 0) > 0.4 ? 'content.primary' : 'content.on.bg'
84
+ (luminance(currentHex(tint, theme)) ?? 0) > 0.4 ? 'content.primary' : 'content.on.accent'
@@ -1,6 +1,6 @@
1
1
  import {Button as ComposeButton, Text as ComposeText, OutlinedButton} from '@expo/ui/jetpack-compose'
2
2
  import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
3
- import {useSxModifiers} from '@hzblj/kara-sx'
3
+ import {useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
4
4
  import {useAppTheme} from '@hzblj/kara-theme'
5
5
  import {useMemo} from 'react'
6
6
  import {useHostGuard} from '../../host-context'
@@ -16,6 +16,7 @@ import {buttonTokens, contentOnTint} from './button-tokens'
16
16
  * colour, as a prominent style does on iOS. `buttonStyle` and `controlSize` are SwiftUI's alone.
17
17
  */
18
18
  export const Button: ButtonComponent = ({
19
+ accessibilityHint,
19
20
  disabled = false,
20
21
  label,
21
22
  onPress,
@@ -28,9 +29,10 @@ export const Button: ButtonComponent = ({
28
29
  const theme = useAppTheme()
29
30
  const tokens = buttonTokens(variant, disabled, theme)
30
31
  const sxModifiers = useSxModifiers({wide})
32
+ const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint}, accessibilityHint !== undefined)
31
33
  const modifiers = useMemo(
32
- () => (testID ? [...sxModifiers, testIdModifier(testID)] : sxModifiers),
33
- [sxModifiers, testID]
34
+ () => [...sxModifiers, ...(testID ? [testIdModifier(testID)] : []), ...accessibilityModifiers],
35
+ [accessibilityModifiers, sxModifiers, testID]
34
36
  )
35
37
  const style = theme.typography.body
36
38
  const outlined = variant === 'glass' && !tint
@@ -6,6 +6,7 @@ import {
6
6
  glassEffect as glassEffectModifier,
7
7
  tint as tintModifier,
8
8
  } from '@expo/ui/swift-ui/modifiers'
9
+ import {useAccessibilityModifiers} from '@hzblj/kara-sx'
9
10
  import {type ColorToken, useAppTheme} from '@hzblj/kara-theme'
10
11
  import {useMemo} from 'react'
11
12
  import {useHostGuard} from '../../host-context'
@@ -29,6 +30,7 @@ const TINT_BY_VARIANT = {
29
30
  } as const satisfies Record<ButtonVariant, ColorToken | undefined>
30
31
 
31
32
  export const Button: ButtonComponent = ({
33
+ accessibilityHint,
32
34
  buttonStyle,
33
35
  controlSize = 'large',
34
36
  disabled = false,
@@ -59,8 +61,14 @@ export const Button: ButtonComponent = ({
59
61
  // A prominent style — and tinted glass — is coloured by the tint, so a caller's own tint picks the label on it.
60
62
  const content = tint && filled && !disabled ? contentOnTint(tint, theme) : tokens.content
61
63
 
64
+ const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint})
65
+
62
66
  const modifiers = useMemo(() => {
63
- const list = [buttonStyleModifier(glassTint ? 'plain' : style), controlSizeModifier(controlSize)]
67
+ const list = [
68
+ buttonStyleModifier(glassTint ? 'plain' : style),
69
+ controlSizeModifier(controlSize),
70
+ ...accessibilityModifiers,
71
+ ]
64
72
 
65
73
  if (glassTint) {
66
74
  list.push(
@@ -75,7 +83,7 @@ export const Button: ButtonComponent = ({
75
83
  }
76
84
 
77
85
  return list
78
- }, [controlSize, disabled, glassTint, resolvedTint, style, theme])
86
+ }, [accessibilityModifiers, controlSize, disabled, glassTint, resolvedTint, style, theme])
79
87
 
80
88
  return (
81
89
  <SwiftUIButton modifiers={modifiers} onPress={onPress} testID={testID}>