@titan-design/react-ui 0.12.0 → 0.13.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 (509) hide show
  1. package/README.md +46 -47
  2. package/dist/bodymap.js +768 -701
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +770 -703
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1320 -215
  7. package/dist/index.d.ts +1320 -215
  8. package/dist/index.js +5996 -4498
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +5773 -4357
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-CvsFqNtx.d.mts → pages-CpW3OB3g.d.mts} +21 -3
  13. package/dist/{pages-B1hqnGIC.d.ts → pages-_3Ajpiqu.d.ts} +21 -3
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1435 -1428
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1443 -1436
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +202 -1042
  21. package/dist/theme/index.d.ts +202 -1042
  22. package/dist/theme/index.js +215 -300
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +202 -296
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +62 -5
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +62 -5
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +972 -0
  31. package/dist/theme/tokens.d.ts +972 -0
  32. package/dist/theme/tokens.js +904 -0
  33. package/dist/theme/tokens.js.map +1 -0
  34. package/dist/theme/tokens.mjs +880 -0
  35. package/dist/theme/tokens.mjs.map +1 -0
  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/package.json +7 -1
  40. package/src/arch/arch-graph.freshness.test.ts +52 -0
  41. package/src/arch/arch-graph.json +5021 -1617
  42. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  43. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  44. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  45. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  46. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  48. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  49. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  51. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  52. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  54. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  55. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  57. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  58. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  60. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  61. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +181 -0
  63. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  64. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  66. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  67. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  69. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  70. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  71. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  73. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  74. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  76. package/src/components/custom/ActiveWork/README.md +305 -0
  77. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  78. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  80. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  81. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  82. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  83. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  84. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  86. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  87. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  88. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  90. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  91. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  92. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  93. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  94. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  95. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  96. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  97. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  98. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  99. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  100. package/src/components/custom/ActiveWork/index.ts +19 -0
  101. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  102. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  103. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  104. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  105. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +4 -3
  106. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  107. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  108. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  109. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  110. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  111. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  112. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  113. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  116. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  117. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  119. package/src/components/custom/Fatigue/README.md +40 -3
  120. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  121. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  122. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  123. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  124. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  125. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  126. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  127. package/src/components/custom/Fatigue/index.ts +17 -5
  128. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  129. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  130. package/src/components/custom/Gauge/Gauge.stories.tsx +2 -2
  131. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  132. package/src/components/custom/Gauge/Gauge.tsx +4 -1
  133. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  134. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  135. package/src/components/custom/Metric/Metric.tsx +5 -20
  136. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  137. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  138. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  139. package/src/components/custom/Prose/README.md +58 -0
  140. package/src/components/custom/Prose/index.ts +1 -0
  141. package/src/components/custom/Scatter/Scatter.stories.tsx +46 -8
  142. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  143. package/src/components/custom/Scatter/Scatter.tsx +116 -14
  144. package/src/components/custom/Sidebar/Sidebar.stories.tsx +40 -7
  145. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  146. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  147. package/src/components/custom/Table/Table.stories.tsx +98 -15
  148. package/src/components/custom/Table/Table.test.tsx +273 -86
  149. package/src/components/custom/Table/Table.tsx +159 -67
  150. package/src/components/custom/Table/column-fit.test.ts +75 -0
  151. package/src/components/custom/Table/column-fit.ts +116 -0
  152. package/src/components/custom/Table/index.ts +9 -0
  153. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  154. package/src/components/custom/Treemap/Treemap.stories.tsx +2 -2
  155. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  156. package/src/components/custom/Treemap/Treemap.tsx +7 -2
  157. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  158. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  159. package/src/components/custom/Typography/Typography.tsx +6 -0
  160. package/src/components/custom/Typography/index.ts +1 -8
  161. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +9 -1
  162. package/src/components/custom/Workout/BaseBadge.stories.tsx +19 -7
  163. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  164. package/src/components/custom/Workout/BaseBadge.tsx +3 -0
  165. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  166. package/src/components/custom/Workout/BodyMap.tsx +3 -1
  167. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +16 -4
  168. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  169. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +45 -9
  170. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +10 -8
  171. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  172. package/src/components/custom/Workout/CapacityBandChart.tsx +139 -108
  173. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  174. package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
  175. package/src/components/custom/Workout/DeviationBar.tsx +21 -16
  176. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +182 -0
  177. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +3 -2
  178. package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -7
  179. package/src/components/custom/Workout/ExerciseCard.test.tsx +13 -3
  180. package/src/components/custom/Workout/ExerciseCard.tsx +42 -51
  181. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +7 -7
  182. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  183. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  184. package/src/components/custom/Workout/ExerciseHeading.tsx +8 -5
  185. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -4
  186. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  187. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  188. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  189. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  190. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  191. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  192. package/src/components/custom/Workout/IntensityBar.tsx +4 -2
  193. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  194. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  195. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  196. package/src/components/custom/Workout/MesoCard.tsx +12 -9
  197. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  198. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  199. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  200. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  201. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  202. package/src/components/custom/Workout/MesoStatusCard.tsx +9 -23
  203. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  204. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  205. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  206. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  207. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  208. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  209. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  210. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  211. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  212. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  213. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +4 -1
  214. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  215. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  216. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  217. package/src/components/custom/Workout/README.md +12 -7
  218. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  219. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  220. package/src/components/custom/Workout/ReadinessCheck.tsx +19 -3
  221. package/src/components/custom/Workout/RestTimer.stories.tsx +3 -2
  222. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  223. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  224. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  225. package/src/components/custom/Workout/SegmentedBar.stories.tsx +5 -5
  226. package/src/components/custom/Workout/SegmentedBar.tsx +6 -2
  227. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  228. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  229. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  230. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  231. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  232. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  233. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  234. package/src/components/custom/Workout/SetBar.tsx +38 -12
  235. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  236. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  237. package/src/components/custom/Workout/SetRow.tsx +61 -23
  238. package/src/components/custom/Workout/SetStrip.stories.tsx +60 -5
  239. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  240. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  241. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  242. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  243. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  244. package/src/components/custom/Workout/SetsRepsLoad.tsx +3 -5
  245. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  246. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  247. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  248. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  249. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  250. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  251. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  252. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  253. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  254. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +9 -3
  255. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  256. package/src/components/custom/Workout/StrengthTrendChart.tsx +31 -30
  257. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  258. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  259. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  260. package/src/components/custom/Workout/TempoDisplay.stories.tsx +22 -7
  261. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  262. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  263. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  264. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +7 -4
  265. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +4 -1
  266. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +4 -1
  267. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  268. package/src/components/custom/Workout/VelocityStrip.tsx +3 -0
  269. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  270. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  271. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  272. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  273. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  274. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  275. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  276. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  277. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  278. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  279. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  280. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  281. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  282. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  283. package/src/components/custom/Workout/ZoneTrack.stories.tsx +7 -6
  284. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  285. package/src/components/custom/Workout/index.ts +5 -0
  286. package/src/components/custom/Workout/metricText.stories.tsx +6 -5
  287. package/src/components/custom/Workout/metricText.tsx +4 -0
  288. package/src/components/custom/Workout/setHeadingKit.tsx +207 -26
  289. package/src/components/custom/charts/README.md +61 -0
  290. package/src/components/custom/charts/SetBarChart.test.tsx +30 -1
  291. package/src/components/custom/charts/SetBarChart.tsx +42 -4
  292. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  293. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  294. package/src/components/custom/charts/SparkBars.tsx +85 -0
  295. package/src/components/custom/charts/index.ts +3 -0
  296. package/src/components/custom/index.ts +3 -0
  297. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  298. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  299. package/src/components/custom/stepper/Stepper.tsx +13 -28
  300. package/src/components/icons/icons.stories.tsx +1 -1
  301. package/src/components/icons/icons.tsx +51 -0
  302. package/src/components/shell/AppShell.stories.tsx +150 -0
  303. package/src/components/shell/AppShell.test.tsx +70 -0
  304. package/src/components/shell/AppShell.tsx +90 -0
  305. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  306. package/src/components/shell/BrandLockup.test.tsx +34 -1
  307. package/src/components/shell/BrandLockup.tsx +27 -11
  308. package/src/components/shell/NavItem.stories.tsx +4 -4
  309. package/src/components/shell/NavItem.test.tsx +17 -0
  310. package/src/components/shell/NavItem.tsx +17 -5
  311. package/src/components/shell/README.md +90 -27
  312. package/src/components/shell/SideNav.stories.tsx +38 -7
  313. package/src/components/shell/SideNav.test.tsx +16 -8
  314. package/src/components/shell/SideNav.tsx +17 -17
  315. package/src/components/shell/TopBar.stories.tsx +44 -19
  316. package/src/components/shell/TopBar.test.tsx +34 -12
  317. package/src/components/shell/TopBar.tsx +61 -40
  318. package/src/components/shell/brands.tsx +66 -0
  319. package/src/components/shell/index.ts +9 -8
  320. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  321. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  322. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  323. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  324. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  325. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  326. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  327. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  328. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  329. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  330. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  331. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  332. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  333. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  334. package/src/components/shell/workout/index.ts +9 -0
  335. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  336. package/src/components/ui/README.md +110 -0
  337. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  338. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  339. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  340. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  341. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  342. package/src/components/ui/avatar/Avatar.tsx +2 -10
  343. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  344. package/src/components/ui/badge/Badge.test.tsx +19 -3
  345. package/src/components/ui/badge/Badge.tsx +48 -55
  346. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  347. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  348. package/src/components/ui/button/Button.stories.tsx +46 -12
  349. package/src/components/ui/button/Button.tsx +32 -25
  350. package/src/components/ui/card/Card.stories.tsx +90 -94
  351. package/src/components/ui/card/Card.test.tsx +80 -6
  352. package/src/components/ui/card/Card.tsx +150 -203
  353. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  354. package/src/components/ui/checkbox/Checkbox.tsx +6 -26
  355. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  356. package/src/components/ui/chip/Chip.test.tsx +10 -6
  357. package/src/components/ui/chip/Chip.tsx +76 -91
  358. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  359. package/src/components/ui/collapse/Collapse.tsx +6 -11
  360. package/src/components/ui/data-row/DataRow.stories.tsx +9 -2
  361. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  362. package/src/components/ui/data-row/DataRow.tsx +18 -7
  363. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  364. package/src/components/ui/divider/Divider.tsx +1 -5
  365. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  366. package/src/components/ui/drawer/Drawer.tsx +13 -30
  367. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  368. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  369. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  370. package/src/components/ui/form-field/FormField.tsx +10 -46
  371. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  372. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  373. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  374. package/src/components/ui/help-tip/index.ts +7 -1
  375. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  376. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  377. package/src/components/ui/icon-box/IconBox.tsx +2 -12
  378. package/src/components/ui/indicator/Indicator.stories.tsx +1 -1
  379. package/src/components/ui/indicator/Indicator.tsx +20 -14
  380. package/src/components/ui/input/Input.stories.tsx +3 -11
  381. package/src/components/ui/input/Input.test.tsx +2 -10
  382. package/src/components/ui/input/Input.tsx +10 -25
  383. package/src/components/ui/input/index.ts +7 -1
  384. package/src/components/ui/link/Link.stories.tsx +19 -7
  385. package/src/components/ui/list-item/ListItem.stories.tsx +11 -2
  386. package/src/components/ui/list-item/ListItem.tsx +2 -9
  387. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  388. package/src/components/ui/menu/Menu.test.tsx +2 -11
  389. package/src/components/ui/menu/Menu.tsx +12 -14
  390. package/src/components/ui/menu/index.ts +8 -1
  391. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  392. package/src/components/ui/modal/Modal.tsx +29 -19
  393. package/src/components/ui/pill/Pill.stories.tsx +58 -23
  394. package/src/components/ui/pill/Pill.test.tsx +101 -3
  395. package/src/components/ui/pill/Pill.tsx +164 -49
  396. package/src/components/ui/pill/index.ts +1 -1
  397. package/src/components/ui/popover/Popover.stories.tsx +5 -7
  398. package/src/components/ui/popover/Popover.test.tsx +1 -6
  399. package/src/components/ui/popover/Popover.tsx +20 -15
  400. package/src/components/ui/popover/index.ts +7 -1
  401. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  402. package/src/components/ui/progress/Progress.test.tsx +1 -5
  403. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  404. package/src/components/ui/radio/Radio.test.tsx +6 -2
  405. package/src/components/ui/radio/Radio.tsx +23 -22
  406. package/src/components/ui/section/Section.stories.tsx +5 -14
  407. package/src/components/ui/section/Section.test.tsx +1 -6
  408. package/src/components/ui/section/Section.tsx +1 -3
  409. package/src/components/ui/section/index.ts +1 -5
  410. package/src/components/ui/select/Select.stories.tsx +3 -13
  411. package/src/components/ui/select/Select.test.tsx +2 -11
  412. package/src/components/ui/select/Select.tsx +14 -14
  413. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  414. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  415. package/src/components/ui/skeleton/index.ts +1 -7
  416. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  417. package/src/components/ui/spinner/Spinner.tsx +1 -4
  418. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  419. package/src/components/ui/stack/Stack.tsx +1 -1
  420. package/src/components/ui/surface/Surface.stories.tsx +17 -5
  421. package/src/components/ui/surface/Surface.test.tsx +88 -28
  422. package/src/components/ui/surface/Surface.tsx +37 -87
  423. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  424. package/src/components/ui/surface/index.ts +8 -0
  425. package/src/components/ui/surface/resolveSurface.ts +98 -0
  426. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  427. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  428. package/src/components/ui/switch/Switch.tsx +7 -6
  429. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  430. package/src/components/ui/tabs/Tabs.tsx +7 -22
  431. package/src/components/ui/tabs/index.ts +1 -7
  432. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  433. package/src/components/ui/tile/Tile.tsx +3 -0
  434. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  435. package/src/components/ui/toast/Toast.test.tsx +2 -8
  436. package/src/components/ui/toast/Toast.tsx +43 -53
  437. package/src/components/ui/toast/index.ts +0 -1
  438. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  439. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  440. package/src/components/ui/toolbar-button/ToolbarButton.tsx +25 -43
  441. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  442. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  443. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  444. package/src/components/ui/tooltip/index.ts +1 -1
  445. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  446. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  447. package/src/components/ui/trigger/index.ts +2 -0
  448. package/src/stories/PresetShowcase.stories.tsx +100 -47
  449. package/src/test/no-device-internals.test.ts +3 -3
  450. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  451. package/src/theme/ColorPalettes.stories.tsx +62 -45
  452. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  453. package/src/theme/Depth.stories.tsx +132 -65
  454. package/src/theme/DepthCalibration.stories.tsx +24 -22
  455. package/src/theme/ThemeProvider.test.tsx +4 -4
  456. package/src/theme/barrel.ts +7 -6
  457. package/src/theme/color-stories.coverage.test.ts +1 -1
  458. package/src/theme/color-utils.ts +41 -29
  459. package/src/theme/config.completeness.test.ts +1 -1
  460. package/src/theme/config.ts +28 -0
  461. package/src/theme/elevation.test.ts +82 -56
  462. package/src/theme/elevation.ts +77 -561
  463. package/src/theme/global.css +30 -2
  464. package/src/theme/grey-ramp.test.ts +24 -8
  465. package/src/theme/lift.test.ts +39 -0
  466. package/src/theme/lift.ts +102 -0
  467. package/src/theme/materials.test.ts +5 -2
  468. package/src/theme/materials.ts +38 -4
  469. package/src/theme/native-theming-guard.test.ts +5 -5
  470. package/src/theme/presets/apply.ts +4 -0
  471. package/src/theme/presets/audiobook.ts +5 -2
  472. package/src/theme/presets/default.ts +4 -0
  473. package/src/theme/presets/index.ts +14 -1
  474. package/src/theme/presets/types.ts +1 -1
  475. package/src/theme/semantic-contrast.test.ts +13 -4
  476. package/src/theme/status-distinctness.test.ts +66 -0
  477. package/src/theme/surface-planes.test.ts +37 -0
  478. package/src/theme/surface-planes.ts +68 -0
  479. package/src/theme/token-export-guard.test.ts +102 -0
  480. package/src/theme/tokens/primitives.test.ts +15 -7
  481. package/src/theme/tokens/primitives.ts +67 -35
  482. package/src/theme/tokens/semantic.ts +71 -37
  483. package/src/theme/wash-ladder.test.ts +58 -0
  484. package/src/theme/workout-tokens.ts +1 -7
  485. package/src/types/jest-axe.d.ts +2 -6
  486. package/src/types/raw-md.d.ts +5 -0
  487. package/src/utils/colors.ts +4 -4
  488. package/src/utils/form.ts +1 -3
  489. package/src/utils/index.ts +2 -7
  490. package/src/utils/number-format.ts +17 -0
  491. package/src/utils/workout-format.ts +15 -6
  492. package/src/web-jsx/jsx-runtime.ts +1 -5
  493. package/tailwind.config.js +5 -0
  494. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  495. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  496. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  497. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  498. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  499. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  500. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  501. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  502. package/src/components/shell/DashboardShell.tsx +0 -75
  503. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  504. package/src/theme/elevation.stories.tsx +0 -346
  505. package/src/utils/useTheme.ts +0 -72
  506. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  507. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  508. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  509. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -0,0 +1,182 @@
