@titan-design/react-ui 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (531) hide show
  1. package/README.md +60 -47
  2. package/dist/bodymap.js +856 -736
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +858 -738
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1431 -237
  7. package/dist/index.d.ts +1431 -237
  8. package/dist/index.js +6506 -4781
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +6280 -4642
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-B1hqnGIC.d.ts → pages-CsjsUn0G.d.ts} +51 -16
  13. package/dist/{pages-CvsFqNtx.d.mts → pages-ZbT_pD_U.d.mts} +51 -16
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1919 -1937
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1927 -1945
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +225 -140
  21. package/dist/theme/index.d.ts +225 -140
  22. package/dist/theme/index.js +298 -319
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +283 -315
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +145 -24
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +145 -24
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +128 -11
  31. package/dist/theme/tokens.d.ts +128 -11
  32. package/dist/theme/tokens.js +126 -26
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +124 -27
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/audits/2026-09-08-critique-addenda.md +51 -0
  37. package/docs/audits/2026-09-08-library-critique.md +212 -0
  38. package/docs/library-roadmap.md +99 -0
  39. package/docs/render-testing-pattern.md +130 -0
  40. package/package.json +1 -1
  41. package/src/arch/Architecture.stories.tsx +3 -1
  42. package/src/arch/arch-graph.freshness.test.ts +52 -0
  43. package/src/arch/arch-graph.json +4989 -1583
  44. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  45. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  46. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  48. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  49. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  51. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  52. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  54. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  55. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  57. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  58. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  60. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  61. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  63. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  64. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +182 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  66. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  67. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  69. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  70. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  71. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  73. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  74. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  76. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  77. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  78. package/src/components/custom/ActiveWork/README.md +305 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  80. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  81. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  82. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  83. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  84. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  86. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  87. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  88. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  90. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  91. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  92. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  93. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  94. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  95. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  96. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  97. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  98. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  99. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  100. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  101. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  102. package/src/components/custom/ActiveWork/index.ts +19 -0
  103. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  104. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  105. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  106. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  107. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +12 -6
  108. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  109. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  110. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  111. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  112. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  113. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  116. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  117. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  119. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  120. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  121. package/src/components/custom/Fatigue/README.md +40 -3
  122. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  123. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  124. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  125. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  126. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  127. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  128. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  129. package/src/components/custom/Fatigue/index.ts +17 -5
  130. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  131. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  132. package/src/components/custom/Gauge/Gauge.stories.tsx +6 -3
  133. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  134. package/src/components/custom/Gauge/Gauge.tsx +7 -2
  135. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  136. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  137. package/src/components/custom/Metric/Metric.tsx +5 -20
  138. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  139. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  140. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  141. package/src/components/custom/Prose/README.md +58 -0
  142. package/src/components/custom/Prose/index.ts +1 -0
  143. package/src/components/custom/Scatter/Scatter.stories.tsx +55 -14
  144. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  145. package/src/components/custom/Scatter/Scatter.tsx +122 -18
  146. package/src/components/custom/Sidebar/Sidebar.stories.tsx +41 -8
  147. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  148. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  149. package/src/components/custom/Table/Table.stories.tsx +99 -16
  150. package/src/components/custom/Table/Table.test.tsx +273 -86
  151. package/src/components/custom/Table/Table.tsx +159 -67
  152. package/src/components/custom/Table/column-fit.test.ts +75 -0
  153. package/src/components/custom/Table/column-fit.ts +116 -0
  154. package/src/components/custom/Table/index.ts +9 -0
  155. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  156. package/src/components/custom/Treemap/Treemap.stories.tsx +15 -12
  157. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  158. package/src/components/custom/Treemap/Treemap.tsx +11 -3
  159. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  160. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  161. package/src/components/custom/Typography/Typography.tsx +6 -0
  162. package/src/components/custom/Typography/index.ts +1 -8
  163. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +11 -2
  164. package/src/components/custom/Workout/BaseBadge.stories.tsx +27 -11
  165. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  166. package/src/components/custom/Workout/BaseBadge.tsx +6 -2
  167. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  168. package/src/components/custom/Workout/BodyMap.tsx +8 -4
  169. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +28 -6
  170. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  171. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +50 -12
  172. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +12 -9
  173. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  174. package/src/components/custom/Workout/CapacityBandChart.tsx +144 -111
  175. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  176. package/src/components/custom/Workout/DeviationBar.test.tsx +10 -4
  177. package/src/components/custom/Workout/DeviationBar.tsx +24 -16
  178. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +183 -0
  179. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +32 -18
  180. package/src/components/custom/Workout/ExerciseCard.stories.tsx +12 -11
  181. package/src/components/custom/Workout/ExerciseCard.test.tsx +52 -22
  182. package/src/components/custom/Workout/ExerciseCard.tsx +99 -183
  183. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +95 -12
  184. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
  185. package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
  186. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  187. package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
  188. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  189. package/src/components/custom/Workout/ExerciseHeading.tsx +194 -46
  190. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +12 -9
  191. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  192. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  193. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  194. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  195. package/src/components/custom/Workout/InputBar.tsx +4 -2
  196. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  197. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  198. package/src/components/custom/Workout/IntensityBar.tsx +8 -3
  199. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  200. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  201. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  202. package/src/components/custom/Workout/MesoCard.tsx +14 -10
  203. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  204. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  205. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  206. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  207. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  208. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -33
  209. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  210. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  211. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  212. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  213. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +9 -4
  214. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  215. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  216. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  217. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  218. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  219. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +16 -3
  220. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  221. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  222. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  223. package/src/components/custom/Workout/README.md +68 -17
  224. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  225. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  226. package/src/components/custom/Workout/ReadinessCheck.tsx +22 -4
  227. package/src/components/custom/Workout/RestTimer.stories.tsx +16 -4
  228. package/src/components/custom/Workout/RestTimer.tsx +4 -2
  229. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  230. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  231. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  232. package/src/components/custom/Workout/SegmentedBar.stories.tsx +21 -17
  233. package/src/components/custom/Workout/SegmentedBar.tsx +10 -5
  234. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  235. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  236. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  237. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  238. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  239. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  240. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  241. package/src/components/custom/Workout/SetBar.tsx +38 -12
  242. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  243. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  244. package/src/components/custom/Workout/SetRow.tsx +61 -23
  245. package/src/components/custom/Workout/SetStrip.stories.tsx +63 -7
  246. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  247. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  248. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  249. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  250. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  251. package/src/components/custom/Workout/SetsRepsLoad.tsx +24 -12
  252. package/src/components/custom/Workout/Sparkline.stories.tsx +14 -11
  253. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  254. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  255. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  256. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  257. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  258. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  259. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  260. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  261. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +11 -4
  262. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  263. package/src/components/custom/Workout/StrengthTrendChart.tsx +39 -36
  264. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  265. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  266. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  267. package/src/components/custom/Workout/TempoDisplay.stories.tsx +26 -10
  268. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  269. package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
  270. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  271. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  272. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +10 -6
  273. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +5 -2
  274. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +44 -1
  275. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  276. package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
  277. package/src/components/custom/Workout/VelocityStrip.tsx +26 -14
  278. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  279. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  280. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  281. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  282. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  283. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  284. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  285. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  286. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  287. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  288. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  289. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  290. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  291. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  292. package/src/components/custom/Workout/ZoneTrack.stories.tsx +10 -7
  293. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  294. package/src/components/custom/Workout/exerciseRowState.ts +29 -0
  295. package/src/components/custom/Workout/index.ts +17 -2
  296. package/src/components/custom/Workout/metricText.stories.tsx +15 -13
  297. package/src/components/custom/Workout/metricText.tsx +8 -2
  298. package/src/components/custom/Workout/setHeadingKit.tsx +219 -36
  299. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  300. package/src/components/custom/charts/README.md +66 -0
  301. package/src/components/custom/charts/SetBarChart.test.tsx +46 -1
  302. package/src/components/custom/charts/SetBarChart.tsx +56 -9
  303. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  304. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  305. package/src/components/custom/charts/SparkBars.tsx +85 -0
  306. package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
  307. package/src/components/custom/charts/flatBarGeometry.ts +32 -0
  308. package/src/components/custom/charts/index.ts +3 -0
  309. package/src/components/custom/index.ts +3 -0
  310. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  311. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  312. package/src/components/custom/stepper/Stepper.tsx +21 -34
  313. package/src/components/icons/icons.stories.tsx +1 -1
  314. package/src/components/icons/icons.tsx +51 -0
  315. package/src/components/shell/AppShell.stories.tsx +150 -0
  316. package/src/components/shell/AppShell.test.tsx +70 -0
  317. package/src/components/shell/AppShell.tsx +90 -0
  318. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  319. package/src/components/shell/BrandLockup.test.tsx +34 -1
  320. package/src/components/shell/BrandLockup.tsx +27 -11
  321. package/src/components/shell/NavItem.stories.tsx +4 -4
  322. package/src/components/shell/NavItem.test.tsx +17 -0
  323. package/src/components/shell/NavItem.tsx +17 -5
  324. package/src/components/shell/README.md +89 -26
  325. package/src/components/shell/SideNav.stories.tsx +38 -7
  326. package/src/components/shell/SideNav.test.tsx +16 -8
  327. package/src/components/shell/SideNav.tsx +17 -17
  328. package/src/components/shell/TopBar.stories.tsx +44 -19
  329. package/src/components/shell/TopBar.test.tsx +34 -12
  330. package/src/components/shell/TopBar.tsx +61 -40
  331. package/src/components/shell/brands.tsx +66 -0
  332. package/src/components/shell/index.ts +9 -8
  333. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  334. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  335. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  336. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  337. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  338. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  339. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  340. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  341. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  342. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  343. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  344. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  345. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  346. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  347. package/src/components/shell/workout/index.ts +9 -0
  348. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  349. package/src/components/ui/README.md +110 -0
  350. package/src/components/ui/alert/Alert.stories.tsx +15 -5
  351. package/src/components/ui/alert/Alert.tsx +24 -3
  352. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  353. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  354. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  355. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  356. package/src/components/ui/avatar/Avatar.tsx +2 -10
  357. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  358. package/src/components/ui/badge/Badge.test.tsx +19 -3
  359. package/src/components/ui/badge/Badge.tsx +48 -55
  360. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  361. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  362. package/src/components/ui/button/Button.stories.tsx +46 -12
  363. package/src/components/ui/button/Button.tsx +44 -37
  364. package/src/components/ui/card/Card.stories.tsx +90 -94
  365. package/src/components/ui/card/Card.test.tsx +80 -6
  366. package/src/components/ui/card/Card.tsx +150 -203
  367. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  368. package/src/components/ui/checkbox/Checkbox.tsx +7 -27
  369. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  370. package/src/components/ui/chip/Chip.test.tsx +10 -6
  371. package/src/components/ui/chip/Chip.tsx +76 -91
  372. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  373. package/src/components/ui/collapse/Collapse.tsx +6 -11
  374. package/src/components/ui/data-row/DataRow.stories.tsx +14 -3
  375. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  376. package/src/components/ui/data-row/DataRow.tsx +18 -7
  377. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  378. package/src/components/ui/divider/Divider.tsx +1 -5
  379. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  380. package/src/components/ui/drawer/Drawer.tsx +13 -27
  381. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  382. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  383. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  384. package/src/components/ui/form-field/FormField.tsx +10 -46
  385. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  386. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  387. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  388. package/src/components/ui/help-tip/index.ts +7 -1
  389. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  390. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  391. package/src/components/ui/icon-box/IconBox.tsx +10 -18
  392. package/src/components/ui/indicator/Indicator.stories.tsx +5 -4
  393. package/src/components/ui/indicator/Indicator.tsx +20 -14
  394. package/src/components/ui/input/Input.stories.tsx +3 -11
  395. package/src/components/ui/input/Input.test.tsx +2 -10
  396. package/src/components/ui/input/Input.tsx +10 -25
  397. package/src/components/ui/input/index.ts +7 -1
  398. package/src/components/ui/link/Link.stories.tsx +19 -7
  399. package/src/components/ui/list-item/ListItem.stories.tsx +17 -5
  400. package/src/components/ui/list-item/ListItem.tsx +2 -9
  401. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  402. package/src/components/ui/menu/Menu.test.tsx +2 -11
  403. package/src/components/ui/menu/Menu.tsx +12 -14
  404. package/src/components/ui/menu/index.ts +8 -1
  405. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  406. package/src/components/ui/modal/Modal.tsx +31 -19
  407. package/src/components/ui/pill/Pill.stories.tsx +111 -23
  408. package/src/components/ui/pill/Pill.test.tsx +101 -3
  409. package/src/components/ui/pill/Pill.tsx +168 -49
  410. package/src/components/ui/pill/index.ts +1 -1
  411. package/src/components/ui/popover/Popover.stories.tsx +6 -8
  412. package/src/components/ui/popover/Popover.test.tsx +1 -6
  413. package/src/components/ui/popover/Popover.tsx +20 -15
  414. package/src/components/ui/popover/index.ts +7 -1
  415. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  416. package/src/components/ui/progress/Progress.test.tsx +1 -5
  417. package/src/components/ui/progress/Progress.tsx +9 -6
  418. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  419. package/src/components/ui/radio/Radio.test.tsx +6 -2
  420. package/src/components/ui/radio/Radio.tsx +23 -22
  421. package/src/components/ui/section/Section.stories.tsx +12 -21
  422. package/src/components/ui/section/Section.test.tsx +1 -6
  423. package/src/components/ui/section/Section.tsx +1 -3
  424. package/src/components/ui/section/index.ts +1 -5
  425. package/src/components/ui/select/Select.stories.tsx +3 -13
  426. package/src/components/ui/select/Select.test.tsx +2 -11
  427. package/src/components/ui/select/Select.tsx +16 -15
  428. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  429. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  430. package/src/components/ui/skeleton/index.ts +1 -7
  431. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  432. package/src/components/ui/spinner/Spinner.tsx +4 -5
  433. package/src/components/ui/stack/Stack.stories.tsx +8 -8
  434. package/src/components/ui/stack/Stack.tsx +1 -1
  435. package/src/components/ui/surface/Surface.stories.tsx +92 -33
  436. package/src/components/ui/surface/Surface.test.tsx +88 -28
  437. package/src/components/ui/surface/Surface.tsx +37 -87
  438. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  439. package/src/components/ui/surface/index.ts +8 -0
  440. package/src/components/ui/surface/resolveSurface.ts +98 -0
  441. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  442. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  443. package/src/components/ui/switch/Switch.tsx +7 -6
  444. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  445. package/src/components/ui/tabs/Tabs.tsx +8 -21
  446. package/src/components/ui/tabs/index.ts +1 -7
  447. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  448. package/src/components/ui/tile/Tile.tsx +3 -0
  449. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  450. package/src/components/ui/toast/Toast.test.tsx +2 -8
  451. package/src/components/ui/toast/Toast.tsx +49 -57
  452. package/src/components/ui/toast/index.ts +0 -1
  453. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  454. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  455. package/src/components/ui/toolbar-button/ToolbarButton.tsx +38 -49
  456. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  457. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  458. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  459. package/src/components/ui/tooltip/index.ts +1 -1
  460. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  461. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  462. package/src/components/ui/trigger/index.ts +2 -0
  463. package/src/stories/PresetShowcase.stories.tsx +101 -48
  464. package/src/test/no-device-internals.test.ts +3 -3
  465. package/src/test/no-raw-color.test.ts +39 -0
  466. package/src/theme/Alpha.stories.tsx +103 -0
  467. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  468. package/src/theme/ColorPalettes.stories.tsx +62 -45
  469. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  470. package/src/theme/Depth.stories.tsx +132 -65
  471. package/src/theme/DepthCalibration.stories.tsx +23 -21
  472. package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
  473. package/src/theme/ThemeProvider.test.tsx +4 -4
  474. package/src/theme/barrel.ts +7 -6
  475. package/src/theme/color-stories.coverage.test.ts +1 -1
  476. package/src/theme/color-utils.ts +41 -29
  477. package/src/theme/config.completeness.test.ts +1 -1
  478. package/src/theme/config.ts +40 -0
  479. package/src/theme/elevation.test.ts +82 -56
  480. package/src/theme/elevation.ts +77 -561
  481. package/src/theme/global.css +45 -5
  482. package/src/theme/grey-ramp.test.ts +24 -8
  483. package/src/theme/lift.test.ts +39 -0
  484. package/src/theme/lift.ts +102 -0
  485. package/src/theme/materials.test.ts +5 -2
  486. package/src/theme/materials.ts +38 -4
  487. package/src/theme/native-theming-guard.test.ts +5 -5
  488. package/src/theme/presets/apply.ts +4 -0
  489. package/src/theme/presets/audiobook.ts +5 -2
  490. package/src/theme/presets/default.ts +4 -0
  491. package/src/theme/presets/index.ts +14 -1
  492. package/src/theme/presets/types.ts +1 -1
  493. package/src/theme/semantic-contrast.test.ts +13 -4
  494. package/src/theme/status-distinctness.test.ts +66 -0
  495. package/src/theme/subtle-tone-weight.test.ts +163 -0
  496. package/src/theme/surface-planes.test.ts +37 -0
  497. package/src/theme/surface-planes.ts +68 -0
  498. package/src/theme/tailwind-var-opacity.test.ts +163 -0
  499. package/src/theme/token-export-guard.test.ts +1 -3
  500. package/src/theme/tokens/primitives.test.ts +15 -7
  501. package/src/theme/tokens/primitives.ts +98 -33
  502. package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
  503. package/src/theme/tokens/semantic.ts +124 -54
  504. package/src/theme/wash-ladder.test.ts +58 -0
  505. package/src/theme/workout-tokens.ts +1 -7
  506. package/src/types/jest-axe.d.ts +2 -6
  507. package/src/types/raw-md.d.ts +5 -0
  508. package/src/utils/colors.alpha-adoption.test.ts +408 -0
  509. package/src/utils/colors.ts +4 -4
  510. package/src/utils/form.ts +1 -3
  511. package/src/utils/index.ts +2 -7
  512. package/src/utils/number-format.ts +17 -0
  513. package/src/utils/workout-format.ts +15 -6
  514. package/src/web-jsx/jsx-runtime.ts +1 -5
  515. package/tailwind.config.js +55 -9
  516. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  517. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  518. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  519. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  520. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  521. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  522. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  523. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  524. package/src/components/shell/DashboardShell.tsx +0 -75
  525. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  526. package/src/theme/elevation.stories.tsx +0 -346
  527. package/src/utils/useTheme.ts +0 -72
  528. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  529. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  530. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  531. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -48,8 +48,11 @@ const weeks: WeekRowProps[] = [
48
48
  ]
