@titan-design/react-ui 0.12.1 → 0.14.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 (531) hide show
  1. package/README.md +60 -47
  2. package/dist/bodymap.js +856 -736
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +858 -738
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1431 -237
  7. package/dist/index.d.ts +1431 -237
  8. package/dist/index.js +6506 -4781
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +6280 -4642
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
  13. package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1919 -1937
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1927 -1945
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +225 -140
  21. package/dist/theme/index.d.ts +225 -140
  22. package/dist/theme/index.js +298 -319
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +283 -315
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +145 -24
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +145 -24
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +128 -11
  31. package/dist/theme/tokens.d.ts +128 -11
  32. package/dist/theme/tokens.js +126 -26
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +124 -27
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/audits/2026-09-08-critique-addenda.md +51 -0
  37. package/docs/audits/2026-09-08-library-critique.md +212 -0
  38. package/docs/library-roadmap.md +99 -0
  39. package/docs/render-testing-pattern.md +130 -0
  40. package/package.json +1 -1
  41. package/src/arch/Architecture.stories.tsx +3 -1
  42. package/src/arch/arch-graph.freshness.test.ts +52 -0
  43. package/src/arch/arch-graph.json +4989 -1583
  44. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  45. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  46. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  48. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  49. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  51. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  52. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  54. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  55. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  57. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  58. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  60. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  61. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  63. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  64. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  66. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  67. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  69. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  70. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  71. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  73. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  74. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  76. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  77. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  78. package/src/components/custom/ActiveWork/README.md +305 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  80. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  81. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  82. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  83. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  84. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  86. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  87. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  88. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  90. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  91. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  92. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  93. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  94. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  95. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  96. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  97. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  98. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  99. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  100. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  101. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  102. package/src/components/custom/ActiveWork/index.ts +19 -0
  103. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  104. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  105. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  106. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  107. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
  108. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  109. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  110. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  111. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  112. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  113. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  116. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  117. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  119. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  120. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  121. package/src/components/custom/Fatigue/README.md +40 -3
  122. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  123. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  124. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  125. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  126. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  127. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  128. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  129. package/src/components/custom/Fatigue/index.ts +17 -5
  130. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  131. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  132. package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
  133. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  134. package/src/components/custom/Gauge/Gauge.tsx +7 -2
  135. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  136. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  137. package/src/components/custom/Metric/Metric.tsx +5 -20
  138. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  139. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  140. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  141. package/src/components/custom/Prose/README.md +58 -0
  142. package/src/components/custom/Prose/index.ts +1 -0
  143. package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
  144. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  145. package/src/components/custom/Scatter/Scatter.tsx +122 -18
  146. package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
  147. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  148. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  149. package/src/components/custom/Table/Table.stories.tsx +99 -16
  150. package/src/components/custom/Table/Table.test.tsx +273 -86
  151. package/src/components/custom/Table/Table.tsx +159 -67
  152. package/src/components/custom/Table/column-fit.test.ts +75 -0
  153. package/src/components/custom/Table/column-fit.ts +116 -0
  154. package/src/components/custom/Table/index.ts +9 -0
  155. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  156. package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
  157. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  158. package/src/components/custom/Treemap/Treemap.tsx +11 -3
  159. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  160. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  161. package/src/components/custom/Typography/Typography.tsx +6 -0
  162. package/src/components/custom/Typography/index.ts +1 -8
  163. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
  164. package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
  165. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  166. package/src/components/custom/Workout/BaseBadge.tsx +6 -2
  167. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  168. package/src/components/custom/Workout/BodyMap.tsx +8 -4
  169. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
  170. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  171. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
  172. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
  173. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  174. package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
  175. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  176. package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
  177. package/src/components/custom/Workout/DeviationBar.tsx +24 -16
  178. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
  179. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
  180. package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
  181. package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
  182. package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
  183. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
  184. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
  185. package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
  186. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  187. package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
  188. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  189. package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
  190. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
  191. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  192. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  193. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  194. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  195. package/src/components/custom/Workout/InputBar.tsx +4 -2
  196. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  197. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  198. package/src/components/custom/Workout/IntensityBar.tsx +8 -3
  199. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  200. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  201. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  202. package/src/components/custom/Workout/MesoCard.tsx +14 -10
  203. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  204. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  205. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  206. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  207. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  208. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
  209. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  210. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  211. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  212. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  213. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
  214. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  215. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  216. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  217. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  218. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  219. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
  220. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  221. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  222. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  223. package/src/components/custom/Workout/README.md +68 -17
  224. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  225. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  226. package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
  227. package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
  228. package/src/components/custom/Workout/RestTimer.tsx +4 -2
  229. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  230. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  231. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  232. package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
  233. package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
  234. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  235. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  236. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  237. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  238. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  239. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  240. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  241. package/src/components/custom/Workout/SetBar.tsx +38 -12
  242. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  243. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  244. package/src/components/custom/Workout/SetRow.tsx +61 -23
  245. package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
  246. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  247. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  248. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  249. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  250. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  251. package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
  252. package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
  253. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  254. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  255. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  256. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  257. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  258. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  259. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  260. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  261. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
  262. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  263. package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
  264. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  265. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  266. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  267. package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
  268. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  269. package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
  270. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  271. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  272. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
  273. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
  274. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
  275. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  276. package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
  277. package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
  278. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  279. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  280. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  281. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  282. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  283. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  284. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  285. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  286. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  287. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  288. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  289. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  290. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  291. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  292. package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
  293. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  294. package/src/components/custom/Workout/exerciseRowState.ts +29 -0
  295. package/src/components/custom/Workout/index.ts +17 -2
  296. package/src/components/custom/Workout/metricText.stories.tsx +15 -13
  297. package/src/components/custom/Workout/metricText.tsx +8 -2
  298. package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
  299. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  300. package/src/components/custom/charts/README.md +66 -0
  301. package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
  302. package/src/components/custom/charts/SetBarChart.tsx +56 -9
  303. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  304. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  305. package/src/components/custom/charts/SparkBars.tsx +85 -0
  306. package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
  307. package/src/components/custom/charts/flatBarGeometry.ts +32 -0
  308. package/src/components/custom/charts/index.ts +3 -0
  309. package/src/components/custom/index.ts +3 -0
  310. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  311. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  312. package/src/components/custom/stepper/Stepper.tsx +21 -34
  313. package/src/components/icons/icons.stories.tsx +1 -1
  314. package/src/components/icons/icons.tsx +51 -0
  315. package/src/components/shell/AppShell.stories.tsx +150 -0
  316. package/src/components/shell/AppShell.test.tsx +70 -0
  317. package/src/components/shell/AppShell.tsx +90 -0
  318. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  319. package/src/components/shell/BrandLockup.test.tsx +34 -1
  320. package/src/components/shell/BrandLockup.tsx +27 -11
  321. package/src/components/shell/NavItem.stories.tsx +4 -4
  322. package/src/components/shell/NavItem.test.tsx +17 -0
  323. package/src/components/shell/NavItem.tsx +17 -5
  324. package/src/components/shell/README.md +89 -26
  325. package/src/components/shell/SideNav.stories.tsx +38 -7
  326. package/src/components/shell/SideNav.test.tsx +16 -8
  327. package/src/components/shell/SideNav.tsx +17 -17
  328. package/src/components/shell/TopBar.stories.tsx +44 -19
  329. package/src/components/shell/TopBar.test.tsx +34 -12
  330. package/src/components/shell/TopBar.tsx +61 -40
  331. package/src/components/shell/brands.tsx +66 -0
  332. package/src/components/shell/index.ts +9 -8
  333. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  334. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  335. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  336. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  337. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  338. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  339. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  340. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  341. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  342. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  343. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  344. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  345. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  346. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  347. package/src/components/shell/workout/index.ts +9 -0
  348. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  349. package/src/components/ui/README.md +110 -0
  350. package/src/components/ui/alert/Alert.stories.tsx +15 -5
  351. package/src/components/ui/alert/Alert.tsx +24 -3
  352. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  353. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  354. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  355. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  356. package/src/components/ui/avatar/Avatar.tsx +2 -10
  357. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  358. package/src/components/ui/badge/Badge.test.tsx +19 -3
  359. package/src/components/ui/badge/Badge.tsx +48 -55
  360. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  361. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  362. package/src/components/ui/button/Button.stories.tsx +46 -12
  363. package/src/components/ui/button/Button.tsx +44 -37
  364. package/src/components/ui/card/Card.stories.tsx +90 -94
  365. package/src/components/ui/card/Card.test.tsx +80 -6
  366. package/src/components/ui/card/Card.tsx +150 -203
  367. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  368. package/src/components/ui/checkbox/Checkbox.tsx +7 -27
  369. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  370. package/src/components/ui/chip/Chip.test.tsx +10 -6
  371. package/src/components/ui/chip/Chip.tsx +76 -91
  372. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  373. package/src/components/ui/collapse/Collapse.tsx +6 -11
  374. package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
  375. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  376. package/src/components/ui/data-row/DataRow.tsx +18 -7
  377. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  378. package/src/components/ui/divider/Divider.tsx +1 -5
  379. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  380. package/src/components/ui/drawer/Drawer.tsx +13 -27
  381. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  382. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  383. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  384. package/src/components/ui/form-field/FormField.tsx +10 -46
  385. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  386. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  387. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  388. package/src/components/ui/help-tip/index.ts +7 -1
  389. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  390. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  391. package/src/components/ui/icon-box/IconBox.tsx +10 -18
  392. package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
  393. package/src/components/ui/indicator/Indicator.tsx +20 -14
  394. package/src/components/ui/input/Input.stories.tsx +3 -11
  395. package/src/components/ui/input/Input.test.tsx +2 -10
  396. package/src/components/ui/input/Input.tsx +10 -25
  397. package/src/components/ui/input/index.ts +7 -1
  398. package/src/components/ui/link/Link.stories.tsx +19 -7
  399. package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
  400. package/src/components/ui/list-item/ListItem.tsx +2 -9
  401. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  402. package/src/components/ui/menu/Menu.test.tsx +2 -11
  403. package/src/components/ui/menu/Menu.tsx +12 -14
  404. package/src/components/ui/menu/index.ts +8 -1
  405. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  406. package/src/components/ui/modal/Modal.tsx +31 -19
  407. package/src/components/ui/pill/Pill.stories.tsx +111 -23
  408. package/src/components/ui/pill/Pill.test.tsx +101 -3
  409. package/src/components/ui/pill/Pill.tsx +168 -49
  410. package/src/components/ui/pill/index.ts +1 -1
  411. package/src/components/ui/popover/Popover.stories.tsx +6 -8
  412. package/src/components/ui/popover/Popover.test.tsx +1 -6
  413. package/src/components/ui/popover/Popover.tsx +20 -15
  414. package/src/components/ui/popover/index.ts +7 -1
  415. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  416. package/src/components/ui/progress/Progress.test.tsx +1 -5
  417. package/src/components/ui/progress/Progress.tsx +9 -6
  418. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  419. package/src/components/ui/radio/Radio.test.tsx +6 -2
  420. package/src/components/ui/radio/Radio.tsx +23 -22
  421. package/src/components/ui/section/Section.stories.tsx +12 -21
  422. package/src/components/ui/section/Section.test.tsx +1 -6
  423. package/src/components/ui/section/Section.tsx +1 -3
  424. package/src/components/ui/section/index.ts +1 -5
  425. package/src/components/ui/select/Select.stories.tsx +3 -13
  426. package/src/components/ui/select/Select.test.tsx +2 -11
  427. package/src/components/ui/select/Select.tsx +16 -15
  428. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  429. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  430. package/src/components/ui/skeleton/index.ts +1 -7
  431. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  432. package/src/components/ui/spinner/Spinner.tsx +4 -5
  433. package/src/components/ui/stack/Stack.stories.tsx +8 -8
  434. package/src/components/ui/stack/Stack.tsx +1 -1
  435. package/src/components/ui/surface/Surface.stories.tsx +92 -33
  436. package/src/components/ui/surface/Surface.test.tsx +88 -28
  437. package/src/components/ui/surface/Surface.tsx +37 -87
  438. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  439. package/src/components/ui/surface/index.ts +8 -0
  440. package/src/components/ui/surface/resolveSurface.ts +98 -0
  441. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  442. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  443. package/src/components/ui/switch/Switch.tsx +7 -6
  444. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  445. package/src/components/ui/tabs/Tabs.tsx +8 -21
  446. package/src/components/ui/tabs/index.ts +1 -7
  447. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  448. package/src/components/ui/tile/Tile.tsx +3 -0
  449. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  450. package/src/components/ui/toast/Toast.test.tsx +2 -8
  451. package/src/components/ui/toast/Toast.tsx +49 -57
  452. package/src/components/ui/toast/index.ts +0 -1
  453. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  454. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  455. package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
  456. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  457. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  458. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  459. package/src/components/ui/tooltip/index.ts +1 -1
  460. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  461. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  462. package/src/components/ui/trigger/index.ts +2 -0
  463. package/src/stories/PresetShowcase.stories.tsx +101 -48
  464. package/src/test/no-device-internals.test.ts +3 -3
  465. package/src/test/no-raw-color.test.ts +39 -0
  466. package/src/theme/Alpha.stories.tsx +103 -0
  467. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  468. package/src/theme/ColorPalettes.stories.tsx +62 -45
  469. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  470. package/src/theme/Depth.stories.tsx +132 -65
  471. package/src/theme/DepthCalibration.stories.tsx +23 -21
  472. package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
  473. package/src/theme/ThemeProvider.test.tsx +4 -4
  474. package/src/theme/barrel.ts +7 -6
  475. package/src/theme/color-stories.coverage.test.ts +1 -1
  476. package/src/theme/color-utils.ts +41 -29
  477. package/src/theme/config.completeness.test.ts +1 -1
  478. package/src/theme/config.ts +40 -0
  479. package/src/theme/elevation.test.ts +82 -56
  480. package/src/theme/elevation.ts +77 -561
  481. package/src/theme/global.css +45 -5
  482. package/src/theme/grey-ramp.test.ts +24 -8
  483. package/src/theme/lift.test.ts +39 -0
  484. package/src/theme/lift.ts +102 -0
  485. package/src/theme/materials.test.ts +5 -2
  486. package/src/theme/materials.ts +38 -4
  487. package/src/theme/native-theming-guard.test.ts +5 -5
  488. package/src/theme/presets/apply.ts +4 -0
  489. package/src/theme/presets/audiobook.ts +5 -2
  490. package/src/theme/presets/default.ts +4 -0
  491. package/src/theme/presets/index.ts +14 -1
  492. package/src/theme/presets/types.ts +1 -1
  493. package/src/theme/semantic-contrast.test.ts +13 -4
  494. package/src/theme/status-distinctness.test.ts +66 -0
  495. package/src/theme/subtle-tone-weight.test.ts +163 -0
  496. package/src/theme/surface-planes.test.ts +37 -0
  497. package/src/theme/surface-planes.ts +68 -0
  498. package/src/theme/tailwind-var-opacity.test.ts +163 -0
  499. package/src/theme/token-export-guard.test.ts +1 -3
  500. package/src/theme/tokens/primitives.test.ts +15 -7
  501. package/src/theme/tokens/primitives.ts +98 -33
  502. package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
  503. package/src/theme/tokens/semantic.ts +124 -54
  504. package/src/theme/wash-ladder.test.ts +58 -0
  505. package/src/theme/workout-tokens.ts +1 -7
  506. package/src/types/jest-axe.d.ts +2 -6
  507. package/src/types/raw-md.d.ts +5 -0
  508. package/src/utils/colors.alpha-adoption.test.ts +408 -0
  509. package/src/utils/colors.ts +4 -4
  510. package/src/utils/form.ts +1 -3
  511. package/src/utils/index.ts +2 -7
  512. package/src/utils/number-format.ts +17 -0
  513. package/src/utils/workout-format.ts +15 -6
  514. package/src/web-jsx/jsx-runtime.ts +1 -5
  515. package/tailwind.config.js +55 -9
  516. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  517. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  518. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  519. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  520. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  521. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  522. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  523. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  524. package/src/components/shell/DashboardShell.tsx +0 -75
  525. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  526. package/src/theme/elevation.stories.tsx +0 -346
  527. package/src/utils/useTheme.ts +0 -72
  528. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  529. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  530. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  531. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -1,22 +1,26 @@