1
+ // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import { View, Text } from 'react-native'
4
+ import { DualVelocityStrip } from './VelocityStrip'
5
+ import { greyRamp, primitiveColors } from '../../../theme/tokens/primitives'
6
+
7
+ const PAGE_BG = greyRamp[975]
8
+ const PANEL_BG = greyRamp[950]
9
+ const LIST_BG = greyRamp[925]
10
+
11
+ /** Bilateral cable chest press, LEFT dominant / RIGHT lagging, 6 of 8 reps done. */
12
+ const LEFT = [0.54, 0.52, 0.5, 0.48, 0.46, 0.44]
13
+ const RIGHT = [0.49, 0.47, 0.45, 0.42, 0.4, 0.38]
14
+
15
+ const meta: Meta<typeof DualVelocityStrip> = {
16
+ title: 'Custom/Workout/DataViz/DualVelocityStrip',
17
+ component: DualVelocityStrip,
18
+ tags: ['autodocs'],
19
+ parameters: {
20
+ layout: 'fullscreen',
21
+ docs: {
22
+ description: {
23
+ component:
24
+ 'The dual-voltra (bilateral) DIVERGING per-rep velocity chart. LEFT reps grow UP, ' +
25
+ 'RIGHT reps grow DOWN from one shared centre axis — one mirrored pair per rep index — ' +
26
+ 'so a left-dominant / right-lagging set reads pre-attentively as an asymmetric ' +
27
+ 'silhouette. Reuses VelocityStrip’s slot vocabulary (rep / todo / variable / ' +
28
+ 'continue), its velocity-zone colors, hero geometry, and the live-rep pop. Color is ' +
29
+ 'ALWAYS the velocity zone; SIDE is encoded by position only, never hue. `variant="hero"` ' +
30
+ 'is the wall scale (value labels + per-side running-best reference lines); `variant="rail"` ' +
31
+ 'is the compact rail-expanded scale. Single-voltra sets keep using VelocityStrip.',
32
+ },
33
+ },
34
+ },
35
+ argTypes: {
36
+ variant: { control: 'inline-radio', options: ['hero', 'rail'] },
37
+ targetReps: { control: 'number' },
38
+ liveRepIndex: { control: 'number' },
39
+ height: { control: { type: 'number', min: 48, max: 320, step: 4 } },
40
+ scale: { control: 'inline-radio', options: ['peak', 'fixed'] },
41
+ },
42
+ }
43
+ export default meta
44
+ type Story = StoryObj<typeof DualVelocityStrip>
45
+
46
+ function Panel({ children, width }: { children: React.ReactNode; width?: number }) {
47
+ return (
48
+ <View style={{ width, backgroundColor: PANEL_BG, borderRadius: 12, padding: 20, gap: 12 }}>
49
+ {children}
50
+ </View>
51
+ )
52
+ }
53
+
54
+ function Page({ children }: { children: React.ReactNode }) {
55
+ return (
56
+ <View style={{ padding: 28, backgroundColor: PAGE_BG, minHeight: '100%', gap: 24 }}>
57
+ {children}
58
+ </View>
59
+ )
60
+ }
61
+
62
+ /** HERO — the across-the-room bilateral live chart, L up / R down from the centre axis. */
63
+ export const Hero: Story = {
64
+ args: { variant: 'hero', targetReps: 8, liveRepIndex: 5, height: 280, scale: 'peak' },
65
+ render: (args) => (
66
+ <Page>
67
+ <Panel width={760}>
68
+ <Text
69
+ style={{
70
+ color: primitiveColors.white,
71
+ fontFamily: '"Space Grotesk", sans-serif',
72
+ fontSize: 15,
73
+ fontWeight: '700',
74
+ }}
75
+ >
76
+ Hero — dual diverging velocity
77
+ </Text>
78
+ <DualVelocityStrip {...args} left={{ velocities: LEFT }} right={{ velocities: RIGHT }} />
79
+ </Panel>
80
+ </Page>
81
+ ),
82
+ }
83
+
84
+ /** RAIL — the same diverging language at the compact rail-expanded scale (no labels/refs). */
85
+ export const RailExpanded: Story = {
86
+ args: { variant: 'rail', targetReps: 8, height: 84 },
87
+ render: (args) => (
88
+ <Page>
89
+ <Panel width={320}>
90
+ <Text
91
+ style={{
92
+ color: primitiveColors.white,
93
+ fontFamily: '"Space Grotesk", sans-serif',
94
+ fontSize: 13,
95
+ fontWeight: '700',
96
+ }}
97
+ >
98
+ Rail expanded — same language, smaller
99
+ </Text>
100
+ <View style={{ backgroundColor: LIST_BG, borderRadius: 8, padding: 12 }}>
101
+ <DualVelocityStrip {...args} left={{ velocities: LEFT }} right={{ velocities: RIGHT }} />
102
+ </View>
103
+ </Panel>
104
+ </Page>
105
+ ),
106
+ }
107
+
108
+ /**
109
+ * A completed, narrow-panel set ending on its OWN peak on both sides (TD-07.10 repro): the last
110
+ * rep's value label has no sibling column to its right, and its bar sits at the running-best
111
+ * reference-line height — the two crowd together at the plot's right edge. Narrow enough
112
+ * (~420px panel, 8 reps/side) that the last column's bar width is under the label's estimated
113
+ * width, so the flip rule fires: the last label anchors right (grows left) instead of centering
114
+ * and bleeding past the edge.
115
+ */
116
+ const ASCENDING_LEFT = [
117
+ 0.55, 0.58, 0.6, 0.63, 0.66, 0.68, 0.71, 0.74, 0.77, 0.79, 0.82, 0.85, 0.87, 0.9,
118
+ ]
119
+ const ASCENDING_RIGHT = [
120
+ 0.53, 0.56, 0.58, 0.61, 0.64, 0.66, 0.69, 0.72, 0.75, 0.77, 0.8, 0.83, 0.85, 0.88,
121
+ ]
122
+
123
+ export const CrowdedRightEdgeLabel: Story = {
124
+ args: { variant: 'hero', height: 300, scale: 'peak' },
125
+ render: (args) => (
126
+ <Page>
127
+ <Panel width={420}>
128
+ <Text
129
+ style={{
130
+ color: primitiveColors.white,
131
+ fontFamily: '"Space Grotesk", sans-serif',
132
+ fontSize: 15,
133
+ fontWeight: '700',
134
+ }}
135
+ >
136
+ Peak on the last rep, narrow panel — the flip rule (TD-07.10)
137
+ </Text>
138
+ <DualVelocityStrip
139
+ {...args}
140
+ left={{ velocities: ASCENDING_LEFT }}
141
+ right={{ velocities: ASCENDING_RIGHT }}
142
+ />
143
+ </Panel>
144
+ </Page>
145
+ ),
146
+ parameters: {
147
+ docs: {
148
+ description: {
149
+ story:
150
+ "Before the fix, the last rep's centered label bled past the plot's right edge and sat " +
151
+ "on top of the running-best reference line. The flip rule right-anchors that column's " +
152
+ 'label instead, so it clears both.',
153
+ },
154
+ },
155
+ },
156
+ }
157
+
158
+ /** A range set on both sides — shows the cyan variable-window slots mirrored past the floor. */
159
+ export const RangeSet: Story = {
160
+ args: { variant: 'hero', height: 260, scale: 'peak' },
161
+ render: (args) => (
162
+ <Page>
163
+ <Panel width={760}>
164
+ <Text
165
+ style={{
166
+ color: primitiveColors.white,
167
+ fontFamily: '"Space Grotesk", sans-serif',
168
+ fontSize: 15,
169
+ fontWeight: '700',
170
+ }}
171
+ >
172
+ Range set — variable window mirrored both sides
173
+ </Text>
174
+ <DualVelocityStrip
175
+ {...args}
176
+ left={{ set: { type: 'range', velocities: LEFT.slice(0, 5), floor: 6, max: 10 } }}
177
+ right={{ set: { type: 'range', velocities: RIGHT.slice(0, 5), floor: 6, max: 10 } }}
178
+ />
179
+ </Panel>
180
+ </Page>
181
+ ),
182
+ }
@@ -17,8 +17,9 @@ const liveEntranceCalls = vi.hoisted(
17
17
  )