49
49
 
50
50
  const meta: Meta<typeof MesoCard> = {
51
- title: 'Workout/MesoCard',
51
+ title: 'Custom/Workout/MesoCard',
52
52
  component: MesoCard,
53
+ parameters: {
54
+ docs: { description: { component: 'Composes **Card** · **Badge** · **WeekRow**.' } },
55
+ },
53
56
  tags: ['autodocs'],
54
57
  argTypes: {
55
58
  goal: {
@@ -39,15 +39,9 @@ describe('MesoCard', () => {
39
39
  it('renders name, goal badge, and sub-header', () => {
40
40
  render(<MesoCard {...baseProps} />)
41
41
  expect(screen.getByTestId('meso-card')).toBeInTheDocument()
42
- expect(screen.getByTestId('meso-card-name')).toHaveTextContent(
43
- 'Accumulation',
44
- )
45
- expect(screen.getByTestId('meso-card-goal-badge')).toHaveTextContent(
46
- 'Hypertrophy',
47
- )
48
- expect(screen.getByTestId('meso-card-subheader')).toHaveTextContent(
49
- 'Upper/Lower · Weeks 1-4',
50
- )
42
+ expect(screen.getByTestId('meso-card-name')).toHaveTextContent('Accumulation')
43
+ expect(screen.getByTestId('meso-card-goal-badge')).toHaveTextContent('Hypertrophy')
44
+ expect(screen.getByTestId('meso-card-subheader')).toHaveTextContent('Upper/Lower · Weeks 1-4')
51
45
  })
52
46
 
53
47
  it('renders the 3px gradient top accent', () => {
@@ -80,10 +74,7 @@ describe('MesoCard', () => {
80
74
  it('reflects expanded state via aria-expanded', () => {
81
75
  const onToggle = vi.fn()
82
76
  render(<MesoCard {...baseProps} onToggle={onToggle} expanded />)
83
- expect(screen.getByTestId('meso-card-toggle')).toHaveAttribute(
84
- 'aria-expanded',
85
- 'true',
86
- )
77
+ expect(screen.getByTestId('meso-card-toggle')).toHaveAttribute('aria-expanded', 'true')
87
78
  })
88
79
 
89
80
  it('renders a static container when not expandable', () => {
@@ -108,7 +99,7 @@ describe('MesoCard', () => {
108
99
  { group: 'back', percentage: 50 },
109
100
  { group: 'legs', percentage: 30 },
110
101
  ]}
111
- />,
102
+ />
112
103
  )
113
104
  expect(screen.getByTestId('meso-card-heatmap')).toBeInTheDocument()
114
105
  expect(screen.getAllByTestId('meso-card-heatmap-cell')).toHaveLength(3)
@@ -132,9 +123,7 @@ describe('MesoCard', () => {
132
123
  describe('accessibility', () => {
133
124
  it('has an accessible label describing the meso', () => {
134
125
  render(<MesoCard {...baseProps} />)
135
- expect(
136
- screen.getByLabelText('Accumulation, Hypertrophy, Weeks 1-4'),
137
- ).toBeInTheDocument()
126
+ expect(screen.getByLabelText('Accumulation, Hypertrophy, Weeks 1-4')).toBeInTheDocument()
138
127
  })
139
128
 
140
129
  it('exposes the label on the button when expandable', () => {
@@ -161,7 +150,7 @@ describe('MesoCard', () => {
161
150
  { group: 'chest', percentage: 80 },
162
151
  { group: 'back', percentage: 50 },
163
152
  ]}
164
- />,
153
+ />
165
154
  )
166
155
  const results = await axe(container)
167
156
  expect(results).toHaveNoViolations()
@@ -9,6 +9,8 @@ import {
9
9
  MESO_ACCENT_GRADIENT_DARK,
10
10
  MESO_ACCENT_GRADIENT_LIGHT,
11
11
  } from '../../../theme/extracted-colors-dataviz'
12
+ import { getGlowShadow } from '../../../theme/elevation'
13
+ import { alpha } from '../../../utils/colors'
12
14
 
13
15
  const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
14
16
 
@@ -60,7 +62,7 @@ export interface MesoCardProps extends ViewProps {
60
62
  function heatmapColor(percentage: number): string {
61
63
  const clamped = Math.max(0, Math.min(100, percentage))
62
64
  const opacity = 0.12 + (clamped / 100) * 0.78
63
- return `rgba(255,121,0,${opacity.toFixed(3)})`
65
+ return alpha(BRAND_PRIMARY, opacity)
64
66
  }
65
67
 
66
68
  /**
@@ -119,7 +121,10 @@ export function MesoCard({
119
121
  })
120
122
 
121
123
  const header = (
122
- <View style={{ paddingHorizontal: 14, paddingTop: 12, paddingBottom: 10 }} testID="meso-card-body">
124
+ <View
125
+ style={{ paddingHorizontal: 14, paddingTop: 12, paddingBottom: 10 }}
126
+ testID="meso-card-body"
127
+ >
123
128
  <View className="flex-row items-center" style={{ gap: 8 }} testID="meso-card-header">
124
129
  <Text
125
130
  className="text-text-primary"
@@ -174,7 +179,10 @@ export function MesoCard({
174
179
  )
175
180
 
176
181
  return (
177
- <Animated.View style={highlighted ? { transform: [{ scale: 1.0 }] } : undefined} testID="meso-card-wrapper">
182
+ <Animated.View
183
+ style={highlighted ? { transform: [{ scale: 1.0 }] } : undefined}
184
+ testID="meso-card-wrapper"
185
+ >
178
186
  <Card
179
187
  variant="outline"
180
188
  elevation={2}
@@ -182,9 +190,8 @@ export function MesoCard({
182
190
  className={className}
183
191
  style={[
184
192
  { borderColor: borderColor as unknown as string },
185
- highlighted
186
- ? { boxShadow: '0 0 12px 2px rgba(255,121,0,0.25)' }
187
- : undefined,
193
+ // Highlight is emphasis, not depth: a brand glow through the shared builder.
194
+ highlighted ? getGlowShadow(BRAND_PRIMARY, 'subtle') : undefined,
188
195
  ]}
189
196
  testID="meso-card"
190
197
  {...props}
@@ -211,10 +218,7 @@ export function MesoCard({
211
218
  {header}
212
219
  </Pressable>
213
220
  ) : (
214
- <View
215
- accessibilityLabel={`${name}, ${goal}, ${weekRange}`}
216
- testID="meso-card-static"
217
- >
221
+ <View accessibilityLabel={`${name}, ${goal}, ${weekRange}`} testID="meso-card-static">
218
222
  {header}
219
223
  </View>
220
224
  )}
@@ -4,7 +4,7 @@ import { View } from 'react-native'
4
4
  import { MesoProgressBar, type Meso } from './MesoProgressBar'
5
5
 
6
6
  const meta: Meta<typeof MesoProgressBar> = {
7
- title: 'Workout/MesoProgressBar',
7
+ title: 'Custom/Workout/MesoProgressBar',
8
8
  component: MesoProgressBar,
9
9
  tags: ['autodocs'],
10
10
  argTypes: {
@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest'
2
2
  import { render, screen, fireEvent } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { MesoProgressBar, type Meso } from './MesoProgressBar'
5
+ import { resolveColor } from '../../../theme/resolve-color'
5
6
 
6
7
  const mesos: Meso[] = [
7
8
  { id: 'm1', name: 'Accumulation', weekCount: 4, status: 'completed' },
@@ -37,26 +38,34 @@ describe('MesoProgressBar', () => {
37
38
  })
38
39
 
39
40
  describe('status colors', () => {
40
- it('applies completed color at 0.3 alpha', () => {
41
+ it('tracks the completed segment on the success wash', () => {
41
42
  render(<MesoProgressBar mesos={mesos} activeMesoId={null} onMesoPress={vi.fn()} />)
42
43
  expect(screen.getByTestId('meso-segment-inner-m1')).toHaveStyle({
43
- backgroundColor: 'rgba(46,213,115,0.3)',
44
+ backgroundColor: resolveColor('status-success-muted'),
44
45
  })
45
46
  })
46
47
 
47
- it('applies current color at 0.5 alpha', () => {
48
+ it('tracks the current segment on the brand wash', () => {
48
49
  render(<MesoProgressBar mesos={mesos} activeMesoId={null} onMesoPress={vi.fn()} />)
49
50
  expect(screen.getByTestId('meso-segment-inner-m2')).toHaveStyle({
50
- backgroundColor: 'rgba(255,121,0,0.5)',
51
+ backgroundColor: resolveColor('brand-primary-strong'),
51
52
  })
52
53
  })
53
54
 
54
- it('applies upcoming color', () => {
55
+ it('tracks the upcoming segment on the neutral hairline wash', () => {
55
56
  render(<MesoProgressBar mesos={mesos} activeMesoId={null} onMesoPress={vi.fn()} />)
56
57
  expect(screen.getByTestId('meso-segment-inner-m3')).toHaveStyle({
57
- backgroundColor: 'rgba(255,255,255,0.06)',
58
+ backgroundColor: resolveColor('hairline-subtle'),
58
59
  })
59
60
  })
61
+
62
+ it('gives each status a distinct track, so the three never collapse', () => {
63
+ render(<MesoProgressBar mesos={mesos} activeMesoId={null} onMesoPress={vi.fn()} />)
64
+ const tracks = ['m1', 'm2', 'm3'].map(
65
+ (id) => screen.getByTestId(`meso-segment-inner-${id}`).style.backgroundColor
66
+ )
67
+ expect(new Set(tracks).size).toBe(3)
68
+ })
60
69
  })
61
70
 
62
71
  describe('current week progress fill', () => {
@@ -74,9 +83,7 @@ describe('MesoProgressBar', () => {
74
83
  })
75
84
 
76
85
  it('does not render a fill when current meso has no currentWeek', () => {
77
- const noProgress: Meso[] = [
78
- { id: 'c', name: 'Current', weekCount: 3, status: 'current' },
79
- ]
86
+ const noProgress: Meso[] = [{ id: 'c', name: 'Current', weekCount: 3, status: 'current' }]
80
87
  render(<MesoProgressBar mesos={noProgress} activeMesoId={null} onMesoPress={vi.fn()} />)
81
88
  expect(screen.queryByTestId('meso-segment-fill-c')).not.toBeInTheDocument()
82
89
  })
@@ -95,7 +102,7 @@ describe('MesoProgressBar', () => {
95
102
  render(<MesoProgressBar mesos={mesos} activeMesoId="m2" onMesoPress={vi.fn()} />)
96
103
  const style = screen.getByTestId('meso-segment-inner-m2').getAttribute('style') ?? ''
97
104
  expect(style).toContain('border-top-width: 2px')
98
- expect(style).toContain('border-top-color: rgb(255, 121, 0)')
105
+ expect(style).toContain(`border-top-color: ${resolveColor('brand-primary')}`)
99
106
  })
100
107
 
101
108
  it('does not apply a border to inactive segments', () => {
@@ -132,7 +139,7 @@ describe('MesoProgressBar', () => {
132
139
 
133
140
  it('has no accessibility violations', async () => {
134
141
  const { container } = render(
135
- <MesoProgressBar mesos={mesos} activeMesoId="m2" onMesoPress={vi.fn()} />,
142
+ <MesoProgressBar mesos={mesos} activeMesoId="m2" onMesoPress={vi.fn()} />
136
143
  )
137
144
  const results = await axe(container)
138
145
  expect(results).toHaveNoViolations()
@@ -1,9 +1,7 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { useState } from 'react'
3
3
  import { View, Pressable, Animated, type ViewProps } from 'react-native'
4
- import { getSemanticColors } from '../../../theme/tokens/semantic'
5
-
6
- const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
4
+ import { resolveColor } from '../../../theme/resolve-color'
7
5
 
8
6
  export type MesoStatus = 'completed' | 'current' | 'upcoming'
9
7
 
@@ -26,10 +24,15 @@ export interface MesoProgressBarProps extends ViewProps {
26
24
  className?: string
27
25
  }
28
26
 
29
- const segmentBgColors: Record<MesoStatus, string> = {
30
- completed: 'rgba(46,213,115,0.3)',
31
- current: 'rgba(255,121,0,0.5)',
32
- upcoming: 'rgba(255,255,255,0.06)',
27
+ // The segment track is a status wash, and the three states must stay a legible
28
+ // ramp rather than one weight in three hues.
29
+ const segmentTrackToken: Record<
30
+ MesoStatus,
31
+ 'status-success-muted' | 'brand-primary-strong' | 'hairline-subtle'
32
+ > = {
33
+ completed: 'status-success-muted',
34
+ current: 'brand-primary-strong',
35
+ upcoming: 'hairline-subtle',
33
36
  }
34
37
 
35
38
  function clampProgress(currentWeek: number | undefined, weekCount: number): number {
@@ -46,6 +49,7 @@ interface SegmentProps {
46
49
 
47
50
  function MesoSegment({ meso, isActive, onPress }: SegmentProps) {
48
51
  const [pressScale] = useState(() => new Animated.Value(1))
52
+ const brandPrimary = resolveColor('brand-primary')
49
53
 
50
54
  const handlePressIn = () => {
51
55
  Animated.timing(pressScale, {
@@ -63,9 +67,7 @@ function MesoSegment({ meso, isActive, onPress }: SegmentProps) {
63
67
  }).start()
64
68
  }
65
69
 
66
- const progress = meso.status === 'current'
67
- ? clampProgress(meso.currentWeek, meso.weekCount)
68
- : 0
70
+ const progress = meso.status === 'current' ? clampProgress(meso.currentWeek, meso.weekCount) : 0
69
71
 
70
72
  // weekCount drives the segment's flex width; guard 0 / negative so flex stays
71
73
  // a valid positive value and the segment never collapses out of the layout.
@@ -87,13 +89,13 @@ function MesoSegment({ meso, isActive, onPress }: SegmentProps) {
87
89
  height: 8,
88
90
  borderRadius: 4,
89
91
  overflow: 'hidden',
90
- backgroundColor: segmentBgColors[meso.status],
92
+ backgroundColor: resolveColor(segmentTrackToken[meso.status]),
91
93
  transform: [{ scale: pressScale }],
92
94
  },
93
95
  isActive
94
96
  ? {
95
97
  borderWidth: 2,
96
- borderColor: BRAND_PRIMARY,
98
+ borderColor: brandPrimary,
97
99
  transform: [{ scale: 1.02 }, { scale: pressScale }],
98
100
  }
99
101
  : undefined,
@@ -108,7 +110,7 @@ function MesoSegment({ meso, isActive, onPress }: SegmentProps) {
108
110
  top: 0,
109
111
  bottom: 0,
110
112
  width: `${progress * 100}%`,
111
- backgroundColor: BRAND_PRIMARY,
113
+ backgroundColor: brandPrimary,
112
114
  }}
113
115
  testID={`meso-segment-fill-${meso.id}`}
114
116
  />
@@ -3,8 +3,9 @@ import { View } from 'react-native'
3
3
  import { MesoStatusCard } from './MesoStatusCard'
4
4
 
5
5
  const meta: Meta<typeof MesoStatusCard> = {
6
- title: 'Workout/MesoStatusCard',
6
+ title: 'Custom/Workout/MesoStatusCard',
7
7
  component: MesoStatusCard,
8
+ parameters: { docs: { description: { component: 'Composes **Card** · **StatusDot**.' } } },
8
9
  tags: ['autodocs'],
9
10
  argTypes: {
10
11
  mesoName: {
@@ -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
  })
@@ -5,10 +5,13 @@ import { Card } from '../../ui/card'
5
5
  import { StatusDot } from './StatusDot'
6
6
  import { resolveColor } from '../../../theme/resolve-color'
7
7
  import { getSemanticColors } from '../../../theme/tokens/semantic'
8
+ import { primitiveColors } from '../../../theme/tokens/primitives'
8
9
  import {
9
10
  MESO_ACCENT_GRADIENT_DARK,
10
11
  MESO_ACCENT_GRADIENT_LIGHT,
11
12
  } from '../../../theme/extracted-colors-dataviz'
13
+ import { liftStyle } from '../../../theme/lift'
14
+ import { alpha } from '../../../utils/colors'
12
15
 
13
16
  const t = getSemanticColors('dark')
14
17
 
@@ -31,8 +34,7 @@ const ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY, BRAND_PRIMARY_LIGHT]
31
34
  const CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor('surface-elevated')} 0%, ${resolveColor('surface-raised')} 100%)`
32
35
 
33
36
  /** Gauge track gradient (teal -> amber -> red) at 0.25 alpha. */
34
- const GAUGE_GRADIENT =
35
- 'linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(249,180,21,0.25) 50%, rgba(209,67,67,0.25) 100%)'
37
+ const GAUGE_GRADIENT = `linear-gradient(90deg, ${alpha(SUCCESS, 0.25)} 0%, ${alpha(WARNING, 0.25)} 50%, ${alpha(ERROR, 0.25)} 100%)`
36
38
 
37
39
  export type MesoStatusBadgeVariant = 'success' | 'warning' | 'error'
38
40
 
@@ -40,9 +42,9 @@ const STATUS_VARIANTS: Record<
40
42
  MesoStatusBadgeVariant,
41
43
  { bg: string; border: string; text: string }
42
44
  > = {
43
- success: { bg: 'rgba(46,213,115,0.15)', border: 'rgba(46,213,115,0.3)', text: SUCCESS },
44
- warning: { bg: 'rgba(249,180,21,0.15)', border: 'rgba(249,180,21,0.3)', text: WARNING },
45
- error: { bg: 'rgba(209,67,67,0.15)', border: 'rgba(209,67,67,0.25)', text: ERROR },
45
+ success: { bg: alpha(SUCCESS, 0.15), border: alpha(SUCCESS, 0.3), text: SUCCESS },
46
+ warning: { bg: alpha(WARNING, 0.15), border: alpha(WARNING, 0.3), text: WARNING },
47
+ error: { bg: alpha(ERROR, 0.15), border: alpha(ERROR, 0.25), text: ERROR },
46
48
  }
47
49
 
48
50
  export interface MesoStatusBadge {
@@ -117,23 +119,17 @@ function getGaugeZoneColor(level: number): string {
117
119
  /** Splits `text` into nodes, bolding any segment that exactly matches a highlight. */
118
120
  function renderCoachingText(text: string, highlights?: string[]): ReactNode {
119
121
  if (!highlights || highlights.length === 0) return text
120
- const escaped = highlights
121
- .filter(Boolean)
122
- .map((h) => h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
122
+ const escaped = highlights.filter(Boolean).map((h) => h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
123
123
  if (escaped.length === 0) return text
124
124
  const regex = new RegExp(`(${escaped.join('|')})`, 'g')
125
125
  return text.split(regex).map((part, index) =>
126
126
  highlights.includes(part) ? (
127
- <Text
128
- key={`${part}-${index}`}
129
- className="text-text-primary"
130
- style={{ fontWeight: '700' }}
131
- >
127
+ <Text key={`${part}-${index}`} className="text-text-primary" style={{ fontWeight: '700' }}>
132
128
  {part}
133
129
  </Text>
134
130
  ) : (
135
131
  <Fragment key={`t-${index}`}>{part}</Fragment>
136
- ),
132
+ )
137
133
  )
138
134
  }
139
135
 
@@ -216,10 +212,7 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
216
212
  aria-valuemax={100}
217
213
  testID="meso-status-card-gauge"
218
214
  >
219
- <View
220
- className="flex-row items-center justify-between"
221
- accessibilityElementsHidden
222
- >
215
+ <View className="flex-row items-center justify-between" accessibilityElementsHidden>
223
216
  <Text
224
217
  className="text-text-secondary"
225
218
  style={{
@@ -265,7 +258,7 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
265
258
  bottom: 0,
266
259
  width: 1,
267
260
  marginLeft: -0.5,
268
- backgroundColor: 'rgba(255,255,255,0.3)',
261
+ backgroundColor: alpha(primitiveColors.white, 0.3),
269
262
  }}
270
263
  testID="meso-status-card-gauge-center"
271
264
  />
@@ -281,7 +274,8 @@ function Gauge({ gauge }: { gauge: MesoStatusGauge }) {
281
274
  borderRadius: 9999,
282
275
  borderWidth: 2,
283
276
  backgroundColor: markerColor,
284
- boxShadow: '0 0 4px rgba(0,0,0,0.5)',
277
+ // A knob resting on the gauge: lift, with the ring as its edge.
278
+ ...liftStyle(1, 'dark', { rim: 0 }),
285
279
  }}
286
280
  testID="meso-status-card-gauge-marker"
287
281
  />
@@ -353,10 +347,7 @@ export function MesoStatusCard({
353
347
 
354
348
  <View style={{ padding: 14, gap: 14 }} testID="meso-status-card-body">
355
349
  <View testID="meso-status-card-header">
356
- <View
357
- className="flex-row items-center justify-between"
358
- style={{ gap: 8 }}
359
- >
350
+ <View className="flex-row items-center justify-between" style={{ gap: 8 }}>
360
351
  <Text
361
352
  className="text-text-primary"
362
353
  style={{
@@ -385,11 +376,7 @@ export function MesoStatusCard({
385
376
  </View>
386
377
 
387
378
  {metrics.length > 0 && (
388
- <View
389
- className="flex-row flex-wrap"
390
- style={{ gap: 8 }}
391
- testID="meso-status-card-metrics"
392
- >
379
+ <View className="flex-row flex-wrap" style={{ gap: 8 }} testID="meso-status-card-metrics">
393
380
  {metrics.map((metric, index) => (
394
381
  <MetricCell key={`${metric.label}-${index}`} metric={metric} />
395
382
  ))}
@@ -407,9 +394,9 @@ export function MesoStatusCard({
407
394
  {coaching != null && (
408
395
  <View
409
396
  style={{
410
- backgroundColor: 'rgba(249,180,21,0.06)',
397
+ backgroundColor: alpha(WARNING, 0.06),
411
398
  borderWidth: 1,
412
- borderColor: 'rgba(249,180,21,0.15)',
399
+ borderColor: alpha(WARNING, 0.15),
413
400
  borderRadius: 8,
414
401
  paddingVertical: 10,
415
402
  paddingHorizontal: 12,
@@ -434,9 +421,9 @@ export function MesoStatusCard({
434
421
  className="flex-row items-center"
435
422
  style={{
436
423
  gap: 8,
437
- backgroundColor: 'rgba(46,213,115,0.06)',
424
+ backgroundColor: alpha(SUCCESS, 0.06),
438
425
  borderWidth: 1,
439
- borderColor: 'rgba(46,213,115,0.2)',
426
+ borderColor: alpha(SUCCESS, 0.2),
440
427
  borderRadius: 8,
441
428
  paddingVertical: 10,
442
429
  paddingHorizontal: 12,
@@ -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: () => (