@titan-design/react-ui 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (531) hide show
  1. package/README.md +60 -47
  2. package/dist/bodymap.js +856 -736
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +858 -738
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1431 -237
  7. package/dist/index.d.ts +1431 -237
  8. package/dist/index.js +6506 -4781
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +6280 -4642
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
  13. package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1919 -1937
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1927 -1945
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +225 -140
  21. package/dist/theme/index.d.ts +225 -140
  22. package/dist/theme/index.js +298 -319
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +283 -315
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +145 -24
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +145 -24
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +128 -11
  31. package/dist/theme/tokens.d.ts +128 -11
  32. package/dist/theme/tokens.js +126 -26
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +124 -27
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/audits/2026-09-08-critique-addenda.md +51 -0
  37. package/docs/audits/2026-09-08-library-critique.md +212 -0
  38. package/docs/library-roadmap.md +99 -0
  39. package/docs/render-testing-pattern.md +130 -0
  40. package/package.json +1 -1
  41. package/src/arch/Architecture.stories.tsx +3 -1
  42. package/src/arch/arch-graph.freshness.test.ts +52 -0
  43. package/src/arch/arch-graph.json +4989 -1583
  44. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  45. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  46. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  48. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  49. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  51. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  52. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  54. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  55. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  57. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  58. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  60. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  61. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  63. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  64. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  66. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  67. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  69. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  70. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  71. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  73. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  74. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  76. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  77. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  78. package/src/components/custom/ActiveWork/README.md +305 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  80. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  81. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  82. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  83. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  84. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  86. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  87. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  88. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  90. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  91. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  92. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  93. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  94. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  95. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  96. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  97. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  98. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  99. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  100. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  101. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  102. package/src/components/custom/ActiveWork/index.ts +19 -0
  103. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  104. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  105. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  106. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  107. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
  108. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  109. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  110. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  111. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  112. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  113. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  116. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  117. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  119. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  120. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  121. package/src/components/custom/Fatigue/README.md +40 -3
  122. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  123. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  124. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  125. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  126. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  127. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  128. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  129. package/src/components/custom/Fatigue/index.ts +17 -5
  130. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  131. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  132. package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
  133. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  134. package/src/components/custom/Gauge/Gauge.tsx +7 -2
  135. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  136. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  137. package/src/components/custom/Metric/Metric.tsx +5 -20
  138. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  139. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  140. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  141. package/src/components/custom/Prose/README.md +58 -0
  142. package/src/components/custom/Prose/index.ts +1 -0
  143. package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
  144. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  145. package/src/components/custom/Scatter/Scatter.tsx +122 -18
  146. package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
  147. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  148. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  149. package/src/components/custom/Table/Table.stories.tsx +99 -16
  150. package/src/components/custom/Table/Table.test.tsx +273 -86
  151. package/src/components/custom/Table/Table.tsx +159 -67
  152. package/src/components/custom/Table/column-fit.test.ts +75 -0
  153. package/src/components/custom/Table/column-fit.ts +116 -0
  154. package/src/components/custom/Table/index.ts +9 -0
  155. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  156. package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
  157. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  158. package/src/components/custom/Treemap/Treemap.tsx +11 -3
  159. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  160. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  161. package/src/components/custom/Typography/Typography.tsx +6 -0
  162. package/src/components/custom/Typography/index.ts +1 -8
  163. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
  164. package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
  165. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  166. package/src/components/custom/Workout/BaseBadge.tsx +6 -2
  167. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  168. package/src/components/custom/Workout/BodyMap.tsx +8 -4
  169. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
  170. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  171. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
  172. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
  173. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  174. package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
  175. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  176. package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
  177. package/src/components/custom/Workout/DeviationBar.tsx +24 -16
  178. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
  179. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
  180. package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
  181. package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
  182. package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
  183. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
  184. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
  185. package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
  186. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  187. package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
  188. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  189. package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
  190. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
  191. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  192. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  193. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  194. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  195. package/src/components/custom/Workout/InputBar.tsx +4 -2
  196. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  197. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  198. package/src/components/custom/Workout/IntensityBar.tsx +8 -3
  199. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  200. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  201. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  202. package/src/components/custom/Workout/MesoCard.tsx +14 -10
  203. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  204. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  205. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  206. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  207. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  208. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
  209. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  210. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  211. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  212. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  213. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
  214. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  215. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  216. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  217. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  218. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  219. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
  220. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  221. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  222. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  223. package/src/components/custom/Workout/README.md +68 -17
  224. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  225. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  226. package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
  227. package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
  228. package/src/components/custom/Workout/RestTimer.tsx +4 -2
  229. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  230. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  231. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  232. package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
  233. package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
  234. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  235. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  236. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  237. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  238. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  239. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  240. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  241. package/src/components/custom/Workout/SetBar.tsx +38 -12
  242. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  243. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  244. package/src/components/custom/Workout/SetRow.tsx +61 -23
  245. package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
  246. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  247. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  248. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  249. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  250. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  251. package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
  252. package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
  253. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  254. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  255. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  256. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  257. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  258. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  259. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  260. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  261. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
  262. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  263. package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
  264. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  265. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  266. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  267. package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
  268. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  269. package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
  270. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  271. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  272. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
  273. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
  274. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
  275. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  276. package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
  277. package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
  278. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  279. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  280. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  281. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  282. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  283. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  284. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  285. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  286. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  287. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  288. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  289. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  290. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  291. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  292. package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
  293. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  294. package/src/components/custom/Workout/exerciseRowState.ts +29 -0
  295. package/src/components/custom/Workout/index.ts +17 -2
  296. package/src/components/custom/Workout/metricText.stories.tsx +15 -13
  297. package/src/components/custom/Workout/metricText.tsx +8 -2
  298. package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
  299. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  300. package/src/components/custom/charts/README.md +66 -0
  301. package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
  302. package/src/components/custom/charts/SetBarChart.tsx +56 -9
  303. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  304. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  305. package/src/components/custom/charts/SparkBars.tsx +85 -0
  306. package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
  307. package/src/components/custom/charts/flatBarGeometry.ts +32 -0
  308. package/src/components/custom/charts/index.ts +3 -0
  309. package/src/components/custom/index.ts +3 -0
  310. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  311. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  312. package/src/components/custom/stepper/Stepper.tsx +21 -34
  313. package/src/components/icons/icons.stories.tsx +1 -1
  314. package/src/components/icons/icons.tsx +51 -0
  315. package/src/components/shell/AppShell.stories.tsx +150 -0
  316. package/src/components/shell/AppShell.test.tsx +70 -0
  317. package/src/components/shell/AppShell.tsx +90 -0
  318. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  319. package/src/components/shell/BrandLockup.test.tsx +34 -1
  320. package/src/components/shell/BrandLockup.tsx +27 -11
  321. package/src/components/shell/NavItem.stories.tsx +4 -4
  322. package/src/components/shell/NavItem.test.tsx +17 -0
  323. package/src/components/shell/NavItem.tsx +17 -5
  324. package/src/components/shell/README.md +89 -26
  325. package/src/components/shell/SideNav.stories.tsx +38 -7
  326. package/src/components/shell/SideNav.test.tsx +16 -8
  327. package/src/components/shell/SideNav.tsx +17 -17
  328. package/src/components/shell/TopBar.stories.tsx +44 -19
  329. package/src/components/shell/TopBar.test.tsx +34 -12
  330. package/src/components/shell/TopBar.tsx +61 -40
  331. package/src/components/shell/brands.tsx +66 -0
  332. package/src/components/shell/index.ts +9 -8
  333. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  334. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  335. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  336. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  337. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  338. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  339. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  340. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  341. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  342. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  343. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  344. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  345. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  346. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  347. package/src/components/shell/workout/index.ts +9 -0
  348. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  349. package/src/components/ui/README.md +110 -0
  350. package/src/components/ui/alert/Alert.stories.tsx +15 -5
  351. package/src/components/ui/alert/Alert.tsx +24 -3
  352. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  353. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  354. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  355. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  356. package/src/components/ui/avatar/Avatar.tsx +2 -10
  357. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  358. package/src/components/ui/badge/Badge.test.tsx +19 -3
  359. package/src/components/ui/badge/Badge.tsx +48 -55
  360. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  361. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  362. package/src/components/ui/button/Button.stories.tsx +46 -12
  363. package/src/components/ui/button/Button.tsx +44 -37
  364. package/src/components/ui/card/Card.stories.tsx +90 -94
  365. package/src/components/ui/card/Card.test.tsx +80 -6
  366. package/src/components/ui/card/Card.tsx +150 -203
  367. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  368. package/src/components/ui/checkbox/Checkbox.tsx +7 -27
  369. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  370. package/src/components/ui/chip/Chip.test.tsx +10 -6
  371. package/src/components/ui/chip/Chip.tsx +76 -91
  372. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  373. package/src/components/ui/collapse/Collapse.tsx +6 -11
  374. package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
  375. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  376. package/src/components/ui/data-row/DataRow.tsx +18 -7
  377. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  378. package/src/components/ui/divider/Divider.tsx +1 -5
  379. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  380. package/src/components/ui/drawer/Drawer.tsx +13 -27
  381. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  382. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  383. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  384. package/src/components/ui/form-field/FormField.tsx +10 -46
  385. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  386. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  387. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  388. package/src/components/ui/help-tip/index.ts +7 -1
  389. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  390. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  391. package/src/components/ui/icon-box/IconBox.tsx +10 -18
  392. package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
  393. package/src/components/ui/indicator/Indicator.tsx +20 -14
  394. package/src/components/ui/input/Input.stories.tsx +3 -11
  395. package/src/components/ui/input/Input.test.tsx +2 -10
  396. package/src/components/ui/input/Input.tsx +10 -25
  397. package/src/components/ui/input/index.ts +7 -1
  398. package/src/components/ui/link/Link.stories.tsx +19 -7
  399. package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
  400. package/src/components/ui/list-item/ListItem.tsx +2 -9
  401. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  402. package/src/components/ui/menu/Menu.test.tsx +2 -11
  403. package/src/components/ui/menu/Menu.tsx +12 -14
  404. package/src/components/ui/menu/index.ts +8 -1
  405. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  406. package/src/components/ui/modal/Modal.tsx +31 -19
  407. package/src/components/ui/pill/Pill.stories.tsx +111 -23
  408. package/src/components/ui/pill/Pill.test.tsx +101 -3
  409. package/src/components/ui/pill/Pill.tsx +168 -49
  410. package/src/components/ui/pill/index.ts +1 -1
  411. package/src/components/ui/popover/Popover.stories.tsx +6 -8
  412. package/src/components/ui/popover/Popover.test.tsx +1 -6
  413. package/src/components/ui/popover/Popover.tsx +20 -15
  414. package/src/components/ui/popover/index.ts +7 -1
  415. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  416. package/src/components/ui/progress/Progress.test.tsx +1 -5
  417. package/src/components/ui/progress/Progress.tsx +9 -6
  418. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  419. package/src/components/ui/radio/Radio.test.tsx +6 -2
  420. package/src/components/ui/radio/Radio.tsx +23 -22
  421. package/src/components/ui/section/Section.stories.tsx +12 -21
  422. package/src/components/ui/section/Section.test.tsx +1 -6
  423. package/src/components/ui/section/Section.tsx +1 -3
  424. package/src/components/ui/section/index.ts +1 -5
  425. package/src/components/ui/select/Select.stories.tsx +3 -13
  426. package/src/components/ui/select/Select.test.tsx +2 -11
  427. package/src/components/ui/select/Select.tsx +16 -15
  428. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  429. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  430. package/src/components/ui/skeleton/index.ts +1 -7
  431. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  432. package/src/components/ui/spinner/Spinner.tsx +4 -5
  433. package/src/components/ui/stack/Stack.stories.tsx +8 -8
  434. package/src/components/ui/stack/Stack.tsx +1 -1
  435. package/src/components/ui/surface/Surface.stories.tsx +92 -33
  436. package/src/components/ui/surface/Surface.test.tsx +88 -28
  437. package/src/components/ui/surface/Surface.tsx +37 -87
  438. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  439. package/src/components/ui/surface/index.ts +8 -0
  440. package/src/components/ui/surface/resolveSurface.ts +98 -0
  441. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  442. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  443. package/src/components/ui/switch/Switch.tsx +7 -6
  444. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  445. package/src/components/ui/tabs/Tabs.tsx +8 -21
  446. package/src/components/ui/tabs/index.ts +1 -7
  447. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  448. package/src/components/ui/tile/Tile.tsx +3 -0
  449. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  450. package/src/components/ui/toast/Toast.test.tsx +2 -8
  451. package/src/components/ui/toast/Toast.tsx +49 -57
  452. package/src/components/ui/toast/index.ts +0 -1
  453. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  454. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  455. package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
  456. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  457. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  458. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  459. package/src/components/ui/tooltip/index.ts +1 -1
  460. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  461. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  462. package/src/components/ui/trigger/index.ts +2 -0
  463. package/src/stories/PresetShowcase.stories.tsx +101 -48
  464. package/src/test/no-device-internals.test.ts +3 -3
  465. package/src/test/no-raw-color.test.ts +39 -0
  466. package/src/theme/Alpha.stories.tsx +103 -0
  467. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  468. package/src/theme/ColorPalettes.stories.tsx +62 -45
  469. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  470. package/src/theme/Depth.stories.tsx +132 -65
  471. package/src/theme/DepthCalibration.stories.tsx +23 -21
  472. package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
  473. package/src/theme/ThemeProvider.test.tsx +4 -4
  474. package/src/theme/barrel.ts +7 -6
  475. package/src/theme/color-stories.coverage.test.ts +1 -1
  476. package/src/theme/color-utils.ts +41 -29
  477. package/src/theme/config.completeness.test.ts +1 -1
  478. package/src/theme/config.ts +40 -0
  479. package/src/theme/elevation.test.ts +82 -56
  480. package/src/theme/elevation.ts +77 -561
  481. package/src/theme/global.css +45 -5
  482. package/src/theme/grey-ramp.test.ts +24 -8
  483. package/src/theme/lift.test.ts +39 -0
  484. package/src/theme/lift.ts +102 -0
  485. package/src/theme/materials.test.ts +5 -2
  486. package/src/theme/materials.ts +38 -4
  487. package/src/theme/native-theming-guard.test.ts +5 -5
  488. package/src/theme/presets/apply.ts +4 -0
  489. package/src/theme/presets/audiobook.ts +5 -2
  490. package/src/theme/presets/default.ts +4 -0
  491. package/src/theme/presets/index.ts +14 -1
  492. package/src/theme/presets/types.ts +1 -1
  493. package/src/theme/semantic-contrast.test.ts +13 -4
  494. package/src/theme/status-distinctness.test.ts +66 -0
  495. package/src/theme/subtle-tone-weight.test.ts +163 -0
  496. package/src/theme/surface-planes.test.ts +37 -0
  497. package/src/theme/surface-planes.ts +68 -0
  498. package/src/theme/tailwind-var-opacity.test.ts +163 -0
  499. package/src/theme/token-export-guard.test.ts +1 -3
  500. package/src/theme/tokens/primitives.test.ts +15 -7
  501. package/src/theme/tokens/primitives.ts +98 -33
  502. package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
  503. package/src/theme/tokens/semantic.ts +124 -54
  504. package/src/theme/wash-ladder.test.ts +58 -0
  505. package/src/theme/workout-tokens.ts +1 -7
  506. package/src/types/jest-axe.d.ts +2 -6
  507. package/src/types/raw-md.d.ts +5 -0
  508. package/src/utils/colors.alpha-adoption.test.ts +408 -0
  509. package/src/utils/colors.ts +4 -4
  510. package/src/utils/form.ts +1 -3
  511. package/src/utils/index.ts +2 -7
  512. package/src/utils/number-format.ts +17 -0
  513. package/src/utils/workout-format.ts +15 -6
  514. package/src/web-jsx/jsx-runtime.ts +1 -5
  515. package/tailwind.config.js +55 -9
  516. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  517. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  518. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  519. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  520. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  521. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  522. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  523. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  524. package/src/components/shell/DashboardShell.tsx +0 -75
  525. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  526. package/src/theme/elevation.stories.tsx +0 -346
  527. package/src/utils/useTheme.ts +0 -72
  528. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  529. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  530. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  531. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -1,6 +1,8 @@
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'
5
+ import { greyRamp } from '../../../theme/tokens/primitives'
4
6
 
