@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
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { View, Text } from 'react-native'
3
3
  import { SetStrip, type SetStripSet } from './SetStrip'
4
+ import { Surface } from '../../ui/surface'
4
5
 
5
6
  /**
6
7
  * `SetStrip` — the per-set segmented performance strip. One continuous bar per
@@ -9,7 +10,7 @@ import { SetStrip, type SetStripSet } from './SetStrip'
9
10
  * amber-300 · green-300).
10
11
  */
11
12
  const meta: Meta<typeof SetStrip> = {
12
- title: 'Workout/SetStrip',
13
+ title: 'Custom/Workout/SetStrip',
13
14
  component: SetStrip,
14
15
  tags: ['autodocs'],
15
16
  parameters: {
@@ -18,8 +19,8 @@ const meta: Meta<typeof SetStrip> = {
18
19
  component:
19
20
  '**Molecule.** The per-set segmented performance strip — one bar per set, rep ' +
20
21
  'intensities as butted ramp-pin segments (no rep gaps), active set pulses. Composes ' +
21
- '[SetBar](?path=/docs/workout-setbar--docs) × N. ' +
22
- 'Used-by ↑ [ExerciseCardHeading](?path=/docs/workout-exercisecardheading--docs).',
22
+ '[SetBar](?path=/docs/custom-workout-setbar--docs) × N. ' +
23
+ 'Used-by ↑ [ExerciseCardHeading](?path=/docs/custom-workout-exercisecardheading--docs).',
23
24
  },
24
25
  },
25
26
  },
@@ -28,9 +29,9 @@ const meta: Meta<typeof SetStrip> = {
28
29
  },
29
30
  decorators: [
30
31
  (Story) => (
31
- <View style={{ width: 210, padding: 16, backgroundColor: '#131313' }}>
32
+ <Surface level="base" style={{ width: 210, padding: 16 }}>
32
33
  <Story />
33
- </View>
34
+ </Surface>
34
35
  ),
35
36
  ],
36
37
  }
@@ -78,6 +79,60 @@ export const ShortStrip: Story = {
78
79
  },
79
80
  }
80
81
 
82
+ // ---- prescribed rep range (VMCP-03.04) — optional on `todo`/`active` only
83
+
84
+ export const RepRange: Story = {
85
+ args: {
86
+ height: 8,
87
+ sets: [
88
+ { status: 'done', velocities: decay(8, 0.9) },
89
+ { status: 'active', velocities: decay(3, 0.75), planned: 8, repsLow: 8, repsHigh: 12 },
90
+ { status: 'todo', planned: 8, repsLow: 8, repsHigh: 12 },
91
+ ],
92
+ },
93
+ parameters: {
94
+ docs: {
95
+ description: {
96
+ story:
97
+ "The plan's prescribed rep RANGE (VMCP-03.04): `8–12` on the active and todo sets. " +
98
+ "Independent of the `range` set-type's value range (isokinetic bands).",
99
+ },
100
+ },
101
+ },
102
+ }
103
+
104
+ export const RepRangeSingleNumber: Story = {
105
+ args: {
106
+ height: 8,
107
+ sets: [
108
+ { status: 'active', velocities: decay(3, 0.75), planned: 8, repsLow: 8, repsHigh: 8 },
109
+ { status: 'todo', planned: 8, repsLow: 8 },
110
+ ],
111
+ },
112
+ parameters: {
113
+ docs: {
114
+ description: {
115
+ story: 'Equal bounds (or only one given) collapse to a single number: `8`, not `8–8`.',
116
+ },
117
+ },
118
+ },
119
+ }
120
+
121
+ export const RepRangeAbsent: Story = {
122
+ args: {
123
+ height: 8,
124
+ sets: [
125
+ { status: 'active', velocities: decay(3, 0.75), planned: 8 },
126
+ { status: 'todo', planned: 8 },
127
+ ],
128
+ },
129
+ parameters: {
130
+ docs: {
131
+ description: { story: 'No prescription on the set — no label renders (the default today).' },
132
+ },
133
+ },
134
+ }
135
+
81
136
  // ---- set-type variant sheet: every variant at the real rail width, for eyeballing