18
18
 
19
19
  vi.mock('../charts/live-rep-growth', async () => {
20
- const actual =
21
- await vi.importActual<typeof import('../charts/live-rep-growth')>('../charts/live-rep-growth')
20
+ const actual = await vi.importActual<typeof import('../charts/live-rep-growth')>(
21
+ '../charts/live-rep-growth'
22
+ )
22
23
  return {
23
24
  ...actual,
24
25
  useLiveRepGrowth: (
@@ -1,10 +1,10 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { ExerciseCard } from './ExerciseCard'
4
3
  import type { SetRowProps } from './SetRow'
4
+ import { Surface } from '../../ui/surface'
5
5
 
6
6
  const meta: Meta<typeof ExerciseCard> = {
7
- title: 'Workout/ExerciseCard',
7
+ title: 'Custom/Workout/ExerciseCard',
8
8
  component: ExerciseCard,
9
9
  tags: ['autodocs'],
10
10
  parameters: {
@@ -14,10 +14,11 @@ const meta: Meta<typeof ExerciseCard> = {
14
14
  '**Organism** (data-contract card). Three representations: `upcoming` (a dimmed, ' +
15
15
  'not-yet-reached row), collapsed (name + summary + per-set velocity strips), and expanded ' +
16
16
  '— the unified card whose persistent header is the real ' +
17
- '[ExerciseCardHeading](?path=/docs/workout-exercisecardheading--docs) over a SET · REPS · ' +
18
- 'LBS · RPE body of [SetRow](?path=/docs/workout-setrow--docs)s (compact spotlight on the ' +
17
+ '[ExerciseCardHeading](?path=/docs/custom-workout-exercisecardheading--docs) over a SET · REPS · ' +
18
+ 'LBS · RPE body of [SetRow](?path=/docs/custom-workout-setrow--docs)s (compact spotlight on the ' +
19
19
  'live set). Expand is controlled (`expanded` + `onExpandedChange`) or uncontrolled ' +
20
- '(`defaultExpanded`); `upcoming` overrides expand.',
20
+ '(`defaultExpanded`); `upcoming` overrides expand.' +
21
+ ' Composes **VelocityStrip** · **PlaceholderStrip** · **PrBadge** · **SetRow** · **SetTableHeader** · **ExerciseCardHeading**.',
21
22
  },
22
23
  },
23
24
  },
@@ -33,9 +34,9 @@ const meta: Meta<typeof ExerciseCard> = {
33
34
  },
34
35
  decorators: [
35
36
  (Story) => (
36
- <View style={{ maxWidth: 400, padding: 16, backgroundColor: '#121212' }}>
37
+ <Surface level="base" style={{ maxWidth: 400, padding: 16 }}>
37
38
  <Story />
38
- </View>
39
+ </Surface>
39
40
  ),
40
41
  ],
41
42
  }
@@ -3,7 +3,7 @@ import { render, screen, fireEvent, within } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { ExerciseCard } from './ExerciseCard'
5
5
  import type { SetRowProps } from './SetRow'
6
- import { getSemanticColors } from '../../../theme/tokens/semantic'
6
+ import { resolveColor } from '../../../theme/resolve-color'
7
7
 
8
8
  const baseCollapsedProps = {
9
9
  name: 'Bench Press',
@@ -87,8 +87,10 @@ describe('ExerciseCard', () => {
87
87
  sets: unifiedSets,
88
88
  }
89
89
 
90
- // Read from the tokens, not pinned (see SetRow.test.tsx).
91
- const { 'text-primary': T_ACTIVE, 'text-secondary': T_MUTED } = getSemanticColors('dark')
90
+ // Read from the tokens, not pinned, and through resolveColor as SetRow now
91
+ // renders them (see SetRow.test.tsx).
92
+ const T_ACTIVE = resolveColor('text-primary')
93
+ const T_MUTED = resolveColor('text-secondary')
92
94
 
93
95
  it('renders exercise name via the real ExerciseCardHeading header', () => {
94
96
  render(<ExerciseCard {...expandedProps} />)
@@ -102,6 +104,14 @@ describe('ExerciseCard', () => {
102
104
  expect(screen.getByTestId('exercise-card-body')).toBeInTheDocument()
103
105
  })
104
106
 
107
+ // No baseline covers the expanded card, so the seam's token is asserted here.
108
+ it('draws the header/body seam from the hairline-subtle token', () => {
109
+ render(<ExerciseCard {...expandedProps} />)
110
+ expect(screen.getByTestId('exercise-card-body')).toHaveStyle({
111
+ borderTopColor: resolveColor('hairline-subtle'),
112
+ })
113
+ })
114
+
105
115
  it('renders the header per-set strip derived from the set rows', () => {
106
116
  render(<ExerciseCard {...expandedProps} />)
107
117
  expect(screen.getByTestId('exercise-card-strip')).toBeInTheDocument()
@@ -1,6 +1,6 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { useState } from 'react'
3
- import { View, Text, Pressable } from 'react-native'
3
+ import { View, Pressable } from 'react-native'
4
4
  import { VelocityStrip, type VelocityZoneBandProp } from './VelocityStrip'
5
5
  import { PlaceholderStrip } from './PlaceholderStrip'
6
6
  import { PrBadge } from './PrBadge'
@@ -9,8 +9,9 @@ import { type SetStripSet } from './SetStrip'
9
9
  import { SetTableHeader } from './SetTableHeader'
10
10
  import { ExerciseCardHeading } from './ExerciseCardHeading'
11
11
  import { type ExerciseIndicatorKind } from './ExerciseIndicator'
12
+ import { Typography } from '../Typography'
12
13
  import { roundWeight } from '../../../utils/workout-format'
13
- import { getSemanticColors } from '../../../theme/tokens/semantic'
14
+ import { resolveColor } from '../../../theme/resolve-color'
14
15
 
15
16
  export interface ExerciseCardProps {
16
17
  name: string
@@ -155,30 +156,28 @@ function CollapsedCard({
155
156
  }}
156
157
  >
157
158
  <View className="flex-row items-center" testID="exercise-card-header">
158
- <Text
159
- className="text-text-primary"
160
- style={{
161
- fontSize: 14,
162
- fontFamily: '"Space Grotesk", sans-serif',
163
- fontWeight: '700',
164
- }}
159
+ {/* 14px/700 Space Grotesk is `h6` at the `sm` step; the face and the heading
160
+ role come from the variant, the weight and size from the scale. */}
161
+ <Typography
162
+ variant="h6"
163
+ color="primary"
164
+ className="text-sm font-bold leading-[normal]"
165
165
  testID="exercise-card-name"
166
166
  >
167
167
  {name}
168
- </Text>
168
+ </Typography>
169
169
  <View className="flex-1" />
170
170
  {summary && (
171
- <Text
172
- className="text-text-secondary"
173
- style={{
174
- fontSize: 12,
175
- fontFamily: 'Inter, sans-serif',
176
- marginRight: 8,
177
- }}
171
+ /* The demo's "body" face is Inter, which is the library's `font-sans`
172
+ (the mapping B1 established for PrBadge), not Typography's `font-body`. */
173
+ <Typography
174
+ variant="caption"
175
+ color="secondary"
176
+ className="font-sans mr-2 leading-[normal]"
178
177
  testID="exercise-card-summary"
179
178
  >
180
179
  {formatSummary(summary)}
181
- </Text>
180
+ </Typography>
182
181
  )}
183
182
  {isPR && (
184
183
  <View style={{ marginLeft: 4 }}>
@@ -218,10 +217,6 @@ function CollapsedCard({
218
217
  // is the real ExerciseCardHeading, and the revealed body drops PREV and renders
219
218
  // one real SetRow per set (muted done/todo, brightened live spotlight).
220
219
 
221
- const DARK = getSemanticColors('dark')
222
- /** The header↔body seam. */
223
- const BODY_DIVIDER = DARK['hairline-subtle']
224
-
225
220
  /** Project the set rows onto the heading strip's per-set state (done / active / todo). */
226
221
  function deriveHeaderSetStates(sets: SetRowProps[]): SetStripSet[] {
227
222
  return sets.map((set): SetStripSet => {
@@ -284,8 +279,13 @@ function ExpandedCard({
284
279
  testID="exercise-card-heading"
285
280
  />
286
281
 
282
+ {/* The header↔body seam. */}
287
283
  <View
288
- style={{ borderTopWidth: 1, borderTopColor: BODY_DIVIDER, paddingBottom: 6 }}
284
+ style={{
285
+ borderTopWidth: 1,
286
+ borderTopColor: resolveColor('hairline-subtle'),
287
+ paddingBottom: 6,
288
+ }}
289
289
  testID="exercise-card-body"
290
290
  >
291
291
  <SetTableHeader unit={unit} showPrevious={false} testID="exercise-card-column-headers" />
@@ -327,47 +327,38 @@ function UpcomingCard({
327
327
  >
328
328
  <View className="flex-row items-center">
329
329
  {/* Name never truncates (no numberOfLines); prescription + previousBest ellipsize first. */}
330
- <Text
331
- className="text-text-primary"
332
- style={{
333
- flexShrink: 0,
334
- fontSize: 14,
335
- fontFamily: '"Space Grotesk", sans-serif',
336
- fontWeight: '700',
337
- }}
330
+ <Typography
331
+ variant="h6"
332
+ color="primary"
333
+ className="shrink-0 text-sm font-bold leading-[normal]"
338
334
  testID="exercise-card-name"
339
335
  >
340
336
  {name}
341
- </Text>
337
+ </Typography>
342
338
  {prescription && (
343
- <Text
344
- className="text-text-secondary"
345
- numberOfLines={1}
346
- style={{
347
- flexShrink: 1,
348
- fontSize: 12,
349
- fontFamily: 'Inter, sans-serif',
350
- marginLeft: 8,
351
- }}
339
+ <Typography
340
+ variant="caption"
341
+ color="secondary"
342
+ maxLines={1}
343
+ className="font-sans shrink ml-2 leading-[normal]"
352
344
  testID="exercise-card-prescription"
353
345
  >
354
346
  {prescription}
355
- </Text>
347
+ </Typography>
356
348
  )}
357
349
  <View className="flex-1" style={{ minWidth: 8 }} />
358
350
  {previousBest && (
359
- <Text
360
- className="text-text-tertiary"
361
- numberOfLines={1}
362
- style={{
363
- flexShrink: 1,
364
- fontSize: 11,
365
- fontFamily: 'Inter, sans-serif',
366
- }}
351
+ /* 11px is off the type scale; `2xs` (10px) is the step below, as B2's
352
+ WorkoutCard date took for the same size. */
353
+ <Typography
354
+ variant="caption"
355
+ color="tertiary"
356
+ maxLines={1}
357
+ className="font-sans shrink text-2xs leading-[normal]"
367
358
  testID="exercise-card-previous-best"
368
359
  >
369
360
  {previousBest}
370
- </Text>
361
+ </Typography>
371
362
  )}
372
363
  </View>
373
364
  </View>
@@ -1,7 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { ExerciseCardHeading } from './ExerciseCardHeading'
4
3
  import type { SetStripSet } from './SetStrip'
4
+ import { Surface } from '../../ui/surface'
5
5
 
6
6
  /**
7
7
  * `ExerciseCardHeading` — the complete, standalone session-rail heading: an
@@ -10,7 +10,7 @@ import type { SetStripSet } from './SetStrip'
10
10
  * An empty `setStates` renders no strip.
11
11
  */
12
12
  const meta: Meta<typeof ExerciseCardHeading> = {
13
- title: 'Workout/ExerciseCardHeading',
13
+ title: 'Custom/Workout/ExerciseCardHeading',
14
14
  component: ExerciseCardHeading,
15
15
  tags: ['autodocs'],
16
16
  parameters: {
@@ -18,10 +18,10 @@ const meta: Meta<typeof ExerciseCardHeading> = {
18
18
  description: {
19
19
  component:
20
20
  '**Molecule.** The complete standalone session-rail heading. Composes ' +
21
- '[ExerciseHeading](?path=/docs/workout-exerciseheading--docs) + ' +
22
- '[SetStrip](?path=/docs/workout-setstrip--docs). ' +
21
+ '[ExerciseHeading](?path=/docs/custom-workout-exerciseheading--docs) + ' +
22
+ '[SetStrip](?path=/docs/custom-workout-setstrip--docs). ' +
23
23
  'Used-by ↑ [SessionRail](?path=/docs/shell-sessionrail--docs); ' +
24
- '[ExerciseCard](?path=/docs/workout-exercisecard--docs) `state="rail"` delegates here.',
24
+ '[ExerciseCard](?path=/docs/custom-workout-exercisecard--docs) `state="rail"` delegates here.',
25
25
  },
26
26
  },
27
27
  },
@@ -37,9 +37,9 @@ const meta: Meta<typeof ExerciseCardHeading> = {
37
37
  },
38
38
  decorators: [
39
39
  (Story) => (
40
- <View style={{ width: 246, backgroundColor: '#131313' }}>
40
+ <Surface level="base" style={{ width: 246 }}>
41
41
  <Story />
42
- </View>
42
+ </Surface>
43
43
  ),
44
44
  ],
45
45
  }
@@ -192,7 +192,15 @@ const vbt: ExerciseVbt = {
192
192
  const meta: Meta<typeof ExerciseDetailPage> = {
193
193
  title: 'Pages/Exercise Detail',
194
194
  component: ExerciseDetailPage,
195
- parameters: { layout: 'fullscreen' },
195
+ parameters: {
196
+ layout: 'fullscreen',
197
+ docs: {
198
+ description: {
199
+ component:
200
+ 'Composes **MesoStatusCard** · **StrengthTrendChart** · **CapacityBandChart** · **ExerciseCard** · **VelocityStrip**.',
201
+ },
202
+ },
203
+ },
196
204
  tags: ['autodocs'],
197
205
  args: {
198
206
  exercise: {
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { ExerciseHeading } from './ExerciseHeading'
3
+ import { Surface } from '../../ui/surface'
4
4
 
5
5
  /**
6
6
  * `ExerciseHeading` — the exercise-heading info block (no strip): the name +
@@ -9,7 +9,7 @@ import { ExerciseHeading } from './ExerciseHeading'
9
9
  * target; TempoDisplay is a sibling (it is itself a Pressable).
10
10
  */
11
11
  const meta: Meta<typeof ExerciseHeading> = {
12
- title: 'Workout/ExerciseHeading',
12
+ title: 'Custom/Workout/ExerciseHeading',
13
13
  component: ExerciseHeading,
14
14
  tags: ['autodocs'],
15
15
  parameters: {
@@ -17,10 +17,10 @@ const meta: Meta<typeof ExerciseHeading> = {
17
17
  description: {
18
18
  component:
19
19
  '**Molecule.** The exercise-heading info block (no strip). Composes ' +
20
- '[ExerciseIndicator](?path=/docs/workout-exerciseindicator--docs) + ' +
21
- '[SetsRepsLoad](?path=/docs/workout-setsrepsload--docs) + ' +
22
- '[TempoDisplay](?path=/docs/workout-tempodisplay--docs). ' +
23
- 'Used-by ↑ [ExerciseCardHeading](?path=/docs/workout-exercisecardheading--docs).',
20
+ '[ExerciseIndicator](?path=/docs/custom-workout-exerciseindicator--docs) + ' +
21
+ '[SetsRepsLoad](?path=/docs/custom-workout-setsrepsload--docs) + ' +
22
+ '[TempoDisplay](?path=/docs/custom-workout-tempodisplay--docs). ' +
23
+ 'Used-by ↑ [ExerciseCardHeading](?path=/docs/custom-workout-exercisecardheading--docs).',
24
24
  },
25
25
  },
26
26
  },
@@ -35,9 +35,9 @@ const meta: Meta<typeof ExerciseHeading> = {
35
35
  },
36
36
  decorators: [
37
37
  (Story) => (
38
- <View style={{ width: 246, padding: 16, backgroundColor: '#131313' }}>
38
+ <Surface level="base" style={{ width: 246, padding: 16 }}>
39
39
  <Story />
40
- </View>
40
+ </Surface>
41
41
  ),
42
42
  ],
43
43
  }
@@ -1,5 +1,6 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, Pressable } from 'react-native'
2
+ import { View, Pressable } from 'react-native'
3
+ import { Typography } from '../Typography'
3
4
  import { roundWeight } from '../../../utils/workout-format'
4
5
  import { SetsRepsLoad } from './SetsRepsLoad'
5
6
  import { TempoDisplay } from './TempoDisplay'
@@ -62,13 +63,15 @@ export function ExerciseHeading({
62
63
  style={{ gap: 8 }}
63
64
  testID="exercise-card-header"
64
65
  >
65
- <Text
66
- className="text-text-primary"
67
- style={{ fontSize: 14, fontFamily: '"Space Grotesk", sans-serif', fontWeight: '700' }}
66
+ {/* The row is a button label, not a heading, so `boldLabel` carries the weight and
67
+ no header role has to be undone. Size and the Space Grotesk face are pinned. */}
68
+ <Typography
69
+ variant="boldLabel"
70
+ className="font-heading text-sm leading-[normal]"
68
71
  testID="exercise-card-name"
69
72
  >
70
73
  {name}
71
- </Text>
74
+ </Typography>
72
75
  <View className="flex-1" />
73
76
  {indicator && <ExerciseIndicator kind={indicator} />}
74
77
  </Pressable>
@@ -6,6 +6,7 @@ import {
6
6
  INDICATOR_PRECEDENCE,
7
7
  type ExerciseIndicatorKind,
8
8
  } from './ExerciseIndicator'
9
+ import { Surface } from '../../ui/surface'
9
10
 
10
11
  /**
11
12
  * `ExerciseIndicator` — a small circular OUTLINED chip in an exercise heading's
@@ -15,7 +16,7 @@ import {
15
16
  * picks the highest-precedence active signal.
16
17
  */
17
18
  const meta: Meta<typeof ExerciseIndicator> = {
18
- title: 'Workout/ExerciseIndicator',
19
+ title: 'Custom/Workout/ExerciseIndicator',
19
20
  component: ExerciseIndicator,
20
21
  tags: ['autodocs'],
21
22
  parameters: {
@@ -26,7 +27,8 @@ const meta: Meta<typeof ExerciseIndicator> = {
26
27
  'precedence-ranked kinds (imbalance › overshoot › velocity-loss › missed-reps › pr › ' +
27
28
  'info) over four status-token tier colors; glyph = signal, color = severity tier. ' +
28
29
  '`resolveIndicator()` selects the single chip to show. Used-by ↑ ' +
29
- '[ExerciseHeading](?path=/docs/workout-exerciseheading--docs).',
30
+ '[ExerciseHeading](?path=/docs/custom-workout-exerciseheading--docs).' +
31
+ ' Composes **Icons**.',
30
32
  },
31
33
  },
32
34
  },
@@ -36,9 +38,9 @@ const meta: Meta<typeof ExerciseIndicator> = {
36
38
  },
37
39
  decorators: [
38
40
  (Story) => (
39
- <View style={{ padding: 24, backgroundColor: '#131313' }}>
41
+ <Surface level="base" style={{ padding: 24 }}>
40
42
  <Story />
41
- </View>
43
+ </Surface>
42
44
  ),
43
45
  ],
44
46
  }
@@ -7,18 +7,30 @@ import {
7
7
  INDICATOR_PRECEDENCE,
8
8
  type ExerciseIndicatorKind,
9
9
  } from './ExerciseIndicator'
10
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
10
11
 
11
- // tier color = titan status token, applied as the glyph's SVG stroke (literal hex):
12
- // danger=status-error, warning=status-warning, success=status-success, info=status-info.
13
- const kinds: { kind: ExerciseIndicatorKind; label: string; tier: string }[] = [
14
- { kind: 'imbalance', label: 'Left/right imbalance', tier: '#D14343' },
15
- { kind: 'overshoot', label: 'Load overshoot', tier: '#D14343' },
16
- { kind: 'velocity-loss', label: 'Velocity loss', tier: '#F9B415' },
17
- { kind: 'missed-reps', label: 'Missed reps', tier: '#F9B415' },
18
- { kind: 'pr', label: 'Personal record', tier: '#2ED573' },
19
- { kind: 'info', label: 'More info', tier: '#2196F3' },
12
+ type StatusToken = 'status-error' | 'status-warning' | 'status-success' | 'status-info'
13
+
14
+ // tier color = titan status token, applied as the glyph's SVG stroke. The component
15
+ // resolves it through `resolveColor`, so on web the stroke is the CSS var and the
16
+ // theme drives the value; the dark hex is asserted separately below.
17
+ const kinds: { kind: ExerciseIndicatorKind; label: string; tier: StatusToken }[] = [
18
+ { kind: 'imbalance', label: 'Left/right imbalance', tier: 'status-error' },
19
+ { kind: 'overshoot', label: 'Load overshoot', tier: 'status-error' },
20
+ { kind: 'velocity-loss', label: 'Velocity loss', tier: 'status-warning' },
21
+ { kind: 'missed-reps', label: 'Missed reps', tier: 'status-warning' },
22
+ { kind: 'pr', label: 'Personal record', tier: 'status-success' },
23
+ { kind: 'info', label: 'More info', tier: 'status-info' },
20
24
  ]
21
25
 
26
+ // The dark-mode values the tier tokens must keep resolving to (the pre-port literals).
27
+ const DARK_TIER: Record<StatusToken, string> = {
28
+ 'status-error': '#D14343',
29
+ 'status-warning': '#F9B415',
30
+ 'status-success': '#2ED573',
31
+ 'status-info': '#2196F3',
32
+ }
33
+
22
34
  describe('ExerciseIndicator', () => {
23
35
  it.each(kinds)('renders the $kind chip glyph and accessible label', ({ kind, label }) => {
24
36
  const { container } = render(<ExerciseIndicator kind={kind} />)
@@ -27,9 +39,13 @@ describe('ExerciseIndicator', () => {
27
39
  expect(screen.getByLabelText(label)).toBeInTheDocument()
28
40
  })
29
41
 
30
- it.each(kinds)('tints the $kind glyph with its tier color', ({ kind, tier }) => {
42
+ it.each(kinds)('tints the $kind glyph with its tier token', ({ kind, tier }) => {
31
43
  const { container } = render(<ExerciseIndicator kind={kind} />)
32
- expect(container.querySelector('svg')!.getAttribute('stroke')).toBe(tier)
44
+ expect(container.querySelector('svg')!.getAttribute('stroke')).toBe(`var(--color-${tier})`)
45
+ })
46
+
47
+ it.each(kinds)('keeps the $kind tier on its dark-mode value', ({ tier }) => {
48
+ expect(getSemanticColors('dark')[tier]).toBe(DARK_TIER[tier])
33
49
  })
34
50
 
35
51
  it('renders a decorative (aria-hidden) glyph so the chip owns the label', () => {