5
7
  /**
6
8
  * `SetStrip` — the per-set segmented performance strip. One continuous bar per
@@ -9,7 +11,7 @@ import { SetStrip, type SetStripSet } from './SetStrip'
9
11
  * amber-300 · green-300).
10
12
  */
11
13
  const meta: Meta<typeof SetStrip> = {
12
- title: 'Workout/SetStrip',
14
+ title: 'Custom/Workout/SetStrip',
13
15
  component: SetStrip,
14
16
  tags: ['autodocs'],
15
17
  parameters: {
@@ -18,8 +20,8 @@ const meta: Meta<typeof SetStrip> = {
18
20
  component:
19
21
  '**Molecule.** The per-set segmented performance strip — one bar per set, rep ' +
20
22
  '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).',
23
+ '[SetBar](?path=/docs/custom-workout-setbar--docs) × N. ' +
24
+ 'Used-by ↑ [ExerciseCardHeading](?path=/docs/custom-workout-exercisecardheading--docs).',
23
25
  },
24
26
  },
25
27
  },
@@ -28,9 +30,9 @@ const meta: Meta<typeof SetStrip> = {
28
30
  },
29
31
  decorators: [
30
32
  (Story) => (
31
- <View style={{ width: 210, padding: 16, backgroundColor: '#131313' }}>
33
+ <Surface level="base" style={{ width: 210, padding: 16 }}>
32
34
  <Story />
33
- </View>
35
+ </Surface>
34
36
  ),
35
37
  ],
