@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,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { View } from 'react-native'
3
- import { primitiveRamps } from '../../../theme/tokens/primitives'
3
+ import { resolveColor } from '../../../theme/resolve-color'
4
4
  import { SessionHeader, type SessionHeaderPlanEntry } from './SessionHeader'
5
5
 
6
6
  /**
@@ -21,10 +21,10 @@ const meta: Meta<typeof SessionHeader> = {
21
21
  component:
22
22
  '**Molecule** (shell S3). The session-rail heading — title, stat tiles, chunked ' +
23
23
  'pace bar, sets label + ⏱ readout, on the raised grey plane. Composes ' +
24
- '[MetricTiles](?path=/docs/workout-metrictiles--docs) / ' +
25
- '[ScheduleTiles](?path=/docs/workout-scheduletiles--docs) + ' +
26
- '[SegmentedProgressBar](?path=/docs/workout-segmentedprogressbar--docs) + ' +
27
- '[TimerReadout](?path=/docs/components-atoms-timerreadout--docs). ' +
24
+ '[MetricTiles](?path=/docs/custom-workout-metrictiles--docs) / ' +
25
+ '[ScheduleTiles](?path=/docs/custom-workout-scheduletiles--docs) + ' +
26
+ '[SegmentedProgressBar](?path=/docs/custom-workout-segmentedprogressbar--docs) + ' +
27
+ '[TimerReadout](?path=/docs/custom-timerreadout--docs). ' +
28
28
  'Used-by ↑ [SessionRail](?path=/docs/shell-sessionrail--docs).',
29
29
  },
30
30
  },
@@ -41,7 +41,7 @@ const meta: Meta<typeof SessionHeader> = {
41
41
  export default meta
42
42
  type Story = StoryObj<typeof SessionHeader>
43
43
 
44
- const AMBER = primitiveRamps.amber[300]
44
+ const AMBER = resolveColor('status-warning')
45
45
 
46
46
  const PLAN: SessionHeaderPlanEntry[] = [
47
47
  { name: 'Seated Row', sets: 3 },
@@ -1,5 +1,5 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, type ViewProps } from 'react-native'
2
+ import { View, type ViewProps } from 'react-native'
3
3
  import { Surface, onSurfaceColors, useSurfaceMode } from '../../ui/surface'
4
4
  import { Typography } from '../Typography'
5
5
  import { TimerReadout } from '../TimerReadout'
@@ -97,20 +97,18 @@ export function SessionHeader({
97
97
  testID="session-rail-header"
98
98
  {...props}
99
99
  >
100
- <Text
100
+ {/* `h6` carries the heading face and role; the rail title is 16px/700 on a
101
+ tight 18px line box, which is one step below `h6`'s own 18px/600. */}
102
+ <Typography
103
+ variant="h6"
104
+ color="inherit"
105
+ className="text-base font-bold leading-[18px]"
101
106
  accessibilityRole="header"
102
- style={{
103
- fontSize: 16,
104
- lineHeight: 18,
105
- fontWeight: '700',
106
- fontFamily: '"Space Grotesk", sans-serif',
107
- color: onSurface.primary,
108
- marginBottom: 10,
109
- }}
107
+ style={{ color: onSurface.primary, marginBottom: 10 }}
110
108
  testID="session-rail-title"
111
109
  >
112
110
  {title}
113
- </Text>
111
+ </Typography>
114
112
 
115
113
  {upcoming ? <ScheduleTiles when={next} /> : <MetricTiles metrics={metrics} />}
116
114
 
