@titan-design/react-ui 0.12.1 → 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 +1319 -216
  7. package/dist/index.d.ts +1319 -216
  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 +195 -134
  21. package/dist/theme/index.d.ts +195 -134
  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 +73 -5
  31. package/dist/theme/tokens.d.ts +73 -5
  32. package/dist/theme/tokens.js +55 -7
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +55 -8
  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/package.json +1 -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 +11 -6
  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 +89 -26
  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 +23 -21
  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 +1 -3
  480. package/src/theme/tokens/primitives.test.ts +15 -7
  481. package/src/theme/tokens/primitives.ts +65 -33
  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
@@ -29,11 +29,9 @@ describe('MesoStatusCard', () => {
29
29
  it('renders the card, name, and subtitle', () => {
30
30
  render(<MesoStatusCard {...baseProps} />)
31
31
  expect(screen.getByTestId('meso-status-card')).toBeInTheDocument()
32
- expect(screen.getByTestId('meso-status-card-name')).toHaveTextContent(
33
- 'Hypertrophy Block',
34
- )
32
+ expect(screen.getByTestId('meso-status-card-name')).toHaveTextContent('Hypertrophy Block')
35
33
  expect(screen.getByTestId('meso-status-card-subtitle')).toHaveTextContent(
36
- 'Week 6 of 8 · Upper/Lower',
34
+ 'Week 6 of 8 · Upper/Lower'
37
35
  )
38
36
  })
39
37
 
@@ -44,9 +42,7 @@ describe('MesoStatusCard', () => {
44
42
 
45
43
  it('renders the status badge with its label', () => {
46
44
  render(<MesoStatusCard {...baseProps} />)
47
- expect(screen.getByTestId('meso-status-card-badge')).toHaveTextContent(
48
- 'On Track',
49
- )
45
+ expect(screen.getByTestId('meso-status-card-badge')).toHaveTextContent('On Track')
50
46
  })
51
47
  })
52
48
 
@@ -67,16 +63,14 @@ describe('MesoStatusCard', () => {
67
63
  { label: 'Set', value: '1' },
68
64
  { label: 'Set', value: '2' },
69
65
  ]}
70
- />,
66
+ />
71
67
  )
72
68
  expect(screen.getAllByTestId('meso-status-card-metric')).toHaveLength(2)
73
69
  })
74
70
 
75
71
  it('does not render the metrics grid when empty', () => {
76
72
  render(<MesoStatusCard {...baseProps} metrics={[]} />)
77
- expect(
78
- screen.queryByTestId('meso-status-card-metrics'),
79
- ).not.toBeInTheDocument()
73
+ expect(screen.queryByTestId('meso-status-card-metrics')).not.toBeInTheDocument()
80
74
  })
81
75
  })
82
76
 