1
1
  import React, { useMemo, useState } from 'react'
2
- import { View, Text, Pressable, type ViewProps } from 'react-native'
2
+ import { View, Text, Pressable, type ViewProps, type ViewStyle } from 'react-native'
3
3
  import { cn } from '../../../utils/cn'
4
- import {
5
- getElevationSurface,
6
- getElevationShadow,
7
- getBaseSurfaceColor,
8
- getValidatedElevation,
9
- type ElevationLevel
10
- } from '../../../theme'
11
- import { useTheme } from '../../../utils/useTheme'
4
+ import { liftStyle, type LiftStep } from '../../../theme/lift'
5
+ import { Surface } from '../surface/Surface'
6
+ import { SurfaceContext, type SurfaceContextValue } from '../surface/SurfaceContext'
7
+ import { useResolvedSurface } from '../surface/resolveSurface'
12
8
 
13
9
  export type CardVariant = 'elevated' | 'outline' | 'filled' | 'accent' | 'subtle'
14
- export type CardElevation = 1 | 2 | 3 // subtle, standard, prominent
10
+ /** Planes to lift above the enclosing Surface. */
11
+ export type CardElevation = 1 | 2 | 3
15
12
 
16
13
  export interface CardProps extends ViewProps {
17
- /** Visual variant */
14
+ /**
15
+ * How the card separates from its host:
16
+ * - `elevated` (default): lifts `elevation` planes and wears the lift (rim + shadow).
17
+ * - `accent`: as `elevated`, plus a left stripe.
18
+ * - `filled`: lifts `elevation` planes, tone only — for tiles nested in a lifted card.
19
+ * - `outline`: stays on the host plane with a hairline-strong edge — status borders.
20
+ * - `subtle`: stays on the host plane with a hairline-subtle edge.
21
+ */
18
22
  variant?: CardVariant
19
- /** Elevation level (1=subtle, 2=standard, 3=prominent) */
23
+ /** Planes to lift above the enclosing Surface (default 2: page → the card plane). */
20
24
  elevation?: CardElevation
21
25
  /** Whether the card is interactive (hoverable/pressable) */
22
26
  isInteractive?: boolean
@@ -26,7 +30,10 @@ export interface CardProps extends ViewProps {
26
30
  onPress?: () => void
27
31
  /** Custom border color (hex, rgb, or CSS color). Useful for status cards. */
28
32
  borderColor?: string
29
- /** Custom background color (hex, rgb, or CSS color). Useful for colored cards. */
33
+ /**
34
+ * Custom background color. The one way to override the plane: a `bg-*`
35
+ * className is discarded because the plane is written into `style`.
36
+ */
30
37
  bgColor?: string
31
38
  /** Accent stripe color for accent variant (CSS color or hex) */
32
39
  accentColor?: string
@@ -43,16 +50,51 @@ export interface CardProps extends ViewProps {
43
50
  skeletonContentLines?: number
44
51
  }
45
52
 
46
- const variantStyles: Record<CardVariant, string> = {
47
- elevated: '', // Will be set dynamically via elevation system
48
- outline: 'border-2 border-hairline-strong', // Thicker border with stronger contrast
49
- filled: '', // Will be set dynamically via elevation system
50
- accent: 'border border-hairline',
53
+ const EDGE_CLASS: Record<CardVariant, string> = {
54
+ elevated: '',
55
+ accent: '',
56
+ filled: '',
57
+ outline: 'border border-hairline-strong',
51
58
  subtle: 'border border-hairline-subtle',
52
59
  }
53
60
 
61
+ const LIFTED: Record<CardVariant, boolean> = {
62
+ elevated: true,
63
+ accent: true,
64
+ filled: false,
65
+ outline: false,
66
+ subtle: false,
67
+ }
68
+
69
+ const STAYS_ON_HOST: Record<CardVariant, boolean> = {
70
+ elevated: false,
71
+ accent: false,
72
+ filled: false,
73
+ outline: true,
74
+ subtle: true,
75
+ }
76
+
77
+ /** The card's plane and treatment, hover lifting one more plane when clickable. */
78
+ function useCardDepth(variant: CardVariant, elevation: CardElevation, hovered: boolean) {
79
+ const raise = STAYS_ON_HOST[variant] ? undefined : elevation
80
+ const resolved = useResolvedSurface({ raise, lift: LIFTED[variant] })
81
+ const depthStyle = useMemo(() => {
82
+ if (!hovered || !LIFTED[variant]) return resolved.depthStyle
83
+ const hoverStep = Math.min(3, resolved.step + 1) as LiftStep
84
+ return liftStyle(hoverStep, resolved.mode)
85
+ }, [hovered, variant, resolved])
86
+ return { ...resolved, depthStyle }
87
+ }
88
+
54
89
  /**
55
- * Card component for containing related content.
90
+ * Card component for containing related content. Sits `elevation` planes above
91
+ * the enclosing Surface and publishes its own plane, so a card nested in a card
92
+ * steps up again (and clamps at the top of the ramp).
93
+ *
94
+ * Lift sparingly: a card lifts off the page, but a small card lifted off a
95
+ * small card reads as clutter. Inside a card, organise with `CardInset`, a
96
+ * `filled` tile, or a divider, and keep a second lift for something that
97
+ * genuinely floats over the first.
56
98
  *
57
99
  * @example
58
100
  * <Card>
@@ -63,14 +105,6 @@ const variantStyles: Record<CardVariant, string> = {
63
105
  * <CardContent>
64
106
  * <Text>Card content goes here</Text>
65
107
  * </CardContent>
66
- * <CardFooter>
67
- * <Button>Action</Button>
68
- * </CardFooter>
69
- * </Card>
70
- *
71
- * // Interactive card
72
- * <Card isInteractive onPress={() => console.log('pressed')}>
73
- * <CardContent>Click me</CardContent>
74
108
  * </Card>
75
109
  */
76
110
  export function Card({
@@ -93,131 +127,67 @@ export function Card({
93
127
  }: CardProps) {
94
128
  const isClickable = isInteractive || !!onPress
95
129
  const [isHovered, setIsHovered] = useState(false)
96
- const theme = useTheme()
97
-
98
- // Get elevation level (validate and clamp to card's allowed range)
99
- const elevationLevel = useMemo(() => {
100
- const validated = getValidatedElevation('card', elevation as ElevationLevel)
101
- // Map variant to elevation if needed
102
- if (variant === 'outline' || variant === 'accent' || variant === 'subtle') {
103
- return 1 as ElevationLevel // These border variants use subtle elevation
104
- }
105
- return validated
106
- }, [variant, elevation])
107
-
108
- // Get base surface color for theme
109
- const baseColor = useMemo(() => getBaseSurfaceColor(theme), [theme])
110
-
111
- // Calculate surface color and shadow from elevation
112
- const surfaceColor = useMemo(() => {
113
- // Outline variant in light mode uses a specific off-white for clear visibility
114
- if ((variant === 'outline' || variant === 'accent') && theme === 'light') {
115
- return '#FAFAFA' // Matches --color-surface-elevated in light mode
116
- }
117
- // Subtle variant uses the base surface color without elevation lift
118
- if (variant === 'subtle') {
119
- return getElevationSurface(baseColor, 0 as ElevationLevel, theme)
120
- }
121
- return getElevationSurface(baseColor, elevationLevel, theme)
122
- }, [baseColor, elevationLevel, theme, variant])
123
-
124
- const shadowStyle = useMemo(() => {
125
- // Only apply hover shadow if card is actually clickable
126
- const shouldHover = isHovered && isClickable
127
- return getElevationShadow(baseColor, elevationLevel, theme, shouldHover)
128
- }, [baseColor, elevationLevel, theme, isHovered, isClickable])
129
-
130
- // Render skeleton content when loading, otherwise render children
131
- const cardContent = isLoading ? (
132
- <>
133
- {skeletonHasHeader && (
134
- <CardHeader>
135
- <View className="h-5 w-1/3 bg-interactive-disabled rounded" />
136
- <View className="mt-2 h-4 w-2/3 bg-interactive-disabled rounded" />
137
- </CardHeader>
138
- )}
139
- <CardContent>
140
- {Array.from({ length: skeletonContentLines }).map((_, i) => (
141
- <View
142
- key={i}
143
- className={cn(
144
- 'h-4 bg-interactive-disabled rounded',
145
- i < skeletonContentLines - 1 ? 'mb-2 w-full' : 'w-4/5'
146
- )}
147
- />
148
- ))}
149
- </CardContent>
150
- {skeletonHasFooter && (
151
- <CardFooter>
152
- <View className="h-9 w-24 bg-interactive-disabled rounded" />
153
- </CardFooter>
154
- )}
155
- </>
156
- ) : (
157
- children
158
- )
130
+ const depth = useCardDepth(variant, elevation, isHovered && isClickable)
159
131
 
160
132
  const baseClassName = cn(
161
133
  'rounded-lg overflow-hidden relative',
162
- // Apply variant styles, but only use default border color if no custom borderColor
163
- variant === 'outline'
164
- ? borderColor
165
- ? 'border-2' // Just the border width, color via style
166
- : variantStyles[variant] // Full variant styles including color
167
- : variantStyles[variant],
168
- // Remove Tailwind shadow classes - we're using elevation shadows via style prop
134
+ borderColor ? 'border' : EDGE_CLASS[variant],
169
135
  isClickable && 'web:cursor-pointer web:transition-all web:duration-150',
170
136
  isClickable && 'web:hover:-translate-y-0.5 active:scale-[0.99]',
171
137
  isLoading && 'pointer-events-none animate-pulse',
172
138
  className
173
139
  )
174
140
 
175
- // Accent variant: left stripe via borderLeft override
176
- const accentStyle = useMemo(() => variant === 'accent' ? {
177
- borderLeftWidth: accentWidth ?? 3,
178
- borderLeftColor: accentColor ?? 'var(--color-brand-primary)',
179
- } : {}, [variant, accentWidth, accentColor])
180
-
181
- // Merge styles: background color from elevation + shadow style + custom colors + custom style
182
141
  const mergedStyle = useMemo(() => {
183
- const elevationStyle: Record<string, any> = {
184
- backgroundColor: bgColor || surfaceColor,
142
+ const own: ViewStyle & Record<string, unknown> = {
143
+ backgroundColor: bgColor || depth.backgroundColor,
185
144
  borderRadius: 8,
186
- overflow: 'hidden' as const,
187
- ...shadowStyle,
188
- ...accentStyle,
145
+ overflow: 'hidden',
146
+ ...depth.depthStyle,
189
147
  }
190
-
191
- // Add custom border color if provided
192
- if (borderColor) {
193
- elevationStyle.borderColor = borderColor
148
+ if (variant === 'accent') {
149
+ own.borderLeftWidth = accentWidth ?? 3
150
+ own.borderLeftColor = accentColor ?? 'var(--color-brand-primary)'
194
151
  }
152
+ if (borderColor) own.borderColor = borderColor
153
+ return style ? [own, style] : own
154
+ }, [style, depth, bgColor, borderColor, variant, accentWidth, accentColor])
195
155
 
196
- if (!style) return elevationStyle
197
- // User style takes precedence over elevation defaults (e.g. maxWidth, custom bg)
198
- return [elevationStyle, style]
199
- }, [style, surfaceColor, shadowStyle, borderColor, bgColor, accentStyle])
156
+ const context = useMemo<SurfaceContextValue>(
157
+ () => ({ mode: depth.mode, level: depth.plane }),
158
+ [depth.mode, depth.plane]
159
+ )
200
160
 
201
- if (isClickable) {
202
- return (
203
- <Pressable
204
- onPress={onPress}
205
- onHoverIn={() => setIsHovered(true)}
206
- onHoverOut={() => setIsHovered(false)}
207
- accessibilityRole="button"
208
- className={baseClassName}
209
- style={mergedStyle}
210
- {...props}
211
- >
212
- {cardContent}
213
- </Pressable>
214
- )
215
- }
161
+ const content = isLoading ? (
162
+ <SkeletonBody
163
+ hasHeader={skeletonHasHeader}
164
+ hasFooter={skeletonHasFooter}
165
+ contentLines={skeletonContentLines}
166
+ />
167
+ ) : (
168
+ children
169
+ )
216
170
 
217
171
  return (
218
- <View className={baseClassName} style={mergedStyle} {...props}>
219
- {cardContent}
220
- </View>
172
+ <SurfaceContext.Provider value={context}>
173
+ {isClickable ? (
174
+ <Pressable
175
+ onPress={onPress}
176
+ onHoverIn={() => setIsHovered(true)}
177
+ onHoverOut={() => setIsHovered(false)}
178
+ accessibilityRole="button"
179
+ className={baseClassName}
180
+ style={mergedStyle}
181
+ {...props}
182
+ >
183
+ {content}
184
+ </Pressable>
185
+ ) : (
186
+ <View className={baseClassName} style={mergedStyle} {...props}>
187
+ {content}
188
+ </View>
189
+ )}
190
+ </SurfaceContext.Provider>
221
191
  )
222
192
  }
223
193
 
@@ -230,11 +200,7 @@ export interface CardHeaderProps {
230
200
  * Header section of a Card.
231
201
  */
232
202
  export function CardHeader({ children, className }: CardHeaderProps) {
233
- return (
234
- <View className={cn('px-6 py-6', className)}>
235
- {children}
236
- </View>
237
- )
203
+ return <View className={cn('px-6 py-6', className)}>{children}</View>
238
204
  }
239
205
 
240
206
  export interface CardTitleProps {
@@ -265,11 +231,7 @@ export interface CardDescriptionProps {
265
231
  * Description for CardHeader.
266
232
  */
267
233
  export function CardDescription({ children, className }: CardDescriptionProps) {
268
- return (
269
- <Text className={cn('mt-1.5 text-sm text-text-secondary', className)}>
270
- {children}
271
- </Text>
272
- )
234
+ return <Text className={cn('mt-1.5 text-sm text-text-secondary', className)}>{children}</Text>
273
235
  }
274
236
 
275
237
  export interface CardContentProps {
@@ -281,11 +243,7 @@ export interface CardContentProps {
281
243
  * Content section of a Card.
282
244
  */
283
245
  export function CardContent({ children, className }: CardContentProps) {
284
- return (
285
- <View className={cn('px-6 py-4', className)}>
286
- {children}
287
- </View>
288
- )
246
+ return <View className={cn('px-6 py-4', className)}>{children}</View>
289
247
  }
290
248
 
291
249
  export interface CardFooterProps {
@@ -297,35 +255,18 @@ export interface CardFooterProps {
297
255
  * Footer section of a Card.
298
256
  */
299
257
  export function CardFooter({ children, className }: CardFooterProps) {
300
- return (
301
- <View className={cn('px-6 py-4 flex-row items-center gap-2', className)}>
302
- {children}
303
- </View>
304
- )
258
+ return <View className={cn('px-6 py-4 flex-row items-center gap-2', className)}>{children}</View>
305
259
  }
306
260
 
307
- export interface CardSkeletonProps {
308
- /** Whether to show header skeleton */
309
- hasHeader?: boolean
310
- /** Whether to show footer skeleton */
311
- hasFooter?: boolean
312
- /** Number of content lines */
313
- contentLines?: number
314
- /** Additional className */
315
- className?: string
261
+ interface SkeletonBodyProps {
262
+ hasHeader: boolean
263
+ hasFooter: boolean
264
+ contentLines: number
316
265
  }
317
266
 
318
- /**
319
- * Skeleton placeholder for Card loading state.
320
- */
321
- export function CardSkeleton({
322
- hasHeader = true,
323
- hasFooter = false,
324
- contentLines = 3,
325
- className,
326
- }: CardSkeletonProps) {
267
+ function SkeletonBody({ hasHeader, hasFooter, contentLines }: SkeletonBodyProps) {
327
268
  return (
328
- <Card variant="elevated" elevation={2} className={cn('animate-pulse', className)}>
269
+ <>
329
270
  {hasHeader && (
330
271
  <CardHeader>
331
272
  <View className="h-5 w-1/3 bg-interactive-disabled rounded" />
@@ -348,54 +289,60 @@ export function CardSkeleton({
348
289
  <View className="h-9 w-24 bg-interactive-disabled rounded" />
349
290
  </CardFooter>
350
291
  )}
292
+ </>
293
+ )
294
+ }
295
+
296
+ export interface CardSkeletonProps {
297
+ /** Whether to show header skeleton */
298
+ hasHeader?: boolean
299
+ /** Whether to show footer skeleton */
300
+ hasFooter?: boolean
301
+ /** Number of content lines */
302
+ contentLines?: number
303
+ /** Additional className */
304
+ className?: string
305
+ }
306
+
307
+ /**
308
+ * Skeleton placeholder for Card loading state.
309
+ */
310
+ export function CardSkeleton({
311
+ hasHeader = true,
312
+ hasFooter = false,
313
+ contentLines = 3,
314
+ className,
315
+ }: CardSkeletonProps) {
316
+ return (
317
+ <Card variant="elevated" className={cn('animate-pulse', className)}>
318
+ <SkeletonBody hasHeader={hasHeader} hasFooter={hasFooter} contentLines={contentLines} />
351
319
  </Card>
352
320
  )
353
321
  }
354
322
 
355
323
  export interface CardInsetProps extends ViewProps {
356
- /** Elevation level for inset (-2=deep, -1=shallow) */
357
- elevation?: -2 | -1
358
324
  /** Additional className */
359
325
  className?: string
360
326
  children?: React.ReactNode
361
327
  }
362
328
 
363
329
  /**
364
- * Inset element within a Card. Applies pressed neumorphic shadow
365
- * to create a sunken/recessed appearance.
366
- *
330
+ * A sunken well inside a Card: one plane down from the card with the inset
331
+ * recess. Nest one inside another to step down again (clamps at the frame).
332
+ *
367
333
  * @example
368
- * <Card elevation={2}>
334
+ * <Card>
369
335
  * <CardContent>
370
- * <CardInset elevation={-1}>
336
+ * <CardInset className="p-4">
371
337
  * <Text>This content appears inset/sunken</Text>
372
338
  * </CardInset>
373
339
  * </CardContent>
374
340
  * </Card>
375
341
  */
376
- export function CardInset({
377
- elevation = -1,
378
- className,
379
- children,
380
- style,
381
- ...props
382
- }: CardInsetProps) {
383
- const theme = useTheme()
384
- const baseColor = useMemo(() => getBaseSurfaceColor(theme), [theme])
385
- const surfaceColor = useMemo(() => {
386
- return getElevationSurface(baseColor, elevation as ElevationLevel, theme)
387
- }, [baseColor, elevation, theme])
388
- const shadowStyle = useMemo(() => {
389
- return getElevationShadow(baseColor, elevation as ElevationLevel, theme)
390
- }, [baseColor, elevation, theme])
391
-
342
+ export function CardInset({ className, children, ...props }: CardInsetProps) {
392
343
  return (
393
- <View
394
- className={cn('rounded', className)}
395
- style={[{ backgroundColor: surfaceColor }, shadowStyle, style]}
396
- {...props}
397
- >
344
+ <Surface pressed className={cn('rounded', className)} rounded={false} {...props}>
398
345
  {children}
399
- </View>
346
+ </Surface>
400
347
  )
401
348
  }
@@ -5,7 +5,7 @@ import { Checkbox, CheckboxGroup } from './Checkbox'
5
5
  const meta: Meta<typeof Checkbox> = {
6
6
  title: 'Components/Molecules/Checkbox',
7
7
  component: Checkbox,
8
- tags: ['autodocs'],
8
+ tags: ['autodocs', 'status:stable', '!status:review'],
9
9
  argTypes: {
10
10
  isChecked: {
11
11
  control: 'boolean',
@@ -78,11 +78,7 @@ export const Checkbox = forwardRef<View, CheckboxProps>(function Checkbox(
78
78
  disabled: isDisabled,
79
79
  }}
80
80
  accessibilityLabel={label}
81
- className={cn(
82
- 'flex-row items-start gap-2',
83
- isDisabled && 'opacity-50',
84
- className
85
- )}
81
+ className={cn('flex-row items-start gap-2', isDisabled && 'opacity-50', className)}
86
82
  {...props}
87
83
  >
88
84
  <View
@@ -97,28 +93,20 @@ export const Checkbox = forwardRef<View, CheckboxProps>(function Checkbox(
97
93
  )}
98
94
  >
99
95
  {isChecked && !isIndeterminate && (
100
- <View className={cn('bg-white rounded-sm', styles.icon)}>
96
+ <View className={cn('bg-on-brand-primary rounded-sm', styles.icon)}>
101
97
  {/* Checkmark icon - using a simple view as placeholder */}
102
98
  <Text className="text-brand-primary font-bold text-center leading-none">✓</Text>
103
99
  </View>
104
100
  )}
105
- {isIndeterminate && (
106
- <View className={cn('bg-white h-0.5 w-2/3 rounded')} />
107
- )}
101
+ {isIndeterminate && <View className={cn('bg-on-brand-primary h-0.5 w-2/3 rounded')} />}
108
102
  </View>
109
103
 
110
104
  {(label || helperText) && (
111
105
  <View className="flex-1">
112
106
  {label && (
113
- <Text className={cn('text-text-primary font-medium', styles.label)}>
114
- {label}
115
- </Text>
116
- )}
117
- {helperText && (
118
- <Text className="text-xs text-text-secondary mt-0.5">
119
- {helperText}
120
- </Text>
107
+ <Text className={cn('text-text-primary font-medium', styles.label)}>{label}</Text>
121
108
  )}
109
+ {helperText && <Text className="text-xs text-text-secondary mt-0.5">{helperText}</Text>}
122
110
  </View>
123
111
  )}
124
112
  </Pressable>
@@ -146,16 +134,8 @@ export function CheckboxGroup({
146
134
  }: CheckboxGroupProps) {
147
135
  return (
148
136
  <View className={cn('gap-1', className)}>
149
- {label && (
150
- <Text className="text-sm font-medium text-text-primary mb-2">
151
- {label}
152
- </Text>
153
- )}
154
- <View
155
- className={cn(
156
- orientation === 'vertical' ? 'flex-col gap-2' : 'flex-row gap-4'
157
- )}
158
- >
137
+ {label && <Text className="text-sm font-medium text-text-primary mb-2">{label}</Text>}
138
+ <View className={cn(orientation === 'vertical' ? 'flex-col gap-2' : 'flex-row gap-4')}>
159
139
  {children}
160
140
  </View>
161
141
  </View>
@@ -5,7 +5,7 @@ import { Chip } from './Chip'
5
5
  const meta: Meta<typeof Chip> = {
6
6
  title: 'Components/Atoms/Chip',
7
7
  component: Chip,
8
- tags: ['autodocs'],
8
+ tags: ['autodocs', 'status:stable', '!status:review'],
9
9
  argTypes: {
10
10
  variant: {
11
11
  control: 'select',
@@ -53,9 +53,15 @@ export const AllVariants: Story = {
53
53
  render: () => (
54
54
  <View style={{ gap: 12 }}>
55
55
  <View style={{ flexDirection: 'row', gap: 8 }}>
56
- <Chip variant="solid" color="primary">Solid</Chip>
57
- <Chip variant="subtle" color="primary">Subtle</Chip>
58
- <Chip variant="outline" color="primary">Outline</Chip>
56
+ <Chip variant="solid" color="primary">
57
+ Solid
58
+ </Chip>
59
+ <Chip variant="subtle" color="primary">
60
+ Subtle
61
+ </Chip>
62
+ <Chip variant="outline" color="primary">
63
+ Outline
64
+ </Chip>
59
65
  </View>
60
66
  </View>
61
67
  ),
@@ -75,8 +81,12 @@ export const Dismissible: Story = {
75
81
  render: () => (
76
82
  <View style={{ flexDirection: 'row', gap: 8 }}>
77
83
  <Chip onDelete={() => console.log('delete')}>Remove me</Chip>
78
- <Chip color="primary" onDelete={() => console.log('delete')}>Primary</Chip>
79
- <Chip color="error" onDelete={() => console.log('delete')}>Error</Chip>
84
+ <Chip color="primary" onDelete={() => console.log('delete')}>
85
+ Primary
86
+ </Chip>
87
+ <Chip color="error" onDelete={() => console.log('delete')}>
88
+ Error
89
+ </Chip>
80
90
  </View>
81
91
  ),
82
92
  }
@@ -85,7 +95,9 @@ export const Clickable: Story = {
85
95
  render: () => (
86
96
  <View style={{ flexDirection: 'row', gap: 8 }}>
87
97
  <Chip onPress={() => console.log('clicked')}>Click me</Chip>
88
- <Chip color="primary" onPress={() => console.log('clicked')}>Primary</Chip>
98
+ <Chip color="primary" onPress={() => console.log('clicked')}>
99
+ Primary
100
+ </Chip>
89
101
  </View>
90
102
  ),
91
103
  }
@@ -94,8 +106,12 @@ export const Disabled: Story = {
94
106
  render: () => (
95
107
  <View style={{ flexDirection: 'row', gap: 8 }}>
96
108
  <Chip isDisabled>Disabled</Chip>
97
- <Chip isDisabled onDelete={() => {}}>Disabled Dismissible</Chip>
98
- <Chip isDisabled onPress={() => {}}>Disabled Clickable</Chip>
109
+ <Chip isDisabled onDelete={() => {}}>
110
+ Disabled Dismissible
111
+ </Chip>
112
+ <Chip isDisabled onPress={() => {}}>
113
+ Disabled Clickable
114
+ </Chip>
99
115
  </View>
100
116
  ),
101
117
  }
@@ -103,10 +119,18 @@ export const Disabled: Story = {
103
119
  export const AsTagList: Story = {
104
120
  render: () => (
105
121
  <View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap', maxWidth: 300 }}>
106
- <Chip size="sm" color="primary" onDelete={() => {}}>React</Chip>
107
- <Chip size="sm" color="secondary" onDelete={() => {}}>TypeScript</Chip>
108
- <Chip size="sm" color="info" onDelete={() => {}}>Tailwind</Chip>
109
- <Chip size="sm" color="success" onDelete={() => {}}>NativeWind</Chip>
122
+ <Chip size="sm" color="primary" onDelete={() => {}}>
123
+ React
124
+ </Chip>
125
+ <Chip size="sm" color="secondary" onDelete={() => {}}>
126
+ TypeScript
127
+ </Chip>
128
+ <Chip size="sm" color="info" onDelete={() => {}}>
129
+ Tailwind
130
+ </Chip>
131
+ <Chip size="sm" color="success" onDelete={() => {}}>
132
+ NativeWind
133
+ </Chip>
110
134
  </View>
111
135
  ),
112
136
  }
@@ -53,11 +53,7 @@ describe('Chip', () => {
53
53
  })
54
54
 
55
55
  it('renders leftElement', () => {
56
- render(
57
- <Chip leftElement={<span data-testid="icon">icon</span>}>
58
- With Icon
59
- </Chip>
60
- )
56
+ render(<Chip leftElement={<span data-testid="icon">icon</span>}>With Icon</Chip>)
61
57
  expect(screen.getByTestId('icon')).toBeInTheDocument()
62
58
  })
63
59
 
@@ -71,7 +67,15 @@ describe('Chip', () => {
71
67
  })
72
68
 
73
69
  it('renders with all color options', () => {
74
- const colors = ['default', 'primary', 'secondary', 'success', 'error', 'warning', 'info'] as const
70
+ const colors = [
71
+ 'default',
72
+ 'primary',
73
+ 'secondary',
74
+ 'success',
75
+ 'error',
76
+ 'warning',
77
+ 'info',
78
+ ] as const
75
79
  colors.forEach((color) => {
76
80
  const { unmount } = render(<Chip color={color}>Test</Chip>)
77
81
  expect(screen.getByText('Test')).toBeInTheDocument()