@@ -132,7 +130,9 @@ export function SessionHeader({
132
130
  >
133
131
  <Typography
134
132
  variant="mono"
135
- style={{ fontSize: 10, color: setsLabelColor }}
133
+ color="inherit"
134
+ className="text-2xs"
135
+ style={{ color: setsLabelColor }}
136
136
  testID="session-rail-sets"
137
137
  >
138
138
  {setsLabel}
@@ -1,17 +1,16 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View, Text } from 'react-native'
2
+ import { View } from 'react-native'
3
3
  import { primitiveRamps } from '../../../theme/tokens/primitives'
4
- import { getSemanticColors } from '../../../theme/tokens/semantic'
5
4
  import { SessionRail, type SessionRailExercise } from './SessionRail'
6
-
7
- const t = getSemanticColors('dark')
5
+ import { Surface } from '../../ui/surface'
6
+ import { Typography } from '../Typography'
8
7
 
9
8
  /**
10
9
  * `SessionRail` (shell organism) — the live-workout exercise list: a flat raised
11
10
  * `SessionHeader` glance (title, stat tiles, chunked pace bar) over a sunk, inset list
12
11
  * of ExerciseCard rail headings. The header plan (chunk widths ∝ sets) is derived from
13
- * `exercises`. Surfaces bind to the grey ramp; the list depth is a subtle neumorphic
14
- * inset. Driven entirely by props.
12
+ * `exercises`. Surfaces bind to the grey ramp; the list is a well cut into the rail
13
+ * with the shared `insetWell` material. Driven entirely by props.
15
14
  */
16
15
  const meta: Meta<typeof SessionRail> = {
17
16
  title: 'Shell/SessionRail',
@@ -23,12 +22,14 @@ const meta: Meta<typeof SessionRail> = {
23
22
  },
24
23
  decorators: [
25
24
  (Story) => (
26
- <View className="min-h-screen flex-row" style={{ backgroundColor: t['background-base'] }}>
25
+ <Surface level="background" className="min-h-screen flex-row">
27
26
  <Story />
28
27
  <View className="flex-1 items-center justify-center">
29
- <Text style={{ color: '#6B7280', fontSize: 12 }}>main viewport</Text>
28
+ <Typography variant="caption" color="tertiary">
29
+ main viewport
30
+ </Typography>
30
31
  </View>
31
- </View>
32
+ </Surface>
32
33
  ),
33
34
  ],
34
35
  parameters: {
@@ -37,10 +38,10 @@ const meta: Meta<typeof SessionRail> = {
37
38
  description: {
38
39
  component:
39
40
  '**Organism** (shell S3). The live-workout exercise list: raised header glance over ' +
40
- 'a sunk inset list, subtle neumorphic depth (grey ramp + `neumorphicShadows`). ' +
41
+ 'a sunk inset list (grey ramp + the `insetWell` material). ' +
41
42
  'Composes ' +
42
43
  '[SessionHeader](?path=/docs/shell-sessionrail-sessionheader--docs) + ' +
43
- '[ExerciseCardHeading](?path=/docs/workout-exercisecardheading--docs) × N. ' +
44
+ '[ExerciseCardHeading](?path=/docs/custom-workout-exercisecardheading--docs) × N. ' +
44
45
  'Sits beside [SideNav](?path=/docs/shell-sidenav--docs) in the dashboard shell.',
45
46
  },
46
47
  },
@@ -13,11 +13,11 @@ import type { SetStripSet } from './SetStrip'
13
13
  import type { ExerciseIndicatorKind } from './ExerciseIndicator'
14
14
 
15
15
  // Warm-tapered ramp surfaces (the locked S3 shell shades): the nav + the heading plane read as
16
- // ONE dark plane (background shell, #1C1916, owned by SessionHeader's `<Surface level="background">`).
17
- // The exercise list is a LIGHTER inset panel — `<Surface level="elevated">` (#2A2827)
18
- // recessed via its inner shadow yet paler than the header, so the transparent
19
- // exercise headings on it never blend into the header plane. Surface owns both backgrounds,
20
- // so the rail no longer hand-sets them.
16
+ // ONE dark plane, the shell `background`, owned by SessionHeader's `<Surface level="background">`.
17
+ // The exercise list is a well cut into the rail — `<Surface level="base">` with the shared
18
+ // `insetWell` material — recessed by its inner shadow yet paler than the header, so the
19
+ // transparent exercise headings on it never blend into the header plane. Surface owns both
20
+ // backgrounds, so the rail no longer hand-sets them.
21
21
  const SEMANTIC = getSemanticColors('dark')
22
22
  /** Row divider — one step up from the well it sits in so the line reads. */
23
23
  const DIVIDER = greyRamp[875]
@@ -128,10 +128,8 @@ export function SessionRail({
128
128
  />
129
129
 
130
130
  {/* The list is a WELL cut into the rail: one plane down from the rail's
131
- `elevated`, recessed by the shared inset material rather than by a
132
- neumorphic pressed shadow (which had no room to read at this
133
- lightness). `level` follows the well's tone so on-surface text
134
- resolves against what is actually painted. */}
131
+ `elevated`, recessed by the shared inset material. `level` follows the
132
+ well's tone so on-surface text resolves against what is painted. */}
135
133
  <Surface
136
134
  level="base"
137
135
  style={[{ flex: 1 }, insetWell(SEMANTIC['surface-base'])]}
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
- import { View } from 'react-native'
3
2
  import { SetBar, type SetStripSet } from './SetBar'
3
+ import { Surface } from '../../ui/surface'
4
4
 
5
5
  /**
6
6
  * `SetBar` — ONE set's multi-coloured bar: the butted per-rep colour segments for a
@@ -9,7 +9,7 @@ import { SetBar, type SetStripSet } from './SetBar'
9
9
  * (red-600 · orange-400 · amber-300 · green-300). {@link SetStrip} lays several side by side.
10
10
  */
11
11
  const meta: Meta<typeof SetBar> = {
12
- title: 'Workout/SetBar',
12
+ title: 'Custom/Workout/SetBar',
13
13
  component: SetBar,
14
14
  tags: ['autodocs'],
15
15
  parameters: {
@@ -18,7 +18,8 @@ const meta: Meta<typeof SetBar> = {
18
18
  component:
19
19
  '**Atom.** ONE set’s multi-coloured bar — the per-rep colour segments for a ' +
20
20
  'single set (done / active-with-pulse / todo). Colours are the real titan ramp pins. ' +
21
- 'Used-by ↑ [SetStrip](?path=/docs/workout-setstrip--docs).',
21
+ 'Used-by ↑ [SetStrip](?path=/docs/custom-workout-setstrip--docs).' +
22
+ ' Composes **SegmentedBar**.',
22
23
  },
23
24
  },
24
25
  },
@@ -27,9 +28,9 @@ const meta: Meta<typeof SetBar> = {
27
28
  },
28
29
  decorators: [
29
30
  (Story) => (
30
- <View style={{ width: 180, flexDirection: 'row', padding: 16, backgroundColor: '#131313' }}>
31
+ <Surface level="base" style={{ width: 180, flexDirection: 'row', padding: 16 }}>
31
32
  <Story />
32
- </View>
33
+ </Surface>
33
34
  ),
34
35
  ],
35
36
  }
@@ -78,7 +79,10 @@ export const Range: Story = {
78
79
  export const Drop: Story = {
79
80
  args: {
80
81
  height: 8,
81
- set: { status: 'drop', subloads: [decay(10, 0.85), decay(8, 0.7), decay(6, 0.6)] } as SetStripSet,
82
+ set: {
83
+ status: 'drop',
84
+ subloads: [decay(10, 0.85), decay(8, 0.7), decay(6, 0.6)],
85
+ } as SetStripSet,
82
86
  },
83
87
  parameters: {
84
88
  docs: {
@@ -2,6 +2,8 @@ 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 { SetBar, velocityZoneColor, SET_STRIP_ZONES, SET_STRIP_VARIABLE_COLOR } from './SetBar'
5
+ import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
6
+ import { resolveColor } from '../../../theme/resolve-color'
5
7
 
6
8
  /** The slot wrapper (carrier of the leading-gap margin) is a fill segment's parent. */
7
9
  const slotOf = (fill: HTMLElement) => fill.parentElement as HTMLElement
@@ -36,10 +38,30 @@ describe('SetBar', () => {
36
38
  expect(screen.queryByTestId('set-strip-pulse')).not.toBeInTheDocument()
37
39
  })
38
40
 
41
+ // SetBar has no visual baseline. The placeholder fill was `greyRamp[900]` — the
42
+ // `surface-elevated` plane used as a mark — so its role is asserted here.
43
+ it('fills unperformed reps from the border role, not a surface plane', () => {
44
+ render(<SetBar set={{ status: 'todo', planned: 10 }} />)
45
+ expect(screen.getByTestId('set-strip-empty')).toHaveStyle({
46
+ backgroundColor: resolveColor('border-prominent'),
47
+ })
48
+ })
49
+
50
+ it('sources the four zone pins from the canonical effort scale', () => {
51
+ expect(SET_STRIP_ZONES).toEqual({
52
+ slow: WORKOUT_TOKENS.scale.red,
53
+ moderate: WORKOUT_TOKENS.scale.orange,
54
+ fast: WORKOUT_TOKENS.scale.yellow,
55
+ fastest: WORKOUT_TOKENS.scale.green,
56
+ })
57
+ })
58
+
39
59
  describe('range (variable rep-range)', () => {
40
60
  it('renders range-max segments; committed-todo grey, variable-todo cyan', () => {
41
61
  // floor 15, max 20, 12 done → 12 velocity fills · 3 grey (committed) · 5 cyan (variable)
42
- render(<SetBar set={{ status: 'range', floor: 15, max: 20, doneVels: Array(12).fill(0.9) }} />)
62
+ render(
63
+ <SetBar set={{ status: 'range', floor: 15, max: 20, doneVels: Array(12).fill(0.9) }} />
64
+ )
43
65
  expect(screen.getAllByTestId('set-strip-fill')).toHaveLength(12)
44
66
  expect(screen.getAllByTestId('set-strip-empty')).toHaveLength(3)
45
67
  expect(screen.getAllByTestId('set-strip-variable')).toHaveLength(5)
@@ -47,7 +69,9 @@ describe('SetBar', () => {
47
69
 
48
70
  it('counts a done rep past the floor as a real velocity rep (not variable)', () => {
49
71
  // 17 done into the variable zone → 17 velocity fills, 3 cyan, 0 grey
50
- render(<SetBar set={{ status: 'range', floor: 15, max: 20, doneVels: Array(17).fill(0.8) }} />)
72
+ render(
73
+ <SetBar set={{ status: 'range', floor: 15, max: 20, doneVels: Array(17).fill(0.8) }} />
74
+ )
51
75
  expect(screen.getAllByTestId('set-strip-fill')).toHaveLength(17)
52
76
  expect(screen.getAllByTestId('set-strip-variable')).toHaveLength(3)
53
77
  expect(screen.queryByTestId('set-strip-empty')).not.toBeInTheDocument()
@@ -64,7 +88,17 @@ describe('SetBar', () => {
64
88
 
65
89
  describe('drop set', () => {
66
90
  it('renders one fill per rep across sub-loads, split by 2px notches', () => {
67
- render(<SetBar set={{ status: 'drop', subloads: [[0.85, 0.8, 0.7], [0.7, 0.6]] }} />)
91
+ render(
92
+ <SetBar
93
+ set={{
94
+ status: 'drop',
95
+ subloads: [
96
+ [0.85, 0.8, 0.7],
97
+ [0.7, 0.6],
98
+ ],
99
+ }}
100
+ />
101
+ )
68
102
  const fills = screen.getAllByTestId('set-strip-fill')
69
103
  expect(fills).toHaveLength(5)
70
104
  expect(slotOf(fills[0])).not.toHaveStyle({ marginLeft: '2px' }) // first sub-load butts the edge
@@ -74,7 +108,18 @@ describe('SetBar', () => {
74
108
 
75
109
  describe('myo-rep / rest-pause (done)', () => {
76
110
  it('renders activation + clusters, split by 3px cluster gaps', () => {
77
- render(<SetBar set={{ status: 'myo', activation: [0.75, 0.7, 0.65], clusters: [[0.6, 0.55], [0.5, 0.45]] }} />)
111
+ render(
112
+ <SetBar
113
+ set={{
114
+ status: 'myo',
115
+ activation: [0.75, 0.7, 0.65],
116
+ clusters: [
117
+ [0.6, 0.55],
118
+ [0.5, 0.45],
119
+ ],
120
+ }}
121
+ />
122
+ )
78
123
  const fills = screen.getAllByTestId('set-strip-fill')
79
124
  expect(fills).toHaveLength(7)
80
125
  expect(slotOf(fills[3])).toHaveStyle({ marginLeft: '3px' }) // first cluster
@@ -1,25 +1,38 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { primitiveRamps, greyRamp } from '../../../theme/tokens/primitives'
2
+ import { primitiveRamps } from '../../../theme/tokens/primitives'
3
+ import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
4
+ import { resolveColor } from '../../../theme/resolve-color'
3
5
  import { SegmentedBar, type SegmentedBarSegment } from './SegmentedBar'
4
6
 
5
7
  /**
6
- * The four canonical rep-intensity zone pins (real titan ramp pins). A rep's mean
7
- * velocity ratio maps to one of these; butted together they form a set's bar.
8
+ * The four canonical rep-intensity zone pins. A rep's mean velocity ratio maps to
9
+ * one of these; butted together they form a set's bar.
10
+ *
11
+ * Sourced from {@link WORKOUT_TOKENS}.`scale`, which its own docs already call the
12
+ * single source for the velocity zone bars — this used to re-read the same four
13
+ * ramp steps independently, so the two could drift apart silently. Byte-identical.
8
14
  */
9
15
  export const SET_STRIP_ZONES = {
10
- slow: primitiveRamps.red[600],
11
- moderate: primitiveRamps.orange[400],
12
- fast: primitiveRamps.amber[300],
13
- fastest: primitiveRamps.green[300],
16
+ slow: WORKOUT_TOKENS.scale.red,
17
+ moderate: WORKOUT_TOKENS.scale.orange,
18
+ fast: WORKOUT_TOKENS.scale.yellow,
19
+ fastest: WORKOUT_TOKENS.scale.green,
14
20
  } as const
15
21
 
16
- /** Grey fill for planned-but-unperformed reps / upcoming sets (grey placeholder). */
17
- const TODO_COLOR = greyRamp[900]
22
+ // Fill for planned-but-unperformed reps / upcoming sets. Was `greyRamp[900]` — the
23
+ // `surface-elevated` plane borrowed as a MARK colour, the same anti-pattern the
24
+ // specimen's `--dot-inactive` note calls out. It takes the border role instead, which
25
+ // is where B2 landed PlaceholderStrip: the two placeholder languages now match.
26
+ const TODO_COLOR = resolveColor('border-prominent')
18
27
 
19
28
  /**
20
29
  * The learned "variable / unknown / opportunity" semantic (cyan-900, a real ramp
21
30
  * pin): an unperformed rep in a range's variable zone, and the fading trail of a
22
31
  * myo set whose cluster count isn't yet known.
32
+ *
33
+ * FINDING for E3: no semantic role expresses this. `brand-secondary` is cyan-600
34
+ * and `brand-secondary-dark` cyan-700; neither reaches cyan-900, and pinning this
35
+ * to either would restyle the mark rather than migrate it.
23
36
  */
24
37
  export const SET_STRIP_VARIABLE_COLOR = primitiveRamps.cyan[900]
25
38
 
@@ -39,6 +52,10 @@ const MYO_UPCOMING_FADE = 0.6
39
52
  /**
40
53
  * Active-set pulse range per zone: each segment eases between its pin's adjacent
41
54
  * lighter/darker ramp steps, keeping the intensity reading while signalling "live".
55
+ *
56
+ * Literal ramp steps, not tokens: `Animated.interpolate` parses its own endpoints
57
+ * and cannot read a `var()` ref — the same exception SegmentedBar's `pulseColor`
58
+ * documents (B2).
42
59
  */
43
60
  const PULSE_RANGE: Record<string, [string, string]> = {
44
61
  [SET_STRIP_ZONES.slow]: [primitiveRamps.red[500], primitiveRamps.red[700]],
@@ -58,7 +75,10 @@ export function velocityZoneColor(v: number): string {
58
75
  /**
59
76
  * One set's data. Flat variants: `done` = every rep performed; `active` = reps-so-far
60
77
  * performed against a planned count (remainder greyed, performed reps pulse); `todo` =
61
- * a planned but unstarted set (solid grey bar). Set-type variants:
78
+ * a planned but unstarted set (solid grey bar). `active`/`todo` optionally carry the
79
+ * plan's prescribed rep RANGE (`repsLow`/`repsHigh`, VMCP-03.04) — a value range
80
+ * (isokinetic bands), not a rep count, so it's independent of `range`'s `floor`/`max`.
81
+ * Set-type variants:
62
82
  * - `range` — variable rep-range (e.g. 15–20): `max` segments; committed zone
63
83
  * `0..floor` (done = velocity, todo = grey), variable zone `floor..max` (done =
64
84
  * velocity, todo = the variable/opportunity cyan). `doneVels` may run past `floor`.
@@ -69,8 +89,14 @@ export function velocityZoneColor(v: number): string {
69
89
  */
70
90
  export type SetStripSet =
71
91
  | { status: 'done'; velocities: number[] }
72
- | { status: 'active'; velocities: number[]; planned: number }
73
- | { status: 'todo'; planned: number }
92
+ | {
93
+ status: 'active'
94
+ velocities: number[]
95
+ planned: number
96
+ repsLow?: number
97
+ repsHigh?: number
98
+ }
99
+ | { status: 'todo'; planned: number; repsLow?: number; repsHigh?: number }
74
100
  | { status: 'range'; floor: number; max: number; doneVels: number[] }
75
101
  | { status: 'drop'; subloads: number[][] }
76
102
  | { status: 'myo'; activation: number[]; clusters: number[][] }
@@ -4,7 +4,7 @@ import { SetRow } from './SetRow'
4
4
  import { SetTableHeader } from './SetTableHeader'
5
5
 
6
6
  const meta: Meta<typeof SetRow> = {
7
- title: 'Workout/SetRow',
7
+ title: 'Custom/Workout/SetRow',
8
8
  component: SetRow,
9
9
  tags: ['autodocs'],
10
10
  parameters: {
@@ -16,7 +16,8 @@ const meta: Meta<typeof SetRow> = {
16
16
  '(logged), `live` (performing now — brightened, compact velocity-height spotlight), `todo` ' +
17
17
  '(planned — muted grey stub). Done + upcoming share one muted treatment; only `live` is ' +
18
18
  'brightened. No PREV column. Composed by ' +
19
- '[ExerciseCard](?path=/docs/workout-exercisecard--docs) (expanded).',
19
+ '[ExerciseCard](?path=/docs/custom-workout-exercisecard--docs) (expanded).' +
20
+ ' Composes **VelocityStrip**.',
20
21
  },
21
22
  },
22
23
  },
@@ -3,13 +3,16 @@ import { render, screen } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { View } from 'react-native'
5
5
  import { SetRow, type SetRowProps } from './SetRow'
6
- import { getSemanticColors } from '../../../theme/tokens/semantic'
6
+ import { resolveColor } from '../../../theme/resolve-color'
7
7
 
8
8
  // Text treatments, read from the tokens rather than pinned as hexes. These
9
9
  // used to be hand-copied literals and silently desynced when the greys moved
10
10
  // to the warm ramp (TD-07.14). The assertion still bites — it checks the
11
11
  // component reaches for the right ROLE — it just no longer restates the value.
12
- const { 'text-primary': T_ACTIVE, 'text-secondary': T_MUTED } = getSemanticColors('dark')
12
+ // Now through `resolveColor`, which is what the row renders after the B3 port:
13
+ // the same role, but the theme-switching `var()` ref rather than a dark hex.
14
+ const T_ACTIVE = resolveColor('text-primary')
15
+ const T_MUTED = resolveColor('text-secondary')
13
16
 
14
17
  const doneRow: SetRowProps = {
15
18
  state: 'done',
@@ -116,6 +119,17 @@ describe('SetRow', () => {
116
119
  expect(screen.getByTestId('set-row-type-badge')).toHaveTextContent('W')
117
120
  })
118
121
 
122
+ // SetRow has no visual baseline, so the chip's tokens are asserted here. It
123
+ // used to borrow `WORKOUT_TOKENS.scale.orange` — the EFFORT scale — for a
124
+ // label that reads set type, over a hand-mixed 0.12 wash off a different hue.
125
+ it('draws the type badge from the brand role, not the effort scale', () => {
126
+ render(<SetRow {...doneRow} setType="DROP" />)
127
+ expect(screen.getByTestId('set-row-type-badge')).toHaveStyle({
128
+ color: resolveColor('brand-primary'),
129
+ backgroundColor: resolveColor('brand-primary-subtle'),
130
+ })
131
+ })
132
+
119
133
  it('does not render the type badge when setType is absent', () => {
120
134
  render(<SetRow {...doneRow} />)
121
135
  expect(screen.queryByTestId('set-row-type-badge')).not.toBeInTheDocument()
@@ -1,10 +1,10 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { type ReactNode } from 'react'
3
- import { View, Text } from 'react-native'
3
+ import { View } from 'react-native'
4
4
  import { VelocityStrip, type VelocityZoneBandProp } from './VelocityStrip'
5
+ import { Typography } from '../Typography'
5
6
  import { roundWeight, roundRpe } from '../../../utils/workout-format'
6
- import { getSemanticColors } from '../../../theme/tokens/semantic'
7
- import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
7
+ import { resolveColor } from '../../../theme/resolve-color'
8
8
 
9
9
  export type SetRowUnit = 'lbs' | 'kg'
10
10
 
@@ -61,20 +61,23 @@ export type SetRowProps =
61
61
  planned?: number
62
62
  })
63
63
 
64
- const DARK = getSemanticColors('dark')
65
- /** Live set — brightest (neutral 100). Done + upcoming share the muted neutral 400. */
66
- const TEXT_ACTIVE = DARK['text-primary']
67
- const TEXT_MUTED = DARK['text-secondary']
64
+ /** Live set — brightest. Done + upcoming share the muted role. */
65
+ const TEXT_ACTIVE = 'text-primary'
66
+ const TEXT_MUTED = 'text-secondary'
68
67
 
69
68
  /** Column widths mirror SetTableHeader(showPrevious=false) so cells align under it. */
70
69
  const COL = { set: 36, reps: 44, load: 56, rpe: 36 } as const
71
- const cellText = { fontFamily: 'Inter, sans-serif', fontSize: 13, fontWeight: '600' as const }
72
- const typeBadge = {
73
- fontSize: 10,
74
- fontWeight: '700' as const,
75
- fontFamily: 'Inter, sans-serif',
76
- color: WORKOUT_TOKENS.scale.orange,
77
- backgroundColor: 'rgba(255,165,2,0.12)',
70
+
71
+ // 13px is off the type scale (TOKENS.md §4); `boldLabel` is the 12px font-sans step
72
+ // and `font-semibold` holds the original 600 — Inter is `font-sans` here, per B1.
73
+ const CELL_CLASS = 'font-semibold leading-[normal]'
74
+
75
+ // The set-type chip was `WORKOUT_TOKENS.scale.orange` on a hand-mixed
76
+ // rgba(255,165,2,0.12). That pin is `sequentialEffort[3]` — the EFFORT scale, borrowed
77
+ // for a label that reads set type, not intensity. It resolves to orange-400, which is
78
+ // `brand-primary`, so the chip takes the brand role and the `-subtle` rung its 0.12 wash
79
+ // was already reaching for. The wash's own base (255,165,2) is off the orange ramp.
80
+ const typeBadgeStyle = {
78
81
  paddingVertical: 1,
79
82
  paddingHorizontal: 5,
80
83
  borderRadius: 3,
@@ -163,7 +166,8 @@ function RowStrip({ set }: { set: SetRowProps }) {
163
166
  */
164
167
  export function SetRow(set: SetRowProps) {
165
168
  const live = set.state === 'live'
166
- const valueColor = live ? TEXT_ACTIVE : TEXT_MUTED
169
+ const valueColor = resolveColor(live ? TEXT_ACTIVE : TEXT_MUTED)
170
+ const mutedColor = resolveColor(TEXT_MUTED)
167
171
  const rpe = set.state === 'todo' ? null : set.rpe
168
172
 
169
173
  return (
@@ -175,25 +179,59 @@ export function SetRow(set: SetRowProps) {
175
179
  <View className="flex-row items-center" style={{ justifyContent: 'space-between' }}>
176
180
  <Cell width={COL.set} testID="set-row-set-number">
177
181
  {set.setType ? (
178
- <Text style={typeBadge} testID="set-row-type-badge">
182
+ <Typography
183
+ variant="boldLabel"
184
+ color="inherit"
185
+ className="text-2xs leading-[normal]"
186
+ style={{
187
+ ...typeBadgeStyle,
188
+ color: resolveColor('brand-primary'),
189
+ backgroundColor: resolveColor('brand-primary-subtle'),
190
+ }}
191
+ testID="set-row-type-badge"
192
+ >
179
193
  {set.setType}
180
- </Text>
194
+ </Typography>
181
195
  ) : (
182
- <Text style={{ ...cellText, color: valueColor, fontWeight: live ? '700' : '600' }}>
196
+ <Typography
197
+ variant="boldLabel"
198
+ color="inherit"
199
+ className={live ? 'font-bold leading-[normal]' : CELL_CLASS}
200
+ style={{ color: valueColor }}
201
+ >
183
202
  {set.setNumber}
184
- </Text>
203
+ </Typography>
185
204
  )}
186
205
  </Cell>
187
206
  <Cell width={COL.reps} testID="set-row-reps">
188
- <Text style={{ ...cellText, color: valueColor }}>{displayReps(set)}</Text>
207
+ <Typography
208
+ variant="boldLabel"
209
+ color="inherit"
210
+ className={CELL_CLASS}
211
+ style={{ color: valueColor }}
212
+ >
213
+ {displayReps(set)}
214
+ </Typography>
189
215
  </Cell>
190
216
  <Cell width={COL.load} testID="set-row-weight">
191
- <Text style={{ ...cellText, color: valueColor }}>{roundWeight(displayWeight(set))}</Text>
217
+ <Typography
218
+ variant="boldLabel"
219
+ color="inherit"
220
+ className={CELL_CLASS}
221
+ style={{ color: valueColor }}
222
+ >
223
+ {roundWeight(displayWeight(set))}
224
+ </Typography>
192
225
  </Cell>
193
226
  <Cell width={COL.rpe} testID="set-row-rpe">
194
- <Text style={{ ...cellText, color: TEXT_MUTED }}>
227
+ <Typography
228
+ variant="boldLabel"
229
+ color="inherit"
230
+ className={CELL_CLASS}
231
+ style={{ color: mutedColor }}
232
+ >
195
233
  {rpe != null ? roundRpe(rpe) : '—'}
196
- </Text>
234
+ </Typography>
197
235
  </Cell>
198
236
  </View>
199
237
  <View style={{ marginTop: live ? 6 : 4 }} testID="set-row-strip">