36
38
  }
@@ -78,13 +80,67 @@ export const ShortStrip: Story = {
78
80
  },
79
81
  }
80
82
 
83
+ // ---- prescribed rep range (VMCP-03.04) — optional on `todo`/`active` only
84
+
85
+ export const RepRange: Story = {
86
+ args: {
87
+ height: 8,
88
+ sets: [
89
+ { status: 'done', velocities: decay(8, 0.9) },
90
+ { status: 'active', velocities: decay(3, 0.75), planned: 8, repsLow: 8, repsHigh: 12 },
91
+ { status: 'todo', planned: 8, repsLow: 8, repsHigh: 12 },
92
+ ],
93
+ },
94
+ parameters: {
95
+ docs: {
96
+ description: {
97
+ story:
98
+ "The plan's prescribed rep RANGE (VMCP-03.04): `8–12` on the active and todo sets. " +
99
+ "Independent of the `range` set-type's value range (isokinetic bands).",
100
+ },
101
+ },
102
+ },
103
+ }
104
+
105
+ export const RepRangeSingleNumber: Story = {
106
+ args: {
107
+ height: 8,
108
+ sets: [
109
+ { status: 'active', velocities: decay(3, 0.75), planned: 8, repsLow: 8, repsHigh: 8 },
110
+ { status: 'todo', planned: 8, repsLow: 8 },
111
+ ],
112
+ },
113
+ parameters: {
114
+ docs: {
115
+ description: {
116
+ story: 'Equal bounds (or only one given) collapse to a single number: `8`, not `8–8`.',
117
+ },
118
+ },
119
+ },
120
+ }
121
+
122
+ export const RepRangeAbsent: Story = {
123
+ args: {
124
+ height: 8,
125
+ sets: [
126
+ { status: 'active', velocities: decay(3, 0.75), planned: 8 },
127
+ { status: 'todo', planned: 8 },
128
+ ],
129
+ },
130
+ parameters: {
131
+ docs: {
132
+ description: { story: 'No prescription on the set — no label renders (the default today).' },
133
+ },
134
+ },
135
+ }
136
+
81
137
  // ---- set-type variant sheet: every variant at the real rail width, for eyeballing
