@oxyhq/bloom 0.26.1 → 0.28.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 (300) hide show
  1. package/lib/commonjs/animated-check/AnimatedCheck.js +162 -0
  2. package/lib/commonjs/animated-check/AnimatedCheck.js.map +1 -0
  3. package/lib/commonjs/animated-check/index.js +13 -0
  4. package/lib/commonjs/animated-check/index.js.map +1 -0
  5. package/lib/commonjs/avatar/Avatar.js +67 -96
  6. package/lib/commonjs/avatar/Avatar.js.map +1 -1
  7. package/lib/commonjs/avatar/AvatarRing.js +183 -0
  8. package/lib/commonjs/avatar/AvatarRing.js.map +1 -0
  9. package/lib/commonjs/avatar/index.js.map +1 -1
  10. package/lib/commonjs/avatar/squircle-path.js +14 -0
  11. package/lib/commonjs/avatar/squircle-path.js.map +1 -0
  12. package/lib/commonjs/avatar/svg-module.js +15 -0
  13. package/lib/commonjs/avatar/svg-module.js.map +1 -0
  14. package/lib/commonjs/dialog/placement.js +9 -7
  15. package/lib/commonjs/dialog/placement.js.map +1 -1
  16. package/lib/commonjs/fill/index.js.map +1 -1
  17. package/lib/commonjs/hooks/index.js +20 -0
  18. package/lib/commonjs/hooks/index.js.map +1 -1
  19. package/lib/commonjs/hooks/useGutters.js +106 -0
  20. package/lib/commonjs/hooks/useGutters.js.map +1 -0
  21. package/lib/commonjs/hooks/useHaptics.js +69 -0
  22. package/lib/commonjs/hooks/useHaptics.js.map +1 -0
  23. package/lib/commonjs/hooks/useInteractionState.js +8 -2
  24. package/lib/commonjs/hooks/useInteractionState.js.map +1 -1
  25. package/lib/commonjs/index.js +155 -63
  26. package/lib/commonjs/index.js.map +1 -1
  27. package/lib/commonjs/index.web.js +155 -63
  28. package/lib/commonjs/index.web.js.map +1 -1
  29. package/lib/commonjs/media-inset-border/MediaInsetBorder.js +50 -0
  30. package/lib/commonjs/media-inset-border/MediaInsetBorder.js.map +1 -0
  31. package/lib/commonjs/media-inset-border/index.js +13 -0
  32. package/lib/commonjs/media-inset-border/index.js.map +1 -0
  33. package/lib/commonjs/motion/ScreenTransition.js +46 -0
  34. package/lib/commonjs/motion/ScreenTransition.js.map +1 -0
  35. package/lib/commonjs/motion/index.js +32 -0
  36. package/lib/commonjs/motion/index.js.map +1 -0
  37. package/lib/commonjs/motion/motion.js +93 -0
  38. package/lib/commonjs/motion/motion.js.map +1 -0
  39. package/lib/commonjs/pressable-scale/PressableScale.js +75 -0
  40. package/lib/commonjs/pressable-scale/PressableScale.js.map +1 -0
  41. package/lib/commonjs/pressable-scale/index.js +13 -0
  42. package/lib/commonjs/pressable-scale/index.js.map +1 -0
  43. package/lib/commonjs/pressable-with-hover/PressableWithHover.js +36 -0
  44. package/lib/commonjs/pressable-with-hover/PressableWithHover.js.map +1 -0
  45. package/lib/commonjs/pressable-with-hover/index.js +13 -0
  46. package/lib/commonjs/pressable-with-hover/index.js.map +1 -0
  47. package/lib/commonjs/styles/breakpoints.js +24 -0
  48. package/lib/commonjs/styles/breakpoints.js.map +1 -0
  49. package/lib/commonjs/styles/index.js +7 -0
  50. package/lib/commonjs/styles/index.js.map +1 -1
  51. package/lib/commonjs/subtle-hover/SubtleHover.js +69 -0
  52. package/lib/commonjs/subtle-hover/SubtleHover.js.map +1 -0
  53. package/lib/commonjs/subtle-hover/index.js +13 -0
  54. package/lib/commonjs/subtle-hover/index.js.map +1 -0
  55. package/lib/commonjs/theme/build-theme.js +2 -0
  56. package/lib/commonjs/theme/build-theme.js.map +1 -1
  57. package/lib/commonjs/theme/gradients.js +39 -0
  58. package/lib/commonjs/theme/gradients.js.map +1 -0
  59. package/lib/commonjs/theme/index.js +7 -0
  60. package/lib/commonjs/theme/index.js.map +1 -1
  61. package/lib/module/animated-check/AnimatedCheck.js +157 -0
  62. package/lib/module/animated-check/AnimatedCheck.js.map +1 -0
  63. package/lib/module/animated-check/index.js +4 -0
  64. package/lib/module/animated-check/index.js.map +1 -0
  65. package/lib/module/avatar/Avatar.js +66 -96
  66. package/lib/module/avatar/Avatar.js.map +1 -1
  67. package/lib/module/avatar/AvatarRing.js +177 -0
  68. package/lib/module/avatar/AvatarRing.js.map +1 -0
  69. package/lib/module/avatar/index.js.map +1 -1
  70. package/lib/module/avatar/squircle-path.js +10 -0
  71. package/lib/module/avatar/squircle-path.js.map +1 -0
  72. package/lib/module/avatar/svg-module.js +11 -0
  73. package/lib/module/avatar/svg-module.js.map +1 -0
  74. package/lib/module/dialog/placement.js +9 -7
  75. package/lib/module/dialog/placement.js.map +1 -1
  76. package/lib/module/fill/index.js.map +1 -1
  77. package/lib/module/hooks/index.js +2 -0
  78. package/lib/module/hooks/index.js.map +1 -1
  79. package/lib/module/hooks/useGutters.js +103 -0
  80. package/lib/module/hooks/useGutters.js.map +1 -0
  81. package/lib/module/hooks/useHaptics.js +65 -0
  82. package/lib/module/hooks/useHaptics.js.map +1 -0
  83. package/lib/module/hooks/useInteractionState.js +10 -3
  84. package/lib/module/hooks/useInteractionState.js.map +1 -1
  85. package/lib/module/index.js +10 -1
  86. package/lib/module/index.js.map +1 -1
  87. package/lib/module/index.web.js +10 -1
  88. package/lib/module/index.web.js.map +1 -1
  89. package/lib/module/media-inset-border/MediaInsetBorder.js +47 -0
  90. package/lib/module/media-inset-border/MediaInsetBorder.js.map +1 -0
  91. package/lib/module/media-inset-border/index.js +4 -0
  92. package/lib/module/media-inset-border/index.js.map +1 -0
  93. package/lib/module/motion/ScreenTransition.js +40 -0
  94. package/lib/module/motion/ScreenTransition.js.map +1 -0
  95. package/lib/module/motion/index.js +5 -0
  96. package/lib/module/motion/index.js.map +1 -0
  97. package/lib/module/motion/motion.js +87 -0
  98. package/lib/module/motion/motion.js.map +1 -0
  99. package/lib/module/pressable-scale/PressableScale.js +72 -0
  100. package/lib/module/pressable-scale/PressableScale.js.map +1 -0
  101. package/lib/module/pressable-scale/index.js +4 -0
  102. package/lib/module/pressable-scale/index.js.map +1 -0
  103. package/lib/module/pressable-with-hover/PressableWithHover.js +31 -0
  104. package/lib/module/pressable-with-hover/PressableWithHover.js.map +1 -0
  105. package/lib/module/pressable-with-hover/index.js +4 -0
  106. package/lib/module/pressable-with-hover/index.js.map +1 -0
  107. package/lib/module/styles/breakpoints.js +20 -0
  108. package/lib/module/styles/breakpoints.js.map +1 -0
  109. package/lib/module/styles/index.js +1 -0
  110. package/lib/module/styles/index.js.map +1 -1
  111. package/lib/module/subtle-hover/SubtleHover.js +66 -0
  112. package/lib/module/subtle-hover/SubtleHover.js.map +1 -0
  113. package/lib/module/subtle-hover/index.js +4 -0
  114. package/lib/module/subtle-hover/index.js.map +1 -0
  115. package/lib/module/theme/build-theme.js +2 -0
  116. package/lib/module/theme/build-theme.js.map +1 -1
  117. package/lib/module/theme/gradients.js +35 -0
  118. package/lib/module/theme/gradients.js.map +1 -0
  119. package/lib/module/theme/index.js +1 -0
  120. package/lib/module/theme/index.js.map +1 -1
  121. package/lib/typescript/commonjs/animated-check/AnimatedCheck.d.ts +18 -0
  122. package/lib/typescript/commonjs/animated-check/AnimatedCheck.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/animated-check/index.d.ts +3 -0
  124. package/lib/typescript/commonjs/animated-check/index.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/avatar/Avatar.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/avatar/AvatarRing.d.ts +27 -0
  127. package/lib/typescript/commonjs/avatar/AvatarRing.d.ts.map +1 -0
  128. package/lib/typescript/commonjs/avatar/index.d.ts +1 -1
  129. package/lib/typescript/commonjs/avatar/index.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/avatar/squircle-path.d.ts +8 -0
  131. package/lib/typescript/commonjs/avatar/squircle-path.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/avatar/svg-module.d.ts +8 -0
  133. package/lib/typescript/commonjs/avatar/svg-module.d.ts.map +1 -0
  134. package/lib/typescript/commonjs/avatar/types.d.ts +30 -0
  135. package/lib/typescript/commonjs/avatar/types.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/dialog/placement.d.ts +8 -7
  137. package/lib/typescript/commonjs/dialog/placement.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/fill/index.d.ts +2 -2
  139. package/lib/typescript/commonjs/fill/index.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/hooks/index.d.ts +4 -0
  141. package/lib/typescript/commonjs/hooks/index.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/hooks/useGutters.d.ts +32 -0
  143. package/lib/typescript/commonjs/hooks/useGutters.d.ts.map +1 -0
  144. package/lib/typescript/commonjs/hooks/useHaptics.d.ts +28 -0
  145. package/lib/typescript/commonjs/hooks/useHaptics.d.ts.map +1 -0
  146. package/lib/typescript/commonjs/hooks/useInteractionState.d.ts +6 -0
  147. package/lib/typescript/commonjs/hooks/useInteractionState.d.ts.map +1 -1
  148. package/lib/typescript/commonjs/index.d.ts +16 -0
  149. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/index.web.d.ts +16 -0
  151. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  152. package/lib/typescript/commonjs/media-inset-border/MediaInsetBorder.d.ts +26 -0
  153. package/lib/typescript/commonjs/media-inset-border/MediaInsetBorder.d.ts.map +1 -0
  154. package/lib/typescript/commonjs/media-inset-border/index.d.ts +3 -0
  155. package/lib/typescript/commonjs/media-inset-border/index.d.ts.map +1 -0
  156. package/lib/typescript/commonjs/motion/ScreenTransition.d.ts +33 -0
  157. package/lib/typescript/commonjs/motion/ScreenTransition.d.ts.map +1 -0
  158. package/lib/typescript/commonjs/motion/index.d.ts +4 -0
  159. package/lib/typescript/commonjs/motion/index.d.ts.map +1 -0
  160. package/lib/typescript/commonjs/motion/motion.d.ts +24 -0
  161. package/lib/typescript/commonjs/motion/motion.d.ts.map +1 -0
  162. package/lib/typescript/commonjs/pressable-scale/PressableScale.d.ts +21 -0
  163. package/lib/typescript/commonjs/pressable-scale/PressableScale.d.ts.map +1 -0
  164. package/lib/typescript/commonjs/pressable-scale/index.d.ts +3 -0
  165. package/lib/typescript/commonjs/pressable-scale/index.d.ts.map +1 -0
  166. package/lib/typescript/commonjs/pressable-with-hover/PressableWithHover.d.ts +18 -0
  167. package/lib/typescript/commonjs/pressable-with-hover/PressableWithHover.d.ts.map +1 -0
  168. package/lib/typescript/commonjs/pressable-with-hover/index.d.ts +3 -0
  169. package/lib/typescript/commonjs/pressable-with-hover/index.d.ts.map +1 -0
  170. package/lib/typescript/commonjs/styles/breakpoints.d.ts +18 -0
  171. package/lib/typescript/commonjs/styles/breakpoints.d.ts.map +1 -0
  172. package/lib/typescript/commonjs/styles/index.d.ts +2 -0
  173. package/lib/typescript/commonjs/styles/index.d.ts.map +1 -1
  174. package/lib/typescript/commonjs/subtle-hover/SubtleHover.d.ts +32 -0
  175. package/lib/typescript/commonjs/subtle-hover/SubtleHover.d.ts.map +1 -0
  176. package/lib/typescript/commonjs/subtle-hover/index.d.ts +3 -0
  177. package/lib/typescript/commonjs/subtle-hover/index.d.ts.map +1 -0
  178. package/lib/typescript/commonjs/theme/build-theme.d.ts.map +1 -1
  179. package/lib/typescript/commonjs/theme/gradients.d.ts +12 -0
  180. package/lib/typescript/commonjs/theme/gradients.d.ts.map +1 -0
  181. package/lib/typescript/commonjs/theme/index.d.ts +2 -1
  182. package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/theme/types.d.ts +11 -0
  184. package/lib/typescript/commonjs/theme/types.d.ts.map +1 -1
  185. package/lib/typescript/module/animated-check/AnimatedCheck.d.ts +18 -0
  186. package/lib/typescript/module/animated-check/AnimatedCheck.d.ts.map +1 -0
  187. package/lib/typescript/module/animated-check/index.d.ts +3 -0
  188. package/lib/typescript/module/animated-check/index.d.ts.map +1 -0
  189. package/lib/typescript/module/avatar/Avatar.d.ts.map +1 -1
  190. package/lib/typescript/module/avatar/AvatarRing.d.ts +27 -0
  191. package/lib/typescript/module/avatar/AvatarRing.d.ts.map +1 -0
  192. package/lib/typescript/module/avatar/index.d.ts +1 -1
  193. package/lib/typescript/module/avatar/index.d.ts.map +1 -1
  194. package/lib/typescript/module/avatar/squircle-path.d.ts +8 -0
  195. package/lib/typescript/module/avatar/squircle-path.d.ts.map +1 -0
  196. package/lib/typescript/module/avatar/svg-module.d.ts +8 -0
  197. package/lib/typescript/module/avatar/svg-module.d.ts.map +1 -0
  198. package/lib/typescript/module/avatar/types.d.ts +30 -0
  199. package/lib/typescript/module/avatar/types.d.ts.map +1 -1
  200. package/lib/typescript/module/dialog/placement.d.ts +8 -7
  201. package/lib/typescript/module/dialog/placement.d.ts.map +1 -1
  202. package/lib/typescript/module/fill/index.d.ts +2 -2
  203. package/lib/typescript/module/fill/index.d.ts.map +1 -1
  204. package/lib/typescript/module/hooks/index.d.ts +4 -0
  205. package/lib/typescript/module/hooks/index.d.ts.map +1 -1
  206. package/lib/typescript/module/hooks/useGutters.d.ts +32 -0
  207. package/lib/typescript/module/hooks/useGutters.d.ts.map +1 -0
  208. package/lib/typescript/module/hooks/useHaptics.d.ts +28 -0
  209. package/lib/typescript/module/hooks/useHaptics.d.ts.map +1 -0
  210. package/lib/typescript/module/hooks/useInteractionState.d.ts +6 -0
  211. package/lib/typescript/module/hooks/useInteractionState.d.ts.map +1 -1
  212. package/lib/typescript/module/index.d.ts +16 -0
  213. package/lib/typescript/module/index.d.ts.map +1 -1
  214. package/lib/typescript/module/index.web.d.ts +16 -0
  215. package/lib/typescript/module/index.web.d.ts.map +1 -1
  216. package/lib/typescript/module/media-inset-border/MediaInsetBorder.d.ts +26 -0
  217. package/lib/typescript/module/media-inset-border/MediaInsetBorder.d.ts.map +1 -0
  218. package/lib/typescript/module/media-inset-border/index.d.ts +3 -0
  219. package/lib/typescript/module/media-inset-border/index.d.ts.map +1 -0
  220. package/lib/typescript/module/motion/ScreenTransition.d.ts +33 -0
  221. package/lib/typescript/module/motion/ScreenTransition.d.ts.map +1 -0
  222. package/lib/typescript/module/motion/index.d.ts +4 -0
  223. package/lib/typescript/module/motion/index.d.ts.map +1 -0
  224. package/lib/typescript/module/motion/motion.d.ts +24 -0
  225. package/lib/typescript/module/motion/motion.d.ts.map +1 -0
  226. package/lib/typescript/module/pressable-scale/PressableScale.d.ts +21 -0
  227. package/lib/typescript/module/pressable-scale/PressableScale.d.ts.map +1 -0
  228. package/lib/typescript/module/pressable-scale/index.d.ts +3 -0
  229. package/lib/typescript/module/pressable-scale/index.d.ts.map +1 -0
  230. package/lib/typescript/module/pressable-with-hover/PressableWithHover.d.ts +18 -0
  231. package/lib/typescript/module/pressable-with-hover/PressableWithHover.d.ts.map +1 -0
  232. package/lib/typescript/module/pressable-with-hover/index.d.ts +3 -0
  233. package/lib/typescript/module/pressable-with-hover/index.d.ts.map +1 -0
  234. package/lib/typescript/module/styles/breakpoints.d.ts +18 -0
  235. package/lib/typescript/module/styles/breakpoints.d.ts.map +1 -0
  236. package/lib/typescript/module/styles/index.d.ts +2 -0
  237. package/lib/typescript/module/styles/index.d.ts.map +1 -1
  238. package/lib/typescript/module/subtle-hover/SubtleHover.d.ts +32 -0
  239. package/lib/typescript/module/subtle-hover/SubtleHover.d.ts.map +1 -0
  240. package/lib/typescript/module/subtle-hover/index.d.ts +3 -0
  241. package/lib/typescript/module/subtle-hover/index.d.ts.map +1 -0
  242. package/lib/typescript/module/theme/build-theme.d.ts.map +1 -1
  243. package/lib/typescript/module/theme/gradients.d.ts +12 -0
  244. package/lib/typescript/module/theme/gradients.d.ts.map +1 -0
  245. package/lib/typescript/module/theme/index.d.ts +2 -1
  246. package/lib/typescript/module/theme/index.d.ts.map +1 -1
  247. package/lib/typescript/module/theme/types.d.ts +11 -0
  248. package/lib/typescript/module/theme/types.d.ts.map +1 -1
  249. package/package.json +73 -1
  250. package/src/__tests__/AnimatedCheck.test.tsx +31 -0
  251. package/src/__tests__/MediaInsetBorder.test.tsx +36 -0
  252. package/src/__tests__/Motion.test.tsx +58 -0
  253. package/src/__tests__/PressableScale.test.tsx +29 -0
  254. package/src/__tests__/PressableWithHover.test.tsx +48 -0
  255. package/src/__tests__/SubtleHover.test.tsx +42 -0
  256. package/src/__tests__/theme-gradients.test.ts +31 -0
  257. package/src/__tests__/theme.test.ts +3 -0
  258. package/src/__tests__/useGutters.test.ts +58 -0
  259. package/src/__tests__/useHaptics.test.tsx +58 -0
  260. package/src/__tests__/useInteractionState.test.ts +27 -0
  261. package/src/animated-check/AnimatedCheck.stories.tsx +60 -0
  262. package/src/animated-check/AnimatedCheck.tsx +173 -0
  263. package/src/animated-check/index.ts +2 -0
  264. package/src/avatar/Avatar.stories.tsx +95 -0
  265. package/src/avatar/Avatar.tsx +61 -110
  266. package/src/avatar/AvatarRing.tsx +170 -0
  267. package/src/avatar/index.ts +6 -1
  268. package/src/avatar/squircle-path.ts +11 -0
  269. package/src/avatar/svg-module.ts +11 -0
  270. package/src/avatar/types.ts +32 -0
  271. package/src/dialog/placement.ts +10 -7
  272. package/src/fill/index.tsx +2 -2
  273. package/src/hooks/index.ts +4 -0
  274. package/src/hooks/useGutters.ts +99 -0
  275. package/src/hooks/useHaptics.tsx +69 -0
  276. package/src/hooks/useInteractionState.ts +8 -2
  277. package/src/index.ts +20 -0
  278. package/src/index.web.ts +20 -0
  279. package/src/media-inset-border/MediaInsetBorder.stories.tsx +60 -0
  280. package/src/media-inset-border/MediaInsetBorder.tsx +72 -0
  281. package/src/media-inset-border/index.ts +2 -0
  282. package/src/motion/Motion.stories.tsx +99 -0
  283. package/src/motion/ScreenTransition.tsx +71 -0
  284. package/src/motion/index.ts +3 -0
  285. package/src/motion/motion.ts +75 -0
  286. package/src/pressable-scale/PressableScale.stories.tsx +71 -0
  287. package/src/pressable-scale/PressableScale.tsx +103 -0
  288. package/src/pressable-scale/index.ts +2 -0
  289. package/src/pressable-with-hover/PressableWithHover.stories.tsx +43 -0
  290. package/src/pressable-with-hover/PressableWithHover.tsx +43 -0
  291. package/src/pressable-with-hover/index.ts +2 -0
  292. package/src/styles/breakpoints.ts +18 -0
  293. package/src/styles/index.ts +2 -0
  294. package/src/subtle-hover/SubtleHover.stories.tsx +72 -0
  295. package/src/subtle-hover/SubtleHover.tsx +90 -0
  296. package/src/subtle-hover/index.ts +2 -0
  297. package/src/theme/build-theme.ts +2 -0
  298. package/src/theme/gradients.ts +61 -0
  299. package/src/theme/index.ts +2 -1
  300. package/src/theme/types.ts +13 -0
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { PressableScale } from './index';
6
+
7
+ const meta: Meta<typeof PressableScale> = {
8
+ title: 'Components/PressableScale',
9
+ component: PressableScale,
10
+ argTypes: {
11
+ targetScale: { control: { type: 'number', min: 0.8, max: 1, step: 0.01 } },
12
+ },
13
+ };
14
+
15
+ export default meta;
16
+
17
+ type Story = StoryObj<typeof PressableScale>;
18
+
19
+ export const Basic: Story = {
20
+ render: (args) => (
21
+ <PressableScale
22
+ {...args}
23
+ accessibilityLabel="Press me"
24
+ contentContainerStyle={{
25
+ paddingVertical: 12,
26
+ paddingHorizontal: 20,
27
+ borderRadius: 12,
28
+ backgroundColor: '#111827',
29
+ }}
30
+ >
31
+ <Text style={{ color: '#fff', fontWeight: '600' }}>Press me</Text>
32
+ </PressableScale>
33
+ ),
34
+ };
35
+
36
+ export const StrongScale: Story = {
37
+ args: { targetScale: 0.9 },
38
+ render: (args) => (
39
+ <PressableScale
40
+ {...args}
41
+ accessibilityLabel="Press me harder"
42
+ contentContainerStyle={{
43
+ paddingVertical: 12,
44
+ paddingHorizontal: 20,
45
+ borderRadius: 12,
46
+ backgroundColor: '#4f46e5',
47
+ }}
48
+ >
49
+ <Text style={{ color: '#fff', fontWeight: '600' }}>Strong scale (0.9)</Text>
50
+ </PressableScale>
51
+ ),
52
+ };
53
+
54
+ export const Card: Story = {
55
+ render: (args) => (
56
+ <PressableScale {...args} accessibilityLabel="Open card">
57
+ <View
58
+ style={{
59
+ width: 220,
60
+ padding: 16,
61
+ borderRadius: 16,
62
+ backgroundColor: '#f3f4f6',
63
+ gap: 6,
64
+ }}
65
+ >
66
+ <Text style={{ fontWeight: '700' }}>Tappable card</Text>
67
+ <Text style={{ color: '#6b7280' }}>The whole card springs on press.</Text>
68
+ </View>
69
+ </PressableScale>
70
+ ),
71
+ };
@@ -0,0 +1,103 @@
1
+ import React, { forwardRef } from 'react';
2
+ import {
3
+ Platform,
4
+ Pressable,
5
+ type PressableProps,
6
+ type StyleProp,
7
+ type View,
8
+ type ViewStyle,
9
+ } from 'react-native';
10
+ import Animated, {
11
+ cancelAnimation,
12
+ useAnimatedStyle,
13
+ useReducedMotion,
14
+ useSharedValue,
15
+ withTiming,
16
+ } from 'react-native-reanimated';
17
+
18
+ /** Press-in / press-out timing, in ms. */
19
+ const DURATION = 100;
20
+
21
+ /**
22
+ * True on touch-capable web browsers (coarse pointer). The press-scale
23
+ * affordance only makes sense where a finger obscures the element — with a
24
+ * mouse it reads as jitter — so it is disabled on non-touch web, matching the
25
+ * native / web-touch behaviour.
26
+ */
27
+ const IS_WEB_TOUCH_DEVICE =
28
+ Platform.OS === 'web' &&
29
+ typeof window !== 'undefined' &&
30
+ typeof window.matchMedia === 'function' &&
31
+ window.matchMedia('(pointer: coarse)').matches;
32
+
33
+ const SCALE_SUPPORTED = Platform.OS !== 'web' || IS_WEB_TOUCH_DEVICE;
34
+
35
+ export interface PressableScaleProps
36
+ extends Omit<PressableProps, 'children' | 'style'> {
37
+ /** Scale applied while pressed. Defaults to `0.98`. */
38
+ targetScale?: number;
39
+ /** Style for the outer, full-size touch target (stays put while pressing). */
40
+ style?: StyleProp<ViewStyle>;
41
+ /** Style for the inner content wrapper that actually scales. */
42
+ contentContainerStyle?: StyleProp<ViewStyle>;
43
+ children?: React.ReactNode;
44
+ }
45
+
46
+ /**
47
+ * A drop-in {@link Pressable} whose content springs to a smaller scale on
48
+ * press-in and back on release. The outer pressable stays full size (so the
49
+ * touch target never shrinks) while an inner wrapper scales.
50
+ *
51
+ * Honours the OS "reduce motion" setting and disables the effect on non-touch
52
+ * web pointers.
53
+ */
54
+ export const PressableScale = forwardRef<View, PressableScaleProps>(
55
+ function PressableScale(
56
+ {
57
+ targetScale = 0.98,
58
+ style,
59
+ contentContainerStyle,
60
+ children,
61
+ onPressIn,
62
+ onPressOut,
63
+ ...rest
64
+ },
65
+ ref,
66
+ ) {
67
+ const reducedMotion = useReducedMotion();
68
+ const animate = SCALE_SUPPORTED && !reducedMotion;
69
+
70
+ const scale = useSharedValue(1);
71
+
72
+ const animatedStyle = useAnimatedStyle(() => ({
73
+ transform: [{ scale: scale.value }],
74
+ }));
75
+
76
+ return (
77
+ <Pressable
78
+ ref={ref}
79
+ accessibilityRole="button"
80
+ onPressIn={(e) => {
81
+ onPressIn?.(e);
82
+ if (!animate) return;
83
+ cancelAnimation(scale);
84
+ scale.value = withTiming(targetScale, { duration: DURATION });
85
+ }}
86
+ onPressOut={(e) => {
87
+ onPressOut?.(e);
88
+ if (!animate) return;
89
+ cancelAnimation(scale);
90
+ scale.value = withTiming(1, { duration: DURATION });
91
+ }}
92
+ style={style}
93
+ {...rest}
94
+ >
95
+ <Animated.View style={[animate ? animatedStyle : null, contentContainerStyle]}>
96
+ {children}
97
+ </Animated.View>
98
+ </Pressable>
99
+ );
100
+ },
101
+ );
102
+
103
+ PressableScale.displayName = 'PressableScale';
@@ -0,0 +1,2 @@
1
+ export { PressableScale } from './PressableScale';
2
+ export type { PressableScaleProps } from './PressableScale';
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { PressableWithHover } from './index';
6
+
7
+ const meta: Meta<typeof PressableWithHover> = {
8
+ title: 'Components/PressableWithHover',
9
+ component: PressableWithHover,
10
+ };
11
+
12
+ export default meta;
13
+
14
+ type Story = StoryObj<typeof PressableWithHover>;
15
+
16
+ export const Basic: Story = {
17
+ render: () => (
18
+ <PressableWithHover
19
+ accessibilityLabel="Hover me"
20
+ style={{
21
+ paddingVertical: 12,
22
+ paddingHorizontal: 20,
23
+ borderRadius: 12,
24
+ backgroundColor: '#f3f4f6',
25
+ }}
26
+ hoverStyle={{ backgroundColor: '#e5e7eb' }}
27
+ >
28
+ <Text style={{ fontWeight: '600' }}>Hover me (web)</Text>
29
+ </PressableWithHover>
30
+ ),
31
+ };
32
+
33
+ export const ListRow: Story = {
34
+ render: () => (
35
+ <PressableWithHover
36
+ accessibilityLabel="Settings row"
37
+ style={{ paddingVertical: 14, paddingHorizontal: 16, borderRadius: 8, width: 260 }}
38
+ hoverStyle={{ backgroundColor: '#f3f4f6' }}
39
+ >
40
+ <Text>Account settings</Text>
41
+ </PressableWithHover>
42
+ ),
43
+ };
@@ -0,0 +1,43 @@
1
+ import React, { forwardRef } from 'react';
2
+ import {
3
+ Pressable,
4
+ type PressableProps,
5
+ type StyleProp,
6
+ type View,
7
+ type ViewStyle,
8
+ } from 'react-native';
9
+
10
+ import { useInteractionState } from '../hooks/useInteractionState';
11
+
12
+ export interface PressableWithHoverProps extends Omit<PressableProps, 'style'> {
13
+ /** Base style; `hoverStyle` is merged on top while the pointer is over it. */
14
+ style?: StyleProp<ViewStyle>;
15
+ /**
16
+ * Style merged over `style` while hovered. Web-only: react-native-web fires
17
+ * `onHoverIn`/`onHoverOut`, so on touch platforms this is simply never applied.
18
+ */
19
+ hoverStyle: StyleProp<ViewStyle>;
20
+ }
21
+
22
+ /**
23
+ * A {@link Pressable} that merges `hoverStyle` on top of `style` while hovered.
24
+ * Hover is a web-only affordance (native platforms have no pointer hover), so
25
+ * this behaves like a plain `Pressable` on touch devices.
26
+ */
27
+ export const PressableWithHover = forwardRef<View, PressableWithHoverProps>(
28
+ function PressableWithHover({ style, hoverStyle, ...props }, ref) {
29
+ const { state: hovered, onIn: onHoverIn, onOut: onHoverOut } = useInteractionState();
30
+
31
+ return (
32
+ <Pressable
33
+ {...props}
34
+ ref={ref}
35
+ onHoverIn={onHoverIn}
36
+ onHoverOut={onHoverOut}
37
+ style={hovered ? [style, hoverStyle] : style}
38
+ />
39
+ );
40
+ },
41
+ );
42
+
43
+ PressableWithHover.displayName = 'PressableWithHover';
@@ -0,0 +1,2 @@
1
+ export { PressableWithHover } from './PressableWithHover';
2
+ export type { PressableWithHoverProps } from './PressableWithHover';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Bloom's responsive breakpoint min-widths (px), matching the Tailwind /
3
+ * NativeWind defaults used across the design system.
4
+ *
5
+ * This is the SINGLE SOURCE OF TRUTH for breakpoint values. Anything that
6
+ * branches on viewport width — the `<Dialog>` responsive `placement` resolver,
7
+ * the `useGutters` hook — reads from here rather than hardcoding its own
8
+ * numbers, so a change here propagates everywhere.
9
+ */
10
+ export const BREAKPOINTS = {
11
+ sm: 640,
12
+ md: 768,
13
+ lg: 1024,
14
+ xl: 1280,
15
+ } as const;
16
+
17
+ /** A named responsive breakpoint tier. */
18
+ export type Breakpoint = keyof typeof BREAKPOINTS;
@@ -1,6 +1,8 @@
1
1
  export { atoms, flatten } from './atoms';