82
137
  function SheetRow({ label, meta, sets }: { label: string; meta: string; sets: SetStripSet[] }) {
83
138
  return (
@@ -94,6 +94,31 @@ describe('SetStrip', () => {
94
94
  expect(screen.getAllByTestId('set-strip-set')).toHaveLength(2)
95
95
  })
96
96
 
97
+ describe('prescribed rep range (VMCP-03.04)', () => {
98
+ it('todo: renders the range when both bounds are given', () => {
99
+ render(<SetStrip sets={[{ status: 'todo', planned: 10, repsLow: 8, repsHigh: 12 }]} />)
100
+ expect(screen.getByTestId('set-strip-reps-label')).toHaveTextContent('8–12')
101
+ })
102
+
103
+ it('active: renders a single number when the bounds are equal or only one is given', () => {
104
+ const { rerender } = render(
105
+ <SetStrip
106
+ sets={[{ status: 'active', velocities: [0.9], planned: 8, repsLow: 8, repsHigh: 8 }]}
107
+ />
108
+ )
109
+ expect(screen.getByTestId('set-strip-reps-label')).toHaveTextContent('8')
110
+ rerender(
111
+ <SetStrip sets={[{ status: 'active', velocities: [0.9], planned: 8, repsLow: 8 }]} />
112
+ )
113
+ expect(screen.getByTestId('set-strip-reps-label')).toHaveTextContent('8')
114
+ })
115
+
116
+ it('renders no label when neither bound is given', () => {
117
+ render(<SetStrip sets={[{ status: 'todo', planned: 10 }]} />)
118
+ expect(screen.queryByTestId('set-strip-reps-label')).not.toBeInTheDocument()
119
+ })
120
+ })
121
+
97
122
  describe('accessibility', () => {
98
123
  it('has no accessibility violations', async () => {
99
124
  const sets: SetStripSet[] = [
@@ -1,6 +1,7 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, type ViewProps } from 'react-native'
2
+ import { View, Text, type ViewProps } from 'react-native'
3
3
  import { SetBar, type SetStripSet } from './SetBar'
4
+ import { formatRepsRange } from '../../../utils/workout-format'
4
5
 
5
6
  // Re-exported so the per-set colour vocabulary stays importable from either the
6
7
  // molecule (SetStrip) or the atom (SetBar) it now lives in.
@@ -40,6 +41,12 @@ function describeSets(sets: SetStripSet[]): string {
40
41
  return `Set progress: ${done} done, ${active} in progress, ${upcoming} upcoming`
41
42
  }
42
43
 
44
+ /** The prescribed rep-range label for a `todo`/`active` set, or `null` when unset. */
45
+ function repsRangeLabel(set: SetStripSet): string | null {
46
+ if (set.status !== 'todo' && set.status !== 'active') return null
47
+ return formatRepsRange(set.repsLow, set.repsHigh)
48
+ }
49
+
43
50
  export interface SetStripProps extends ViewProps {
44
51
  /** Per-set performance data, in set order. */
45
52
  sets: SetStripSet[]
@@ -63,9 +70,22 @@ export function SetStrip({ sets, height = 8, className, ...props }: SetStripProp
63
70
  testID="set-strip"
64
71
  {...props}
65
72
  >
66
- {sets.map((set, i) => (
67
- <SetBar key={i} set={set} height={height} />
68
- ))}
73
+ {sets.map((set, i) => {
74
+ const label = repsRangeLabel(set)
75
+ if (label == null) return <SetBar key={i} set={set} height={height} />
76
+ return (
77
+ <View key={i} style={{ flex: 1, minWidth: 0, position: 'relative' }}>
78
+ <Text
79
+ className="text-text-secondary text-2xs font-semibold text-center"
80
+ style={{ position: 'absolute', bottom: height + 3, left: 0, right: 0 }}
81
+ testID="set-strip-reps-label"
82
+ >
83
+ {label}
84
+ </Text>
85
+ <SetBar set={set} height={height} />
86
+ </View>
87
+ )
88
+ })}
69
89
  </View>
70
90
  )
71
91
  }
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { SetTableHeader } from './SetTableHeader'
3
+ import { Surface } from '../../ui/surface'
4
4
 
5
5
  /**
6
6
  * 🚧 WIP / placeholder — `SetTableHeader` (exported as `SetTableHeader`; the generic
@@ -10,7 +10,7 @@ import { SetTableHeader } from './SetTableHeader'
10
10
  * unification (TD-03.56).
11
11
  */
12
12
  const meta: Meta<typeof SetTableHeader> = {
13
- title: 'Workout/SetTableHeader',
13
+ title: 'Custom/Workout/SetTableHeader',
14
14
  component: SetTableHeader,
15
15
  tags: ['autodocs'],
16
16
  parameters: {
@@ -18,8 +18,8 @@ const meta: Meta<typeof SetTableHeader> = {
18
18
  description: {
19
19
  component:
20
20
  '**Atom.** The expanded-set-table column header row; per-column widths mirror ' +
21
- '[SetRow](?path=/docs/workout-setrow--docs) so headers align over the rows. ' +
22
- 'Used-by ↑ [ExerciseCard](?path=/docs/workout-exercisecard--docs) (expanded), which ' +
21
+ '[SetRow](?path=/docs/custom-workout-setrow--docs) so headers align over the rows. ' +
22
+ 'Used-by ↑ [ExerciseCard](?path=/docs/custom-workout-exercisecard--docs) (expanded), which ' +
23
23
  'renders it with `showPrevious={false}`. ' +
24
24
  'Column set / widths provisional pending TD-03.56 responsive unification.',
25
25
  },
@@ -31,9 +31,9 @@ const meta: Meta<typeof SetTableHeader> = {
31
31
  },
32
32
  decorators: [
33
33
  (Story) => (
34
- <View style={{ width: 320, backgroundColor: '#131313' }}>
34
+ <Surface level="base" style={{ width: 320 }}>
35
35
  <Story />
36
- </View>
36
+ </Surface>
37
37
  ),
38
38
  ],
39
39
  }
@@ -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 } from 'react-native'
2
+ import { View } from 'react-native'
3
+ import { Typography } from '../Typography'
3
4
 
4
5
  // One header column: its label and its fixed width (PREV is the lone flex column,
5
6
  // `undefined`). Order + widths mirror SetRow's cells so headers align over rows.
@@ -19,6 +20,11 @@ export interface SetTableHeaderProps {
19
20
  // distribute across the full strip width via `justifyContent: space-between`.
20
21
  const PREV_COLUMN: Column = { label: 'PREV', width: undefined }
21
22
 
23
+ // `microLabel` is the 10px step of the scale (TOKENS.md §4), in the same Inter face as
24
+ // the table's value cells. Only the row's line box is pinned: the raw <Text> this
25
+ // replaced carried no lineHeight, and the variant's `leading-normal` would grow the row.
26
+ const COLUMN_LABEL = 'leading-[normal]'
27
+
22
28
  /** Columns in order, with the weight column reflecting `unit`; PREV dropped when hidden. */
23
29
  function buildColumns(unit: 'lbs' | 'kg', showPrevious: boolean): Column[] {
24
30
  return [
@@ -68,18 +74,9 @@ export function SetTableHeader({
68
74
  }}
69
75
  {...(isFlex ? { className: 'flex-1' } : {})}
70
76
  >
71
- <Text
72
- className="text-text-tertiary"
73
- style={{
74
- fontSize: 10,
75
- fontWeight: '600',
76
- fontFamily: 'Inter, sans-serif',
77
- textTransform: 'uppercase',
78
- letterSpacing: 0.8,
79
- }}
80
- >
77
+ <Typography variant="microLabel" color="tertiary" className={COLUMN_LABEL}>
81
78
  {label}
82
- </Text>
79
+ </Typography>
83
80
  </View>
84
81
  )
85
82
  })}
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { SetsRepsLoad } from './SetsRepsLoad'
3
+ import { Surface } from '../../ui/surface'
4
4
 
5
5
  /**
6
6
  * `SetsRepsLoad` — the `sets × reps @ load` prescription line in the TempoDisplay
@@ -8,7 +8,7 @@ import { SetsRepsLoad } from './SetsRepsLoad'
8
8
  * separators).
9
9
  */
10
10
  const meta: Meta<typeof SetsRepsLoad> = {
11
- title: 'Workout/SetsRepsLoad',
11
+ title: 'Custom/Workout/SetsRepsLoad',
12
12
  component: SetsRepsLoad,
13
13
  tags: ['autodocs'],
14
14
  parameters: {
@@ -16,17 +16,17 @@ const meta: Meta<typeof SetsRepsLoad> = {
16
16
  description: {
17
17
  component:
18
18
  '**Atom.** The `sets × reps @ load` prescription line in the TempoDisplay visual ' +
19
- 'language. Composes [MetricCell](?path=/docs/workout-metriccell--docs) ' +
20
- '(shared with [TempoDisplay](?path=/docs/workout-tempodisplay--docs)). ' +
21
- 'Used-by ↑ [ExerciseHeading](?path=/docs/workout-exerciseheading--docs).',
19
+ 'language. Composes [MetricCell](?path=/docs/custom-workout-metriccell--docs) ' +
20
+ '(shared with [TempoDisplay](?path=/docs/custom-workout-tempodisplay--docs)). ' +
21
+ 'Used-by ↑ [ExerciseHeading](?path=/docs/custom-workout-exerciseheading--docs).',
22
22
  },
23
23
  },
24
24
  },
25
25
  decorators: [
26
26
  (Story) => (
27
- <View style={{ padding: 16, backgroundColor: '#131313' }}>
27
+ <Surface level="base" style={{ padding: 16 }}>
28
28
  <Story />
29
- </View>
29
+ </Surface>
30
30
  ),
31
31
  ],
32
32
  }
@@ -1,10 +1,8 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { View, type ViewProps } from 'react-native'
3
- import { getSemanticColors } from '../../../theme/tokens/semantic'
3
+ import { useOnSurfaceColor } from '../../ui/surface/SurfaceContext'
4
4
  import { MetricCell } from './metricText'
5
5
 
6
- const t = getSemanticColors('dark')
7
-
8
6
  const TIMES = '×' // × multiplication sign, padded by muted separators
9
7
  const AT = '@'
10
8
 
@@ -34,8 +32,8 @@ export function SetsRepsLoad({
34
32
  className,
35
33
  ...props
36
34
  }: SetsRepsLoadProps) {
37
- const value = t['text-primary']
38
- const sep = t['text-tertiary']
35
+ const value = useOnSurfaceColor('primary')
36
+ const sep = useOnSurfaceColor('tertiary')
39
37
 
40
38
  return (
41
39
  <View
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { Sparkline } from './Sparkline'
4
4
 
5
5
  const meta: Meta<typeof Sparkline> = {
6
- title: 'Workout/DataViz/Sparkline',
6
+ title: 'Custom/Workout/DataViz/Sparkline',
7
7
  component: Sparkline,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'
2
2
  import { render, screen } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { Sparkline } from './Sparkline'
5
+ import { resolveColor } from '../../../theme/resolve-color'
5
6
 
6
7
  const sampleData = [10, 25, 15, 30, 20]
7
8
 
@@ -19,9 +20,7 @@ describe('Sparkline', () => {
19
20
  it('renders line segments between points', () => {
20
21
  render(<Sparkline data={sampleData} />)
21
22
  for (let i = 1; i < sampleData.length; i++) {
22
- expect(
23
- screen.getByTestId(`sparkline-segment-${i}`),
24
- ).toBeInTheDocument()
23
+ expect(screen.getByTestId(`sparkline-segment-${i}`)).toBeInTheDocument()
25
24
  }
26
25
  })
27
26
 
@@ -40,9 +39,7 @@ describe('Sparkline', () => {
40
39
  it('highlights last dot when highlightLast is true', () => {
41
40
  render(<Sparkline data={sampleData} highlightLast />)
42
41
  const lastIndex = sampleData.length - 1
43
- expect(
44
- screen.getByTestId(`sparkline-dot-${lastIndex}`),
45
- ).toBeInTheDocument()
42
+ expect(screen.getByTestId(`sparkline-dot-${lastIndex}`)).toBeInTheDocument()
46
43
  // Other dots should not be present (showDots is false)
47
44
  expect(screen.queryByTestId('sparkline-dot-0')).not.toBeInTheDocument()
48
45
  })
@@ -62,7 +59,7 @@ describe('Sparkline', () => {
62
59
  { value: 20, color: '#ff0000' },
63
60
  { value: 25, color: '#00ff00', dashed: true },
64
61
  ]}
65
- />,
62
+ />
66
63
  )
67
64
  expect(screen.getByTestId('sparkline-reference-0')).toBeInTheDocument()
68
65
  expect(screen.getByTestId('sparkline-reference-1')).toBeInTheDocument()
@@ -70,9 +67,36 @@ describe('Sparkline', () => {
70
67
 
71
68
  it('does not render reference lines when not provided', () => {
72
69
  render(<Sparkline data={sampleData} />)
73
- expect(
74
- screen.queryByTestId('sparkline-reference-0'),
75
- ).not.toBeInTheDocument()
70
+ expect(screen.queryByTestId('sparkline-reference-0')).not.toBeInTheDocument()
71
+ })
72
+
73
+ it('defaults the trace to the brand-primary token', () => {
74
+ render(<Sparkline data={sampleData} highlightLast />)
75
+ expect(screen.getByTestId('sparkline-segment-1')).toHaveStyle({
76
+ backgroundColor: resolveColor('brand-primary'),
77
+ })
78
+ expect(screen.getByTestId('sparkline-dot-4')).toHaveStyle({
79
+ backgroundColor: resolveColor('brand-primary'),
80
+ })
81
+ })
82
+
83
+ it('lets a caller override the trace colour', () => {
84
+ render(<Sparkline data={sampleData} color="#00ff00" />)
85
+ expect(screen.getByTestId('sparkline-segment-1')).toHaveStyle({
86
+ backgroundColor: '#00ff00',
87
+ })
88
+ })
89
+
90
+ it('renders a reference-line label in the caller colour', () => {
91
+ render(
92
+ <Sparkline
93
+ data={sampleData}
94
+ referenceLines={[{ value: 20, color: '#ff0000', label: 'MAV' }]}
95
+ />
96
+ )
97
+ const label = screen.getByTestId('sparkline-reference-label-0')
98
+ expect(label).toHaveTextContent('MAV')
99
+ expect(label).toHaveStyle({ color: '#ff0000' })
76
100
  })
77
101
 
78
102
  it('uses default dimensions', () => {
@@ -89,23 +113,17 @@ describe('Sparkline', () => {
89
113
 
90
114
  it('sets accessibility label', () => {
91
115
  render(<Sparkline data={sampleData} />)
92
- expect(
93
- screen.getByLabelText('Sparkline chart with 5 data points'),
94
- ).toBeInTheDocument()
116
+ expect(screen.getByLabelText('Sparkline chart with 5 data points')).toBeInTheDocument()
95
117
  })
96
118
 
97
119
  it('sets empty accessibility label for no data', () => {
98
120
  render(<Sparkline data={[]} />)
99
- expect(
100
- screen.getByLabelText('Sparkline chart, no data'),
101
- ).toBeInTheDocument()
121
+ expect(screen.getByLabelText('Sparkline chart, no data')).toBeInTheDocument()
102
122
  })
103
123
 
104
124
  describe('accessibility', () => {
105
125
  it('has no accessibility violations', async () => {
106
- const { container } = render(
107
- <Sparkline data={sampleData} showDots highlightLast />,
108
- )
126
+ const { container } = render(<Sparkline data={sampleData} showDots highlightLast />)
109
127
  const results = await axe(container)
110
128
  expect(results).toHaveNoViolations()
111
129
  })
@@ -1,10 +1,8 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import React from 'react'
3
- import { View, Text, type ViewProps } from 'react-native'
2
+ import { View, type ViewProps } from 'react-native'
4
3
  import { cn } from '../../../utils/cn'
5
- import { getSemanticColors } from '../../../theme/tokens/semantic'
6
-
7
- const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
4
+ import { Typography } from '../Typography'
5
+ import { resolveColor } from '../../../theme/resolve-color'
8
6
 
9
7
  export interface SparklineProps extends ViewProps {
10
8
  data: number[]
@@ -22,10 +20,7 @@ export interface SparklineProps extends ViewProps {
22
20
  className?: string
23
21
  }
24
22
 
25
- function normalizeData(
26
- data: number[],
27
- height: number,
28
- ): number[] {
23
+ function normalizeData(data: number[], height: number): number[] {
29
24
  if (data.length === 0) return []
30
25
  const min = Math.min(...data)
31
26
  const max = Math.max(...data)
@@ -33,11 +28,7 @@ function normalizeData(
33
28
  return data.map((v) => height - ((v - min) / range) * height)
34
29
  }
35
30
 
36
- function normalizeValue(
37
- value: number,
38
- data: number[],
39
- height: number,
40
- ): number {
31
+ function normalizeValue(value: number, data: number[], height: number): number {
41
32
  const min = Math.min(...data)
42
33
  const max = Math.max(...data)
43
34
  const range = max - min || 1
@@ -67,7 +58,7 @@ export function Sparkline({
67
58
  )
68
59
  }
69
60
 
70
- const resolvedColor = color ?? BRAND_PRIMARY
61
+ const resolvedColor = color ?? resolveColor('brand-primary')
71
62
  const normalized = normalizeData(data, height)
72
63
  const stepX = data.length > 1 ? width / (data.length - 1) : 0
73
64
 
@@ -112,19 +103,24 @@ export function Sparkline({
112
103
  testID={`sparkline-reference-${i}`}
113
104
  >
114
105
  {line.label && (
115
- <Text
106
+ // `3xs` (9px) is the scale floor; the label was 7px, which is off it
107
+ // entirely (TOKENS.md §4). The line box stays unpinned, as the raw
108
+ // <Text> this replaced was, so the absolute offset still lands.
109
+ <Typography
110
+ variant="caption"
111
+ color="inherit"
112
+ className="text-3xs leading-[normal]"
116
113
  style={{
117
114
  position: 'absolute',
118
115
  right: 0,
119
116
  top: -10,
120
- fontSize: 7,
121
117
  color: line.color,
122
118
  opacity: 1,
123
119
  }}
124
120
  testID={`sparkline-reference-label-${i}`}
125
121
  >
126
122
  {line.label}
127
- </Text>
123
+ </Typography>
128
124
  )}
129
125
  </View>
130
126
  )
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { StatusDot } from './StatusDot'
4
4
 
5
5
  const meta: Meta<typeof StatusDot> = {
6
- title: 'Components/Atoms/StatusDot',
6
+ title: 'Custom/Workout/StatusDot',
7
7
  component: StatusDot,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -2,6 +2,16 @@ import { describe, it, expect } from 'vitest'
2
2
  import { render, screen } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { StatusDot } from './StatusDot'
5
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
6
+ import { greyRamp } from '../../../theme/tokens/primitives'
7
+ import { hexToRgb } from '../../../theme/color-utils'
8
+ import { resolveColor } from '../../../theme/resolve-color'
9
+
10
+ /** The `rgba(r, g, b` prefix getGlowShadow emits for a token colour. */
11
+ function glowRgb(hex: string): string {
12
+ const { r, g, b } = hexToRgb(hex)!
13
+ return `rgba(${r}, ${g}, ${b}`
14
+ }
5
15
 
6
16
  describe('StatusDot', () => {
7
17
  it('renders a dot', () => {
@@ -50,6 +60,23 @@ describe('StatusDot', () => {
50
60
  })
51
61
 
52
62
  describe('solid variants', () => {
63
+ it.each([
64
+ ['success', 'status-success'],
65
+ ['warning', 'status-warning'],
66
+ ['error', 'status-error'],
67
+ ['neutral', 'text-tertiary'],
68
+ ] as const)('fills the %s dot from the %s token', (variant, token) => {
69
+ render(<StatusDot variant={variant} />)
70
+ expect(screen.getByTestId('status-dot')).toHaveStyle({
71
+ backgroundColor: resolveColor(token),
72
+ })
73
+ })
74
+
75
+ it('draws the glyph on a solid fill in text-inverse', () => {
76
+ render(<StatusDot variant="success" size="md" icon="check" />)
77
+ expect(screen.getByText('✓')).toHaveStyle({ color: resolveColor('text-inverse') })
78
+ })
79
+
53
80
  it('renders all solid variant types without error', () => {
54
81
  const { rerender } = render(<StatusDot variant="success" />)
55
82
  expect(screen.getByTestId('status-dot')).toBeInTheDocument()
@@ -84,6 +111,18 @@ describe('StatusDot', () => {
84
111
  expect(screen.getByLabelText('future status')).toBeInTheDocument()
85
112
  })
86
113
 
114
+ // The rim is a whole rung above the wash. Asserting both rules out the
115
+ // flattening that collapses a two-step ring onto one `-subtle`.
116
+ it.each([
117
+ ['on-track', 'status-success'],
118
+ ['deviation', 'status-warning'],
119
+ ] as const)('washes the %s ring with %s -subtle inside a -muted rim', (variant, role) => {
120
+ render(<StatusDot variant={variant} />)
121
+ const dot = screen.getByTestId('status-dot')
122
+ expect(dot).toHaveStyle({ backgroundColor: resolveColor(`${role}-subtle`) })
123
+ expect(dot.style.borderTopColor).toBe(resolveColor(`${role}-muted`))
124
+ })
125
+
87
126
  it('renders the future variant with a 1px dashed border', () => {
88
127
  render(<StatusDot variant="future" />)
89
128
  const dot = screen.getByTestId('status-dot')
@@ -103,19 +142,19 @@ describe('StatusDot', () => {
103
142
  it('applies a box-shadow when glow is set on a solid variant', () => {
104
143
  render(<StatusDot variant="success" glow />)
105
144
  const dot = screen.getByTestId('status-dot')
106
- expect(dot.style.boxShadow).toContain('rgba(46,213,115,0.4)')
145
+ expect(dot.style.boxShadow).toContain(glowRgb(getSemanticColors('dark')['status-success']))
107
146
  })
108
147
 
109
148
  it('applies a box-shadow when glow is set on a ring variant', () => {
110
149
  render(<StatusDot variant="on-track" glow />)
111
150
  const dot = screen.getByTestId('status-dot')
112
- expect(dot.style.boxShadow).toContain('rgba(46,213,115,0.4)')
151
+ expect(dot.style.boxShadow).toContain(glowRgb(getSemanticColors('dark')['status-success']))
113
152
  })
114
153
 
115
154
  it('applies a gray box-shadow when glow is set on the future variant', () => {
116
155
  render(<StatusDot variant="future" glow />)
117
156
  const dot = screen.getByTestId('status-dot')
118
- expect(dot.style.boxShadow).toContain('rgba(107,114,128,0.4)')
157
+ expect(dot.style.boxShadow).toContain(glowRgb(greyRamp[500]))
119
158
  })
120
159
 
121
160
  it('does not apply a box-shadow when glow is not set', () => {
@@ -128,7 +167,7 @@ describe('StatusDot', () => {
128
167
  describe('accessibility', () => {
129
168
  it('has no accessibility violations', async () => {
130
169
  const { container } = render(
131
- <StatusDot variant="success" size="md" icon="check" label="On track" />,
170
+ <StatusDot variant="success" size="md" icon="check" label="On track" />
132
171
  )
133
172
  const results = await axe(container)
134
173
  expect(results).toHaveNoViolations()
@@ -136,7 +175,7 @@ describe('StatusDot', () => {
136
175
 
137
176
  it('has no accessibility violations for ring variants', async () => {
138
177
  const { container } = render(
139
- <StatusDot variant="on-track" size="md" icon="check" label="On track" />,
178
+ <StatusDot variant="on-track" size="md" icon="check" label="On track" />
140
179
  )
141
180
  const results = await axe(container)
142
181
  expect(results).toHaveNoViolations()