82
138
  function SheetRow({ label, meta, sets }: { label: string; meta: string; sets: SetStripSet[] }) {
83
139
  return (
84
140
  <View style={{ gap: 6 }}>
85
141
  <View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 8 }}>
86
- <Text style={{ color: '#F3F4F6', fontSize: 13, fontWeight: '800' }}>{label}</Text>
87
- <Text style={{ color: '#6B7280', fontSize: 10, fontFamily: 'monospace' }}>{meta}</Text>
142
+ <Text style={{ color: greyRamp[50], fontSize: 13, fontWeight: '800' }}>{label}</Text>
143
+ <Text style={{ color: greyRamp[600], fontSize: 10, fontFamily: 'monospace' }}>{meta}</Text>
88
144
  </View>
89
145
  <SetStrip sets={sets} />
90
146
  </View>
@@ -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
 
@@ -17,12 +15,18 @@ export interface SetsRepsLoadProps extends ViewProps {
17
15
  unit?: string
18
16
  /** Cell font size (px). Default 11 — the compact rail/card scale. Raise for a wall read-out. */
19
17
  fontSize?: number
18
+ /**
19
+ * Flatten the whole run to one dimmer, regular-weight tone (the collapsed/upcoming
20
+ * card row) instead of the bright bold value / muted separator split (the rail).
21
+ * Also drops the `×` separator's padding spaces — "3×6", not "3 × 6".
22
+ */
23
+ muted?: boolean
20
24
  className?: string
21
25
  }