2
2
  export type { ViewStyleProp, TextStyleProp } from './atoms';
3
3
  export * as tokens from './tokens';
4
+ export { BREAKPOINTS } from './breakpoints';
5
+ export type { Breakpoint } from './breakpoints';
4
6
  export { Z_INDEX, zIndex } from './z-index';
5
7
  export { web, native, ios, android, platform, select } from './platform';
6
8
  export { SUPPORTS_NATIVE_DRIVER } from './native-driver';
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import { Pressable, Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { useInteractionState } from '../hooks/useInteractionState';
6
+ import { SubtleHover } from './index';
7
+
8
+ const meta: Meta<typeof SubtleHover> = {
9
+ title: 'Components/SubtleHover',
10
+ component: SubtleHover,
11
+ };
12
+
13
+ export default meta;
14
+
15
+ type Story = StoryObj<typeof SubtleHover>;
16
+
17
+ /** A row that washes its background as the pointer enters (web). */
18
+ function HoverRow({ label, native }: { label: string; native?: boolean }) {
19
+ const { state: hovered, onIn, onOut } = useInteractionState();
20
+
21
+ return (
22
+ <Pressable
23
+ accessibilityLabel={label}
24
+ onHoverIn={onIn}
25
+ onHoverOut={onOut}
26
+ style={{
27
+ width: 280,
28
+ paddingVertical: 14,
29
+ paddingHorizontal: 16,
30
+ borderRadius: 12,
31
+ overflow: 'hidden',
32
+ }}
33
+ >
34
+ <SubtleHover hover={hovered} native={native} style={{ borderRadius: 12 }} />
35
+ <Text style={{ fontWeight: '600' }}>{label}</Text>
36
+ </Pressable>
37
+ );
38
+ }
39
+
40
+ export const ListRow: Story = {
41
+ render: () => <HoverRow label="Hover me (web)" />,
42
+ };
43
+
44
+ export const StrongerWash: Story = {
45
+ render: () => {
46
+ function Row() {
47
+ const { state: hovered, onIn, onOut } = useInteractionState();
48
+ return (
49
+ <Pressable
50
+ accessibilityLabel="Stronger wash"
51
+ onHoverIn={onIn}
52
+ onHoverOut={onOut}
53
+ style={{ width: 280, padding: 16, borderRadius: 12, overflow: 'hidden' }}
54
+ >
55
+ <SubtleHover hover={hovered} opacity={0.85} style={{ borderRadius: 12 }} />
56
+ <Text style={{ fontWeight: '600' }}>Higher opacity override</Text>
57
+ </Pressable>
58
+ );
59
+ }
60
+ return <Row />;
61
+ },
62
+ };
63
+
64
+ export const Stack: Story = {
65
+ render: () => (
66
+ <View style={{ gap: 4 }}>
67
+ <HoverRow label="Inbox" />
68
+ <HoverRow label="Sent" />
69
+ <HoverRow label="Drafts" />
70
+ </View>
71
+ ),
72
+ };
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import { Platform, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
3
+
4
+ import { useTheme } from '../theme';
5
+
6
+ /**
7
+ * True on touch-capable web browsers (coarse pointer). A hover wash only makes
8
+ * sense with a hovering pointer, so it is suppressed on touch web to match
9
+ * native (which has no hover at all).
10
+ */
11
+ const IS_WEB_TOUCH_DEVICE =
12
+ Platform.OS === 'web' &&
13
+ typeof window !== 'undefined' &&
14
+ typeof window.matchMedia === 'function' &&
15
+ window.matchMedia('(pointer: coarse)').matches;
16
+
17
+ /**
18
+ * Web-only style that (a) cross-fades the overlay opacity and (b) promotes it to
19
+ * its own GPU layer so Safari doesn't nudge the layout by a subpixel when the
20
+ * overlay composites on hover. Cast because RN's `ViewStyle` has no `willChange`
21
+ * / `transition*` keys; react-native-web forwards them to the DOM node.
22
+ */
23
+ const WEB_TRANSITION = {
24
+ transitionProperty: 'opacity',
25
+ transitionDuration: '0.1s',
26
+ transitionTimingFunction: 'ease',
27
+ willChange: 'opacity',
28
+ } as ViewStyle;
29
+
30
+ export interface SubtleHoverProps {
31
+ /** Whether the parent is currently hovered. Drives the overlay opacity. */
32
+ hover: boolean;
33
+ /**
34
+ * Overlay opacity when hovered. Defaults to a mode-tuned value (0.4 dark /
35
+ * 0.5 light) that reads as a gentle contrast wash.
36
+ */
37
+ opacity?: number;
38
+ /** Extra style merged last — e.g. `borderRadius` to match the parent. */
39
+ style?: StyleProp<ViewStyle>;
40
+ /**
41
+ * Opt in to rendering on native. Off by default: hover is a pointer
42
+ * affordance, so this is a web-only overlay unless a native surface wants the
43
+ * same wash driven by another interaction (e.g. press).
44
+ */
45
+ native?: boolean;
46
+ }
47
+
48
+ /**
49
+ * An absolutely-positioned, non-interactive contrast wash that fades in while
50
+ * its parent is hovered. Ported from Bluesky's `SubtleHover`. Pairs naturally
51
+ * with {@link useInteractionState} — pass its `state` as `hover`.
52
+ *
53
+ * Renders nothing on touch-web (no hovering pointer) and on native unless
54
+ * `native` is set. Color comes from the theme's `contrast50` token so the wash
55
+ * tracks the active preset and light/dark mode.
56
+ */
57
+ export function SubtleHover({ hover, opacity, style, native = false }: SubtleHoverProps) {
58
+ const theme = useTheme();
59
+
60
+ const isWeb = Platform.OS === 'web';
61
+ if (isWeb ? IS_WEB_TOUCH_DEVICE : !native) {
62
+ return null;
63
+ }
64
+
65
+ const targetOpacity = opacity ?? (theme.isDark ? 0.4 : 0.5);
66
+
67
+ return (
68
+ <View
69
+ style={[
70
+ styles.overlay,
71
+ { backgroundColor: theme.colors.contrast50, opacity: hover ? targetOpacity : 0 },
72
+ isWeb ? WEB_TRANSITION : null,
73
+ style,
74
+ ]}
75
+ />
76
+ );
77
+ }
78
+
79
+ SubtleHover.displayName = 'SubtleHover';
80
+
81
+ const styles = StyleSheet.create({
82
+ overlay: {
83
+ position: 'absolute',
84
+ top: 0,
85
+ left: 0,
86
+ right: 0,
87
+ bottom: 0,
88
+ pointerEvents: 'none',
89
+ },
90
+ });
@@ -0,0 +1,2 @@
1
+ export { SubtleHover } from './SubtleHover';
2
+ export type { SubtleHoverProps } from './SubtleHover';
@@ -2,6 +2,7 @@ import { Platform } from 'react-native';
2
2
  import { APP_COLOR_PRESETS, type AppColorName } from './color-presets';
3
3
  import { getAdaptiveColors } from './adaptive-colors';
4
4
  import { getResolvedTokens, hslToSrgb } from './token-registry';
5
+ import { THEME_GRADIENTS } from './gradients';
5
6
  import type { Theme, ThemeColors } from './types';
6
7
 
7
8
  /**
@@ -119,6 +120,7 @@ export function buildTheme(
119
120
  return {
120
121
  mode: resolved,
121
122
  colors,
123
+ gradients: THEME_GRADIENTS,
122
124
  isDark: resolved === 'dark',
123
125
  isLight: resolved === 'light',
124
126
  };
@@ -0,0 +1,61 @@
1
+ import type { ThemeGradients } from './types';
2
+
3
+ /**
4
+ * Brand-neutral named linear-gradient palettes shared across the design system —
5
+ * decorative avatar rings, hero surfaces, empty-state art, and similar accents.
6
+ *
7
+ * Each entry lists ordered `[offset, color]` stops where `offset` is a `0–1`
8
+ * position along the gradient axis. These are intentionally theme-agnostic (the
9
+ * same values are exposed in light and dark mode) and not tied to any single
10
+ * app's brand, so components can reference them by a stable, descriptive name.
11
+ */
12
+ export const THEME_GRADIENTS: ThemeGradients = {
13
+ sky: {
14
+ values: [
15
+ [0, '#0A7AFF'],
16
+ [1, '#59B9FF'],
17
+ ],
18
+ },
19
+ midnight: {
20
+ values: [
21
+ [0, '#022C5E'],
22
+ [1, '#4079BC'],
23
+ ],
24
+ },
25
+ sunrise: {
26
+ values: [
27
+ [0, '#4E90AE'],
28
+ [0.4, '#AEA3AB'],
29
+ [0.8, '#E6A98F'],
30
+ [1, '#F3A84C'],
31
+ ],
32
+ },
33
+ sunset: {
34
+ values: [
35
+ [0, '#6772AF'],
36
+ [0.6, '#B88BB6'],
37
+ [1, '#FFA6AC'],
38
+ ],
39
+ },
40
+ summer: {
41
+ values: [
42
+ [0, '#FF6A56'],
43
+ [0.3, '#FF9156'],
44
+ [1, '#FFDD87'],
45
+ ],
46
+ },
47
+ nordic: {
48
+ values: [
49
+ [0, '#083367'],
50
+ [1, '#9EE8C1'],
51
+ ],
52
+ },
53
+ bonfire: {
54
+ values: [
55
+ [0, '#203E4E'],
56
+ [0.4, '#755B62'],
57
+ [0.8, '#CD7765'],
58
+ [1, '#EF956E'],
59
+ ],
60
+ },
61
+ };
@@ -6,10 +6,11 @@ export type {
6
6
  export { BloomColorScope, useColorScopeStyle } from './color-scope';
7
7
  export type { BloomColorScopeProps } from './color-scope';
8
8
  export { buildTheme, STATUS_COLORS } from './build-theme';
9
+ export { THEME_GRADIENTS } from './gradients';
9
10
  export { useTheme, useThemeColor, useBloomTheme } from './use-theme';
10
11
  export { useNavigationTheme } from './use-navigation-theme';
11
12
  export type { NavigationTheme, NavigationThemeFont } from './use-navigation-theme';
12
- export type { Theme, ThemeColors, ThemeMode } from './types';
13
+ export type { Theme, ThemeColors, ThemeMode, ThemeGradient, ThemeGradients } from './types';
13
14
  export type { AppColorName, AppColorPreset, PresetTokens } from './color-presets';
14
15
  export {
15
16
  APP_COLOR_NAMES,
@@ -44,9 +44,22 @@ export interface ThemeColors {
44
44
  overlay: string;
45
45
  }
46
46
 
47
+ /**
48
+ * A single named gradient: an ordered list of `[offset, color]` stops where
49
+ * `offset` runs 0–1 along the gradient axis and `color` is any RN/CSS color.
50
+ */
51
+ export interface ThemeGradient {
52
+ values: Array<[number, string]>;
53
+ }
54
+
55
+ /** Map of named gradients exposed on the theme (`theme.gradients`). */
56
+ export type ThemeGradients = Record<string, ThemeGradient>;
57
+
47
58
  export interface Theme {
48
59
  mode: 'light' | 'dark';
49
60
  colors: ThemeColors;
61
+ /** Brand-neutral named linear-gradient palettes. Theme-agnostic (same in light/dark). */
62
+ gradients: ThemeGradients;
50
63
  isDark: boolean;
51
64
  isLight: boolean;
52
65
  }