@@ -84,9 +78,7 @@ describe('MesoStatusCard', () => {
84
78
  it('renders one gauge per entry with a marker', () => {
85
79
  render(<MesoStatusCard {...baseProps} />)
86
80
  expect(screen.getAllByTestId('meso-status-card-gauge')).toHaveLength(2)
87
- expect(
88
- screen.getAllByTestId('meso-status-card-gauge-marker'),
89
- ).toHaveLength(2)
81
+ expect(screen.getAllByTestId('meso-status-card-gauge-marker')).toHaveLength(2)
90
82
  })
91
83
 
92
84
  it('renders a 50% center line per gauge', () => {
@@ -96,34 +88,19 @@ describe('MesoStatusCard', () => {
96
88
  })
97
89
 
98
90
  it('exposes gauge level via progressbar value', () => {
99
- render(
100
- <MesoStatusCard
101
- {...baseProps}
102
- gauges={[{ label: 'Intensity', level: 0.7 }]}
103
- />,
104
- )
91
+ render(<MesoStatusCard {...baseProps} gauges={[{ label: 'Intensity', level: 0.7 }]} />)
105
92
  const gauge = screen.getByLabelText('Intensity: 70%')
106
93
  expect(gauge).toHaveAttribute('role', 'progressbar')
107
94
  expect(gauge).toHaveAttribute('aria-valuenow', '70')
108
95
  })
109
96
 
110
97
  it('clamps out-of-range levels', () => {
111
- render(
112
- <MesoStatusCard
113
- {...baseProps}
114
- gauges={[{ label: 'Volume', level: 1.7 }]}
115
- />,
116
- )
98
+ render(<MesoStatusCard {...baseProps} gauges={[{ label: 'Volume', level: 1.7 }]} />)
117
99
  expect(screen.getByLabelText('Volume: 100%')).toBeInTheDocument()
118
100
  })
119
101
 
120
102
  it('guards a non-finite level, rendering 0% instead of NaN%', () => {
121
- render(
122
- <MesoStatusCard
123
- {...baseProps}
124
- gauges={[{ label: 'Intensity', level: Number.NaN }]}
125
- />,
126
- )
103
+ render(<MesoStatusCard {...baseProps} gauges={[{ label: 'Intensity', level: Number.NaN }]} />)
127
104
  expect(screen.getByLabelText('Intensity: 0%')).toBeInTheDocument()
128
105
  expect(screen.queryByLabelText('Intensity: NaN%')).not.toBeInTheDocument()
129
106
  })
@@ -136,7 +113,7 @@ describe('MesoStatusCard', () => {
136
113
  { label: 'Load', level: 0.3 },
137
114
  { label: 'Load', level: 0.6 },
138
115
  ]}
139
- />,
116
+ />
140
117
  )
141
118
  expect(screen.getAllByTestId('meso-status-card-gauge')).toHaveLength(2)
142
119
  })
@@ -152,9 +129,7 @@ describe('MesoStatusCard', () => {
152
129
 
153
130
  it('omits the coaching box when not provided', () => {
154
131
  render(<MesoStatusCard {...baseProps} coaching={undefined} />)
155
- expect(
156
- screen.queryByTestId('meso-status-card-coaching'),
157
- ).not.toBeInTheDocument()
132
+ expect(screen.queryByTestId('meso-status-card-coaching')).not.toBeInTheDocument()
158
133
  })
159
134
  })
160
135
 
@@ -167,9 +142,7 @@ describe('MesoStatusCard', () => {
167
142
 
168
143
  it('omits the next-target box when not provided', () => {
169
144
  render(<MesoStatusCard {...baseProps} nextTarget={undefined} />)
170
- expect(
171
- screen.queryByTestId('meso-status-card-next-target'),
172
- ).not.toBeInTheDocument()
145
+ expect(screen.queryByTestId('meso-status-card-next-target')).not.toBeInTheDocument()
173
146
  })
174
147
  })
175
148
 
@@ -177,10 +150,7 @@ describe('MesoStatusCard', () => {
177
150
  it('exposes the card as an article with a descriptive label', () => {
178
151
  render(<MesoStatusCard {...baseProps} />)
179
152
  const article = screen.getByRole('article')
180
- expect(article).toHaveAttribute(
181
- 'aria-label',
182
- 'Mesocycle status for Hypertrophy Block',
183
- )
153
+ expect(article).toHaveAttribute('aria-label', 'Mesocycle status for Hypertrophy Block')
184
154
  })
185
155
 
186
156
  it('has no accessibility violations', async () => {
@@ -201,7 +171,7 @@ describe('MesoStatusCard', () => {
201
171
  coaching={undefined}
202
172
  nextTarget={undefined}
203
173
  />
204
- </>,
174
+ </>
205
175
  )
206
176
  expect(await axe(container)).toHaveNoViolations()
207
177
  })
@@ -9,6 +9,7 @@ import {
9
9
  MESO_ACCENT_GRADIENT_DARK,
10
10
  MESO_ACCENT_GRADIENT_LIGHT,
11
11
  } from '../../../theme/extracted-colors-dataviz'
12
+ import { liftStyle } from '../../../theme/lift'
12
13
 
13
14
  const t = getSemanticColors('dark')
14
15
 
@@ -117,23 +118,17 @@ function getGaugeZoneColor(level: number): string {
117
118
  /** Splits `text` into nodes, bolding any segment that exactly matches a highlight. */
118
119
  function renderCoachingText(text: string, highlights?: string[]): ReactNode {
119
120
  if (!highlights || highlights.length === 0) return text
120
- const escaped = highlights
121
- .filter(Boolean)
122
- .map((h) => h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
121
+ const escaped = highlights.filter(Boolean).map((h) => h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
123
122
  if (escaped.length === 0) return text
124
123
  const regex = new RegExp(`(${escaped.join('|')})`, 'g')
125
124
  return text.split(regex).map((part, index) =>
126
125
  highlights.includes(part) ? (
127
- <Text
128
- key={`${part}-${index}`}
129
- className="text-text-primary"
130
- style={{ fontWeight: '700' }}
131
- >
126
+ <Text key={`${part}-${index}`} className="text-text-primary" style={{ fontWeight: '700' }}>
132
127
  {part}
133
128
  </Text>
134
129
  ) : (
135
130
  <Fragment key={`t-${index}`}>{part}</Fragment>
136
- ),
131
+ )
137
132
  )
138
133
  }
139
134
 
@@ -216,10 +211,7 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
216
211
  aria-valuemax={100}
217
212
  testID="meso-status-card-gauge"
218
213
  >
219
- <View
220
- className="flex-row items-center justify-between"
221
- accessibilityElementsHidden
222
- >
214
+ <View className="flex-row items-center justify-between" accessibilityElementsHidden>
223
215
  <Text
224
216
  className="text-text-secondary"
225
217
  style={{
@@ -281,7 +273,8 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
281
273
  borderRadius: 9999,
282
274
  borderWidth: 2,
283
275
  backgroundColor: markerColor,
284
- boxShadow: '0 0 4px rgba(0,0,0,0.5)',
276
+ // A knob resting on the gauge: lift, with the ring as its edge.
277
+ ...liftStyle(1, 'dark', { rim: 0 }),
285
278
  }}
286
279
  testID="meso-status-card-gauge-marker"
287
280
  />
@@ -353,10 +346,7 @@ export function MesoStatusCard({
353
346
 
354
347
  <View style={{ padding: 14, gap: 14 }} testID="meso-status-card-body">
355
348
  <View testID="meso-status-card-header">
356
- <View
357
- className="flex-row items-center justify-between"
358
- style={{ gap: 8 }}
359
- >
349
+ <View className="flex-row items-center justify-between" style={{ gap: 8 }}>
360
350
  <Text
361
351
  className="text-text-primary"
362
352
  style={{
@@ -385,11 +375,7 @@ export function MesoStatusCard({
385
375
  </View>
386
376
 
387
377
  {metrics.length > 0 && (
388
- <View
389
- className="flex-row flex-wrap"
390
- style={{ gap: 8 }}
391
- testID="meso-status-card-metrics"
392
- >
378
+ <View className="flex-row flex-wrap" style={{ gap: 8 }} testID="meso-status-card-metrics">
393
379
  {metrics.map((metric, index) => (
394
380
  <MetricCell key={`${metric.label}-${index}`} metric={metric} />
395
381
  ))}
@@ -6,14 +6,15 @@ import { getSemanticColors } from '../../../theme'
6
6
  const AMBER = getSemanticColors('dark')['status-warning']
7
7
 
8
8
  const meta: Meta<typeof MetricTiles> = {
9
- title: 'Workout/MetricTiles',
9
+ title: 'Custom/Workout/MetricTiles',
10
10
  component: MetricTiles,
11
11
  tags: ['autodocs'],
12
12
  parameters: {
13
13
  docs: {
14
14
  description: {
15
15
  component:
16
- '**Molecule.** An HStack of [Tile](?path=/docs/components-atoms-tile--docs). Used-by ↑ SessionHeader.',
16
+ '**Molecule.** An HStack of [Tile](?path=/docs/components-atoms-tile--docs). Used-by ↑ SessionHeader.' +
17
+ ' Composes **HStack** · **Tile**.',
17
18
  },
18
19
  },
19
20
  },
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { MuscleGroupChip, type VolumeStatus } from './MuscleGroupChip'
4
4
 
5
5
  const meta: Meta<typeof MuscleGroupChip> = {
6
- title: 'Components/Atoms/MuscleGroupChip',
6
+ title: 'Custom/Workout/MuscleGroupChip',
7
7
  component: MuscleGroupChip,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -82,13 +82,7 @@ export const Tappable: Story = {
82
82
  },
83
83
  }
84
84
 
85
- const allStatuses: VolumeStatus[] = [
86
- 'untrained',
87
- 'behind',
88
- 'ontrack',
89
- 'target',
90
- 'over',
91
- ]
85
+ const allStatuses: VolumeStatus[] = ['untrained', 'behind', 'ontrack', 'target', 'over']
92
86
 
93
87
  export const AllStatuses: Story = {
94
88
  render: () => (
@@ -9,23 +9,33 @@ describe('MuscleGroupChip', () => {
9
9
  expect(screen.getByText('Chest')).toBeInTheDocument()
10
10
  })
11
11
 
12
- it('renders the colored dot', () => {
12
+ it('renders the coloured dot under the chip’s own testID', () => {
13
13
  render(<MuscleGroupChip name="Quads" volumeStatus="ontrack" />)
14
14
  expect(screen.getByTestId('muscle-group-chip-dot')).toBeInTheDocument()
15
15
  })
16
16
 
17
+ it('keeps one capsule shape across every status, changing only the dot', () => {
18
+ // The dot carries status; the capsule stays neutral, so a row of chips reads
19
+ // as one family. jsdom cannot compute a nativewind class background, so the
20
+ // fill itself is asserted by the HTML-vs-React parity layer; what is
21
+ // checkable here is that the status never reaches the capsule element.
22
+ const { rerender } = render(<MuscleGroupChip name="Quads" volumeStatus="untrained" />)
23
+ const shape = screen.getByTestId('muscle-group-chip').getAttribute('class')
24
+ for (const status of ['behind', 'ontrack', 'target', 'over'] as const) {
25
+ rerender(<MuscleGroupChip name="Quads" volumeStatus={status} />)
26
+ expect(screen.getByTestId('muscle-group-chip').getAttribute('class'), status).toBe(shape)
27
+ expect(screen.getByTestId('muscle-group-chip-dot')).toBeInTheDocument()
28
+ }
29
+ })
30
+
17
31
  it('sets accessibility label with name and status', () => {
18
32
  render(<MuscleGroupChip name="Back" volumeStatus="behind" />)
19
- expect(
20
- screen.getByLabelText('Back, volume status: behind'),
21
- ).toBeInTheDocument()
33
+ expect(screen.getByLabelText('Back, volume status: behind')).toBeInTheDocument()
22
34
  })
23
35
 
24
36
  it('sets accessibility label with no status when omitted', () => {
25
37
  render(<MuscleGroupChip name="Biceps" />)
26
- expect(
27
- screen.getByLabelText('Biceps, volume status: no status'),
28
- ).toBeInTheDocument()
38
+ expect(screen.getByLabelText('Biceps, volume status: no status')).toBeInTheDocument()
29
39
  })
30
40
 
31
41
  it('wraps in Pressable when onPress is provided', () => {
@@ -38,22 +48,12 @@ describe('MuscleGroupChip', () => {
38
48
 
39
49
  it('does not render Pressable when onPress is omitted', () => {
40
50
  render(<MuscleGroupChip name="Chest" />)
41
- expect(
42
- screen.queryByTestId('muscle-group-chip-pressable'),
43
- ).not.toBeInTheDocument()
51
+ expect(screen.queryByTestId('muscle-group-chip-pressable')).not.toBeInTheDocument()
44
52
  })
45
53
 
46
54
  it('renders all volume status variants without error', () => {
47
- const statuses = [
48
- 'untrained',
49
- 'behind',
50
- 'ontrack',
51
- 'target',
52
- 'over',
53
- ] as const
54
- const { rerender } = render(
55
- <MuscleGroupChip name="Test" volumeStatus="untrained" />,
56
- )
55
+ const statuses = ['untrained', 'behind', 'ontrack', 'target', 'over'] as const
56
+ const { rerender } = render(<MuscleGroupChip name="Test" volumeStatus="untrained" />)
57
57
  for (const status of statuses) {
58
58
  rerender(<MuscleGroupChip name="Test" volumeStatus={status} />)
59
59
  expect(screen.getByText('Test')).toBeInTheDocument()
@@ -63,28 +63,20 @@ describe('MuscleGroupChip', () => {
63
63
  describe('accessibility', () => {
64
64
  it('has no accessibility violations', async () => {
65
65
  const { container } = render(
66
- <MuscleGroupChip
67
- name="Chest"
68
- volumeStatus="ontrack"
69
- onPress={() => {}}
70
- />,
66
+ <MuscleGroupChip name="Chest" volumeStatus="ontrack" onPress={() => {}} />
71
67
  )
72
68
  const results = await axe(container)
73
69
  expect(results).toHaveNoViolations()
74
70
  })
75
71
 
76
72
  it('has no accessibility violations as static chip', async () => {
77
- const { container } = render(
78
- <MuscleGroupChip name="Quads" volumeStatus="behind" />,
79
- )
73
+ const { container } = render(<MuscleGroupChip name="Quads" volumeStatus="behind" />)
80
74
  const results = await axe(container)
81
75
  expect(results).toHaveNoViolations()
82
76
  })
83
77
 
84
78
  it('pressable has correct accessibility label', () => {
85
- render(
86
- <MuscleGroupChip name="Back" volumeStatus="target" onPress={() => {}} />,
87
- )
79
+ render(<MuscleGroupChip name="Back" volumeStatus="target" onPress={() => {}} />)
88
80
  expect(screen.getByLabelText('Back, volume status: target')).toBeInTheDocument()
89
81
  })
90
82
  })
@@ -1,9 +1,6 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, Pressable, type ViewProps } from 'react-native'
3
- import { getSemanticColors } from '../../../theme/tokens/semantic'
4
- import { greyRamp } from '../../../theme/tokens/primitives'
5
-
6
- const t = getSemanticColors('dark')
2
+ import { type ViewProps } from 'react-native'
3
+ import { Pill, type PillTone } from '../../ui/pill'
7
4
 
8
5
  // Aliases for spec compatibility: under=behind, maintenance=ontrack, productive=target
9
6
  export type VolumeStatus = 'untrained' | 'behind' | 'ontrack' | 'target' | 'over'
@@ -15,14 +12,18 @@ export interface MuscleGroupChipProps extends ViewProps {
15
12
  className?: string
16
13
  }
17
14
 
18
- const dotColorMap: Record<VolumeStatus, string> = {
19
- untrained: greyRamp[900],
20
- behind: t['brand-secondary'],
21
- ontrack: t['status-success'],
22
- target: t['brand-primary'],
23
- over: t['status-error'],
15
+ const dotTone: Record<VolumeStatus, PillTone> = {
16
+ untrained: 'neutral',
17
+ behind: 'brand-secondary',
18
+ ontrack: 'success',
19
+ target: 'brand',
20
+ over: 'error',
24
21
  }
25
22
 
23
+ /**
24
+ * MuscleGroupChip — a `Pill` preset that labels a muscle group with a
25
+ * volume-status dot. The dot tone is the pill's tone; the capsule stays neutral.
26
+ */
26
27
  export function MuscleGroupChip({
27
28
  name,
28
29
  volumeStatus,
@@ -30,65 +31,25 @@ export function MuscleGroupChip({
30
31
  className,
31
32
  ...props
32
33
  }: MuscleGroupChipProps) {
33
- const dotColor = volumeStatus
34
- ? dotColorMap[volumeStatus]
35
- : greyRamp[900]
36
-
37
34
  const statusLabel = volumeStatus ?? 'no status'
38
35
 
39
- const content = (
40
- <View
41
- className={['bg-surface-raised border-hairline', className].filter(Boolean).join(' ')}
42
- style={{
43
- flexDirection: 'row',
44
- alignItems: 'center',
45
- borderRadius: 9999,
46
- paddingHorizontal: 9,
47
- paddingVertical: 3,
48
- borderWidth: 1,
49
- }}
50
- accessibilityLabel={onPress ? undefined : `${name}, volume status: ${statusLabel}`}
51
- testID="muscle-group-chip"
36
+ return (
37
+ <Pill
38
+ tone="neutral"
39
+ dotTone={volumeStatus ? dotTone[volumeStatus] : 'neutral'}
40
+ variant="subtle"
41
+ size="md"
42
+ leading="dot"
43
+ onPress={onPress}
44
+ // Borderless, like every other tone: the tinted fill carries the capsule.
45
+ // `md` keeps the chip at its shipped ~24px height with 12px text.
46
+ className={['gap-1.5', className].filter(Boolean).join(' ')}
47
+ textClassName="font-sans font-medium text-text-secondary"
48
+ accessibilityLabel={`${name}, volume status: ${statusLabel}`}
49
+ testID={onPress ? 'muscle-group-chip-pressable' : 'muscle-group-chip'}
52
50
  {...props}
53
51
  >
54
- <View
55
- style={{
56
- width: 6,
57
- height: 6,
58
- borderRadius: 9999,
59
- marginRight: 5,
60
- flexShrink: 0,
61
- backgroundColor: dotColor,
62
- }}
63
- accessibilityElementsHidden
64
- testID="muscle-group-chip-dot"
65
- />
66
- <Text
67
- className="text-text-secondary"
68
- style={{
69
- fontFamily: 'Inter, sans-serif',
70
- fontWeight: '500',
71
- fontSize: 11,
72
- }}
73
- accessibilityElementsHidden
74
- >
75
- {name}
76
- </Text>
77
- </View>
52
+ {name}
53
+ </Pill>
78
54
  )
79
-
80
- if (onPress) {
81
- return (
82
- <Pressable
83
- onPress={onPress}
84
- accessibilityRole="button"
85
- accessibilityLabel={`${name}, volume status: ${statusLabel}`}
86
- testID="muscle-group-chip-pressable"
87
- >
88
- {content}
89
- </Pressable>
90
- )
91
- }
92
-
93
- return content
94
55
  }
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { PlaceholderStrip } from './PlaceholderStrip'
4
4
 
5
5
  const meta: Meta<typeof PlaceholderStrip> = {
6
- title: 'Workout/PlaceholderStrip',
6
+ title: 'Custom/Workout/PlaceholderStrip',
7
7
  component: PlaceholderStrip,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'
2
2
  import { render, screen } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { PlaceholderStrip } from './PlaceholderStrip'
5
+ import { resolveColor } from '../../../theme/resolve-color'
5
6
 
6
7
  describe('PlaceholderStrip', () => {
7
8
  it('renders a strip element', () => {
@@ -11,9 +12,7 @@ describe('PlaceholderStrip', () => {
11
12
 
12
13
  it('sets accessibility label', () => {
13
14
  render(<PlaceholderStrip />)
14
- expect(
15
- screen.getByLabelText('Planned set, not yet completed'),
16
- ).toBeInTheDocument()
15
+ expect(screen.getByLabelText('Planned set, not yet completed')).toBeInTheDocument()
17
16
  })
18
17
 
19
18
  it('applies fixed width when provided', () => {
@@ -28,7 +27,7 @@ describe('PlaceholderStrip', () => {
28
27
  <PlaceholderStrip />
29
28
  <PlaceholderStrip />
30
29
  <PlaceholderStrip />
31
- </>,
30
+ </>
32
31
  )
33
32
  const strips = screen.getAllByTestId('placeholder-strip')
34
33
  expect(strips).toHaveLength(3)
@@ -39,6 +38,18 @@ describe('PlaceholderStrip', () => {
39
38
  expect(screen.queryAllByTestId('placeholder-segment')).toHaveLength(0)
40
39
  })
41
40
 
41
+ it('fills both modes from the border-prominent token, not a pinned hex', () => {
42
+ const rail = resolveColor('border-prominent')
43
+ const { unmount } = render(<PlaceholderStrip />)
44
+ expect(screen.getByTestId('placeholder-strip')).toHaveStyle({ backgroundColor: rail })
45
+ unmount()
46
+
47
+ render(<PlaceholderStrip mode="segmented" segments={2} />)
48
+ for (const segment of screen.getAllByTestId('placeholder-segment')) {
49
+ expect(segment).toHaveStyle({ backgroundColor: rail })
50
+ }
51
+ })
52
+
42
53
  describe('segmented mode', () => {
43
54
  it('renders segments when mode is segmented', () => {
44
55
  render(<PlaceholderStrip mode="segmented" segments={5} />)
@@ -59,9 +70,7 @@ describe('PlaceholderStrip', () => {
59
70
 
60
71
  it('sets accessibility label in segmented mode', () => {
61
72
  render(<PlaceholderStrip mode="segmented" segments={3} />)
62
- expect(
63
- screen.getByLabelText('Planned set, not yet completed'),
64
- ).toBeInTheDocument()
73
+ expect(screen.getByLabelText('Planned set, not yet completed')).toBeInTheDocument()
65
74
  })
66
75
  })
67
76
 
@@ -73,9 +82,7 @@ describe('PlaceholderStrip', () => {
73
82
  })
74
83
 
75
84
  it('has no accessibility violations in segmented mode', async () => {
76
- const { container } = render(
77
- <PlaceholderStrip mode="segmented" segments={4} />,
78
- )
85
+ const { container } = render(<PlaceholderStrip mode="segmented" segments={4} />)
79
86
  const results = await axe(container)
80
87
  expect(results).toHaveNoViolations()
81
88
  })
@@ -1,7 +1,12 @@
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
3
  import { cn } from '../../../utils/cn'
4
- import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
4
+ import { resolveColor } from '../../../theme/resolve-color'
5
+
6
+ // The planned-set rail is a visible hairline rule, not a plane, so it takes the
7
+ // one border token meant to be seen outright. It used to be a `#3A3A3A` pin in
8
+ // `workout-tokens`, which sat between two ramp steps and never flipped to light.
9
+ const RAIL_TOKEN = 'border-prominent' as const
5
10
 
6
11
  export interface PlaceholderStripProps extends ViewProps {
7
12
  width?: number | string
@@ -23,7 +28,7 @@ function SingleStrip({
23
28
  style={[
24
29
  {
25
30
  height: 3,
26
- backgroundColor: WORKOUT_TOKENS.placeholder.fill,
31
+ backgroundColor: resolveColor(RAIL_TOKEN),
27
32
  borderRadius: 2,
28
33
  opacity: 0.5,
29
34
  },
@@ -61,7 +66,7 @@ function SegmentedStrip({
61
66
  style={{
62
67
  flex: 1,
63
68
  height: 3,
64
- backgroundColor: WORKOUT_TOKENS.placeholder.fill,
69
+ backgroundColor: resolveColor(RAIL_TOKEN),
65
70
  borderRadius: 1,
66
71
  minWidth: 4,
67
72
  }}
@@ -73,11 +78,7 @@ function SegmentedStrip({
73
78
  )
74
79
  }
75
80
 
76
- export function PlaceholderStrip({
77
- mode = 'single',
78
- segments,
79
- ...props
80
- }: PlaceholderStripProps) {
81
+ export function PlaceholderStrip({ mode = 'single', segments, ...props }: PlaceholderStripProps) {
81
82
  if (mode === 'segmented') {
82
83
  return <SegmentedStrip segments={segments} {...props} />
83
84
  }
@@ -3,8 +3,9 @@ import { View } from 'react-native'
3
3
  import { PrBadge } from './PrBadge'
4
4
 
5
5
  const meta: Meta<typeof PrBadge> = {
6
- title: 'Components/Atoms/PrBadge',
6
+ title: 'Custom/Workout/PrBadge',
7
7
  component: PrBadge,
8
+ parameters: { docs: { description: { component: 'Composes **Icons** · **BaseBadge**.' } } },
8
9
  tags: ['autodocs'],
9
10
  argTypes: {
10
11
  type: {
@@ -26,9 +26,7 @@ describe('PrBadge', () => {
26
26
 
27
27
  it('sets accessibility label with resolved label', () => {
28
28
  render(<PrBadge animate={false} />)
29
- expect(
30
- screen.getByLabelText('Personal record: PR e1RM'),
31
- ).toBeInTheDocument()
29
+ expect(screen.getByLabelText('Personal record: PR e1RM')).toBeInTheDocument()
32
30
  })
33
31
 
34
32
  describe('type prop', () => {
@@ -66,9 +64,7 @@ describe('PrBadge', () => {
66
64
 
67
65
  it('sets accessibility label with custom label', () => {
68
66
  render(<PrBadge label="PR 5RM" animate={false} />)
69
- expect(
70
- screen.getByLabelText('Personal record: PR 5RM'),
71
- ).toBeInTheDocument()
67
+ expect(screen.getByLabelText('Personal record: PR 5RM')).toBeInTheDocument()
72
68
  })
73
69
  })
74
70
 
@@ -89,9 +85,7 @@ describe('PrBadge', () => {
89
85
 
90
86
  it('has correct accessibility label in compact mode', () => {
91
87
  render(<PrBadge compact animate={false} />)
92
- expect(
93
- screen.getByLabelText('Personal record: PR e1RM'),
94
- ).toBeInTheDocument()
88
+ expect(screen.getByLabelText('Personal record: PR e1RM')).toBeInTheDocument()
95
89
  })
96
90
  })
97
91