22
26
 
23
27
  /**
24
28
  * The `sets × reps @ load` prescription line, in the TempoDisplay visual language
25
- * (Inter · 600 · letter-spacing 1 · value cells with muted `×` / `@` separators).
29
+ * (Inter · letter-spacing 1 · value cells with muted `×` / `@` separators).
26
30
  * Shares the {@link MetricCell} primitive with TempoDisplay so the two stay in step.
27
31
  */
28
32
  export function SetsRepsLoad({
@@ -31,11 +35,17 @@ export function SetsRepsLoad({
31
35
  load,
32
36
  unit = 'lb',
33
37
  fontSize,
38
+ muted = false,
34
39
  className,
35
40
  ...props
36
41
  }: SetsRepsLoadProps) {
37
- const value = t['text-primary']
38
- const sep = t['text-tertiary']
42
+ const primary = useOnSurfaceColor('primary')
43
+ const tertiary = useOnSurfaceColor('tertiary')
44
+ const secondary = useOnSurfaceColor('secondary')
45
+ const value = muted ? secondary : primary
46
+ const sep = muted ? secondary : tertiary
47
+ const weight = muted ? 400 : 600
48
+ const times = muted ? TIMES : ` ${TIMES} `
39
49
 
40
50
  return (
41
51
  <View
@@ -45,18 +55,20 @@ export function SetsRepsLoad({
45
55
  testID="sets-reps-load"
46
56
  {...props}
47
57
  >
48
- <MetricCell color={value} fontSize={fontSize}>
58
+ <MetricCell color={value} fontSize={fontSize} weight={weight}>
49
59
  {sets}
50
60
  </MetricCell>
51
- <MetricCell color={sep} fontSize={fontSize}>{` ${TIMES} `}</MetricCell>
52
- <MetricCell color={value} fontSize={fontSize}>
61
+ <MetricCell color={sep} fontSize={fontSize} weight={weight}>
62
+ {times}
63
+ </MetricCell>
64
+ <MetricCell color={value} fontSize={fontSize} weight={weight}>
53
65
  {reps}
54
66
  </MetricCell>
55
- <MetricCell color={sep} fontSize={fontSize}>{` ${AT} `}</MetricCell>
56
- <MetricCell color={value} fontSize={fontSize}>
67
+ <MetricCell color={sep} fontSize={fontSize} weight={weight}>{` ${AT} `}</MetricCell>
68
+ <MetricCell color={value} fontSize={fontSize} weight={weight}>
57
69
  {load}
58
70
  </MetricCell>
59
- <MetricCell color={sep} fontSize={fontSize}>{` ${unit}`}</MetricCell>
71
+ <MetricCell color={sep} fontSize={fontSize} weight={weight}>{` ${unit}`}</MetricCell>
60
72
  </View>
61
73
  )
62
74
  }
@@ -1,9 +1,12 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { View } from 'react-native'
3
3
  import { Sparkline } from './Sparkline'
4
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
5
+
6
+ const t = getSemanticColors('dark')
4
7
 
5
8
  const meta: Meta<typeof Sparkline> = {
6
- title: 'Workout/DataViz/Sparkline',
9
+ title: 'Custom/Workout/DataViz/Sparkline',
7
10
  component: Sparkline,
8
11
  tags: ['autodocs'],
9
12
  argTypes: {
@@ -56,7 +59,7 @@ export const Descending: Story = {
56
59
  args: {
57
60
  data: descending,
58
61
  showDots: true,
59
- color: '#ff4757',
62
+ color: t['status-error-vivid'],
60
63
  },
61
64
  }
62
65
 
@@ -64,7 +67,7 @@ export const UCurve: Story = {
64
67
  args: {
65
68
  data: uCurve,
66
69
  showDots: true,
67
- color: '#2ed573',
70
+ color: t['status-success'],
68
71
  },
69
72
  }
70
73
 
@@ -72,7 +75,7 @@ export const Flat: Story = {
72
75
  args: {
73
76
  data: flat,
74
77
  showDots: true,
75
- color: '#ffd43b',
78
+ color: t['status-warning-light'],
76
79
  },
77
80
  }
78
81
 
@@ -81,8 +84,8 @@ export const WithReferenceLines: Story = {
81
84
  data: ascending,
82
85
  showDots: true,
83
86
  referenceLines: [
84
- { value: 30, color: '#ff4757', dashed: true },
85
- { value: 45, color: '#2ed573', dashed: false },
87
+ { value: 30, color: t['status-error-vivid'], dashed: true },
88
+ { value: 45, color: t['status-success'], dashed: false },
86
89
  ],
87
90
  },
88
91
  }
@@ -99,7 +102,7 @@ export const DotsAndHighlightLast: Story = {
99
102
  data: volatile,
100
103
  showDots: true,
101
104
  highlightLast: true,
102
- color: '#ffa502',
105
+ color: t['status-warning'],
103
106
  },
104
107
  }
105
108
 
@@ -116,10 +119,10 @@ export const AllShapes: Story = {
116
119
  render: () => (
117
120
  <View style={{ gap: 16, padding: 16 }}>
118
121
  <Sparkline data={ascending} showDots highlightLast />
119
- <Sparkline data={descending} showDots color="#ff4757" />
120
- <Sparkline data={uCurve} showDots color="#2ed573" />
121
- <Sparkline data={flat} showDots color="#ffd43b" />
122
- <Sparkline data={volatile} showDots color="#ffa502" highlightLast />
122
+ <Sparkline data={descending} showDots color={t['status-error-vivid']} />
123
+ <Sparkline data={uCurve} showDots color={t['status-success']} />
124
+ <Sparkline data={flat} showDots color={t['status-warning-light']} />
125
+ <Sparkline data={volatile} showDots color={t['status-warning']} highlightLast />
123
126
  </View>
124
127
  ),
125
128
  }
@@ -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: {