@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
@@ -1,9 +1,10 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, type ViewProps } from 'react-native'
2
+ import { View, type ViewProps } from 'react-native'
3
3
  import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
4
4
  import { greyRamp } from '../../../theme/tokens/primitives'
5
5
  import { ZoneTrack } from './ZoneTrack'
6
6
  import { DataRow } from '../../ui/data-row/DataRow'
7
+ import { Typography } from '../Typography'
7
8
  import type { VolumeLandmarks } from './muscleTaxonomy'
8
9
 
9
10
  // Reuse the canonical BodyMap volume HEAT scale (the `divergingScale` under →
@@ -12,9 +13,11 @@ import type { VolumeLandmarks } from './muscleTaxonomy'
12
13
  const HEAT = WORKOUT_TOKENS.heatmap
13
14
 
14
15
  // The muted, un-reached track colour — the same grey step ZoneTrack defaults
15
- // to, so the bar sits on the shared gauge-track surface.
16
+ // to, so the bar sits on the shared gauge-track surface. Kept as a ramp step
17
+ // rather than `resolveColor('border-prominent')`: ZoneTrack takes literal hex
18
+ // only, never a `var()` ref. FINDING for E3: the gauge-track grey has no
19
+ // semantic token, and ZoneTrack (B5) cannot consume one until it can.
16
20
  const NEUTRAL_TRACK = greyRamp[800]
17
- const MONO = 'monospace'
18
21
 
19
22
  export type VolumeZone = 'under' | 'maintenance' | 'productive' | 'approaching' | 'over'
20
23
 
@@ -113,12 +116,18 @@ export function VolumeLandmarkBar({
113
116
  <DataRow
114
117
  label={muscle}
115
118
  value={
116
- <Text
119
+ // `mono` at `sm` matches DataRow's own 14px label, so the header lockup
120
+ // stays at one type height. The face moves from bare `monospace` to the
121
+ // `font-mono` token stack.
122
+ <Typography
123
+ variant="mono"
124
+ color="inherit"
125
+ className="text-sm font-bold"
126
+ style={{ color: fillColor }}
117
127
  testID="volume-landmark-pct"
118
- style={{ fontSize: 14, fontWeight: '700', fontFamily: MONO, color: fillColor }}
119
128
  >
120
129
  {pct}%
121
- </Text>
130
+ </Typography>
122
131
  }
123
132
  className="py-0"
124
133
  testID="volume-landmark-header"
@@ -3,8 +3,11 @@ import { View } from 'react-native'
3
3
  import { WeekRow } from './WeekRow'
4
4
 
5
5
  const meta: Meta<typeof WeekRow> = {
6
- title: 'Workout/WeekRow',
6
+ title: 'Custom/Workout/WeekRow',
7
7
  component: WeekRow,
8
+ parameters: {
9
+ docs: { description: { component: 'Composes **WorkoutPill** · **IntensityBar**.' } },
10
+ },
8
11
  tags: ['autodocs'],
9
12
  argTypes: {
10
13
  weekNumber: { control: 'number', description: 'Week index (1-based)' },
@@ -3,6 +3,9 @@ import { render, screen, fireEvent } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { WeekRow } from './WeekRow'
5
5
  import type { WeekRowWorkout } from './WeekRow'
6
+ import { resolveColor } from '../../../theme/resolve-color'
7
+ import { WORKOUT_PILL_DELOAD } from '../../../theme/extracted-colors-dataviz'
8
+ import { alpha } from '../../../utils/colors'
6
9
 
7
10
  const workouts: WeekRowWorkout[] = [
8
11
  { name: 'Upper', status: 'completed' },
@@ -57,11 +60,11 @@ describe('WeekRow', () => {
57
60
  expect(screen.queryByTestId('week-row-current-dot')).not.toBeInTheDocument()
58
61
  })
59
62
 
60
- it('applies the current-week tint and left border', () => {
63
+ it('applies the current-week tint and left border from the brand tokens', () => {
61
64
  render(<WeekRow {...baseProps} isCurrent />)
62
65
  const style = screen.getByTestId('week-row').getAttribute('style') ?? ''
63
- expect(style).toContain('background-color')
64
- expect(style).toContain('border-left')
66
+ expect(style).toContain(`background-color: ${resolveColor('brand-primary-subtle')}`)
67
+ expect(style).toContain(`border-left-color: ${resolveColor('brand-primary')}`)
65
68
  })
66
69
  })
67
70
 
@@ -71,22 +74,24 @@ describe('WeekRow', () => {
71
74
  expect(screen.getAllByLabelText(/workout, deload/)).toHaveLength(3)
72
75
  })
73
76
 
74
- it('applies a purple tint to the row', () => {
77
+ it('washes the row with the same deload pin WorkoutPill uses', () => {
75
78
  render(<WeekRow {...baseProps} isDeload />)
76
79
  const style = screen.getByTestId('week-row').getAttribute('style') ?? ''
77
- expect(style).toContain('background-color')
80
+ expect(style).toContain(`background-color: ${alpha(WORKOUT_PILL_DELOAD, 0.06)}`)
81
+ })
82
+
83
+ it('lets a current deload week keep the brand rail over the deload wash', () => {
84
+ render(<WeekRow {...baseProps} isDeload isCurrent />)
85
+ const style = screen.getByTestId('week-row').getAttribute('style') ?? ''
86
+ expect(style).toContain(`background-color: ${resolveColor('brand-primary-subtle')}`)
87
+ expect(style).toContain(`border-left-color: ${resolveColor('brand-primary')}`)
78
88
  })
79
89
  })
80
90
 
81
91
  describe('interaction', () => {
82
92
  it('invokes onPress for a pressable workout', () => {
83
93
  const onPress = vi.fn()
84
- render(
85
- <WeekRow
86
- {...baseProps}
87
- workouts={[{ name: 'Upper', status: 'current', onPress }]}
88
- />,
89
- )
94
+ render(<WeekRow {...baseProps} workouts={[{ name: 'Upper', status: 'current', onPress }]} />)
90
95
  fireEvent.click(screen.getByTestId('workout-pill-pressable'))
91
96
  expect(onPress).toHaveBeenCalledOnce()
92
97
  })
@@ -95,9 +100,7 @@ describe('WeekRow', () => {
95
100
  describe('accessibility', () => {
96
101
  it('has a descriptive accessibility label', () => {
97
102
  render(<WeekRow {...baseProps} />)
98
- expect(
99
- screen.getByLabelText('Week 1 of 4, 3 workouts'),
100
- ).toBeInTheDocument()
103
+ expect(screen.getByLabelText('Week 1 of 4, 3 workouts')).toBeInTheDocument()
101
104
  })
102
105
 
103
106
  it('has no accessibility violations', async () => {
@@ -108,7 +111,7 @@ describe('WeekRow', () => {
108
111
 
109
112
  it('has no accessibility violations as a current deload week', async () => {
110
113
  const { container } = render(
111
- <WeekRow {...baseProps} isCurrent isDeload intensityThreshold={0.8} />,
114
+ <WeekRow {...baseProps} isCurrent isDeload intensityThreshold={0.8} />
112
115
  )
113
116
  const results = await axe(container)
114
117
  expect(results).toHaveNoViolations()
@@ -1,10 +1,17 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, type ViewProps } from 'react-native'
2
+ import { View, type ViewProps } from 'react-native'
3
3
  import { WorkoutPill, type WorkoutPillStatus } from './WorkoutPill'
4
4
  import { IntensityBar } from './IntensityBar'
5
- import { getSemanticColors } from '../../../theme/tokens/semantic'
5
+ import { Typography } from '../Typography'
6
+ import { resolveColor } from '../../../theme/resolve-color'
7
+ import { WORKOUT_PILL_DELOAD } from '../../../theme/extracted-colors-dataviz'
8
+ import { alpha } from '../../../utils/colors'
6
9
 
7
- const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
10
+ // Deload magenta has no semantic token — it is a ramp pin shared with WorkoutPill
11
+ // (`extracted-colors-dataviz`). The row wash must track the pill exactly, so it
12
+ // is derived from the same pin rather than mixed independently. FINDING for E3:
13
+ // the deload role needs a `-subtle` token like every other status.
14
+ const DELOAD_ROW_WASH = alpha(WORKOUT_PILL_DELOAD, 0.06)
8
15
 
9
16
  export interface WeekRowWorkout {
10
17
  name: string
@@ -58,6 +65,7 @@ export function WeekRow({
58
65
  className,
59
66
  ...props
60
67
  }: WeekRowProps) {
68
+ const brandPrimary = resolveColor('brand-primary')
61
69
  const rowStyle: Record<string, unknown> = {
62
70
  flexDirection: 'row' as const,
63
71
  alignItems: 'center' as const,
@@ -67,13 +75,14 @@ export function WeekRow({
67
75
  }
68
76
 
69
77
  if (isDeload) {
70
- rowStyle.backgroundColor = 'rgba(186,41,150,0.06)'
78
+ rowStyle.backgroundColor = DELOAD_ROW_WASH
71
79
  }
72
80
 
81
+ // Current wins over deload: the active week keeps the brand rail and wash.
73
82
  if (isCurrent) {
74
- rowStyle.backgroundColor = 'rgba(255,121,0,0.04)'
83
+ rowStyle.backgroundColor = resolveColor('brand-primary-subtle')
75
84
  rowStyle.borderLeftWidth = 2
76
- rowStyle.borderLeftColor = BRAND_PRIMARY
85
+ rowStyle.borderLeftColor = brandPrimary
77
86
  }
78
87
 
79
88
  return (
@@ -94,24 +103,22 @@ export function WeekRow({
94
103
  width: 6,
95
104
  height: 6,
96
105
  borderRadius: 3,
97
- backgroundColor: BRAND_PRIMARY,
106
+ backgroundColor: brandPrimary,
98
107
  }}
99
108
  accessibilityElementsHidden
100
109
  testID="week-row-current-dot"
101
110
  />
102
111
  )}
103
- <Text
104
- className={isCurrent ? undefined : 'text-text-primary'}
105
- style={{
106
- fontSize: 13,
107
- fontWeight: '700',
108
- fontFamily: 'Inter, sans-serif',
109
- color: isCurrent ? BRAND_PRIMARY : undefined,
110
- }}
112
+ {/* `boldLabel` is 12px/700 Inter — the row-label form. The week number was
113
+ 13px, which is off the type scale (TOKENS.md §4). */}
114
+ <Typography
115
+ variant="boldLabel"
116
+ color={isCurrent ? 'inherit' : 'primary'}
117
+ className={isCurrent ? 'text-brand-primary' : undefined}
111
118
  accessibilityElementsHidden
112
119
  >
113
120
  {`W${weekNumber}`}
114
- </Text>
121
+ </Typography>
115
122
  </View>
116
123
 
117
124
  <View
@@ -3,8 +3,9 @@ import { View } from 'react-native'
3
3
  import { WeightBadge } from './WeightBadge'
4
4
 
5
5
  const meta: Meta<typeof WeightBadge> = {
6
- title: 'Components/Atoms/WeightBadge',
6
+ title: 'Custom/Workout/WeightBadge',
7
7
  component: WeightBadge,
8
+ parameters: { docs: { description: { component: 'Composes **Icons** · **BaseBadge**.' } } },
8
9
  tags: ['autodocs'],
9
10
  argTypes: {
10
11
  value: { control: 'number', description: 'Weight value' },
@@ -3,6 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { WeightBadge } from './WeightBadge'
5
5
  import { getSemanticColors } from '../../../theme/tokens/semantic'
6
+ import { resolveColor } from '../../../theme/resolve-color'
6
7
 
7
8
  // Read from the token, not pinned: these used to be hand-copied hexes and
8
9
  // silently desynced when borders became alpha hairlines (TD-07.16).
@@ -47,18 +48,14 @@ describe('WeightBadge', () => {
47
48
  describe('reps (rep-max context)', () => {
48
49
  it('shows the estimated 1RM label when reps is omitted', () => {
49
50
  render(<WeightBadge value={315} unit="lbs" />)
50
- expect(
51
- screen.getByLabelText('Estimated one rep max: 315 lbs'),
52
- ).toBeInTheDocument()
51
+ expect(screen.getByLabelText('Estimated one rep max: 315 lbs')).toBeInTheDocument()
53
52
  expect(screen.queryByTestId('weight-badge-repmax')).not.toBeInTheDocument()
54
53
  })
55
54
 
56
55
  it('shows the rep-max qualifier and label when reps is provided', () => {
57
56
  render(<WeightBadge value={275} unit="lbs" reps={5} />)
58
57
  expect(screen.getByTestId('weight-badge-repmax')).toHaveTextContent('5RM')
59
- expect(
60
- screen.getByLabelText('5 rep max: 275 lbs'),
61
- ).toBeInTheDocument()
58
+ expect(screen.getByLabelText('5 rep max: 275 lbs')).toBeInTheDocument()
62
59
  })
63
60
  })
64
61
 
@@ -127,27 +124,63 @@ describe('WeightBadge', () => {
127
124
  })
128
125
  })
129
126
 
127
+ // Regression (E3 B3): the value, rep-max and delta colours were read through
128
+ // `getSemanticColors('dark')` / `greyRamp` ON THE RENDER PATH, which pins the badge
129
+ // to dark-mode hex and leaves it unreadable once `.light` is on <html>. Each of these
130
+ // asserts the `var()` reference `resolveColor` returns on web; every one of them
131
+ // fails against the old literal.
132
+ describe('theme-correct colours', () => {
133
+ it('draws a PR value from the brand-primary token, not a dark-mode hex', () => {
134
+ render(<WeightBadge value={315} isPr />)
135
+ expect(screen.getByText(/315 lbs/)).toHaveStyle({ color: resolveColor('brand-primary') })
136
+ })
137
+
138
+ // A load value is not a heading. Typography's h-variants render role="heading",
139
+ // which both misreports the badge and breaks the parity layer's text selector.
140
+ it('renders the value as text, not a heading', () => {
141
+ render(<WeightBadge value={225} />)
142
+ expect(screen.queryByRole('heading')).not.toBeInTheDocument()
143
+ expect(screen.getByText('225 lbs').getAttribute('dir')).toBe('auto')
144
+ })
145
+
146
+ it('draws a non-PR value from the text-secondary token', () => {
147
+ render(<WeightBadge value={225} />)
148
+ expect(screen.getByText('225 lbs')).toHaveStyle({ color: resolveColor('text-secondary') })
149
+ })
150
+
151
+ it('tints the rep-max qualifier with the same token as the value', () => {
152
+ render(<WeightBadge value={275} reps={5} isPr />)
153
+ expect(screen.getByTestId('weight-badge-repmax')).toHaveStyle({
154
+ color: resolveColor('brand-primary'),
155
+ })
156
+ })
157
+
158
+ it.each([
159
+ [3, 'result-improve'],
160
+ [-2, 'result-degrade'],
161
+ ] as const)('draws a %s%% delta from the %s token', (delta, token) => {
162
+ render(<WeightBadge value={225} delta={delta} />)
163
+ expect(screen.getByTestId('weight-badge-delta')).toHaveStyle({
164
+ color: resolveColor(token),
165
+ })
166
+ })
167
+ })
168
+
130
169
  describe('accessibility', () => {
131
170
  it('has no accessibility violations', async () => {
132
- const { container } = render(
133
- <WeightBadge value={225} isPr onPress={() => {}} />,
134
- )
171
+ const { container } = render(<WeightBadge value={225} isPr onPress={() => {}} />)
135
172
  const results = await axe(container)
136
173
  expect(results).toHaveNoViolations()
137
174
  })
138
175
 
139
176
  it('has no accessibility violations with delta', async () => {
140
- const { container } = render(
141
- <WeightBadge value={225} delta={3} onPress={() => {}} />,
142
- )
177
+ const { container } = render(<WeightBadge value={225} delta={3} onPress={() => {}} />)
143
178
  const results = await axe(container)
144
179
  expect(results).toHaveNoViolations()
145
180
  })
146
181
 
147
182
  it('has no accessibility violations as static badge', async () => {
148
- const { container } = render(
149
- <WeightBadge value={315} unit="kg" isPr delta={-2} />,
150
- )
183
+ const { container } = render(<WeightBadge value={315} unit="kg" isPr delta={-2} />)
151
184
  const results = await axe(container)
152
185
  expect(results).toHaveNoViolations()
153
186
  })
@@ -155,15 +188,13 @@ describe('WeightBadge', () => {
155
188
  it('includes delta in the e1RM accessibility label', () => {
156
189
  render(<WeightBadge value={225} delta={5} />)
157
190
  expect(
158
- screen.getByLabelText('Estimated one rep max: 225 lbs, +5% change'),
191
+ screen.getByLabelText('Estimated one rep max: 225 lbs, +5% change')
159
192
  ).toBeInTheDocument()
160
193
  })
161
194
 
162
195
  it('includes delta in the rep-max accessibility label', () => {
163
196
  render(<WeightBadge value={275} reps={5} delta={-3} />)
164
- expect(
165
- screen.getByLabelText('5 rep max: 275 lbs, -3% change'),
166
- ).toBeInTheDocument()
197
+ expect(screen.getByLabelText('5 rep max: 275 lbs, -3% change')).toBeInTheDocument()
167
198
  })
168
199
  })
169
200
  })
@@ -1,5 +1,5 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { Text } from 'react-native'
2
+ import { Typography } from '../Typography'
3
3
  import { DumbbellIcon } from './icons'
4
4
  import {
5
5
  BaseBadge,
@@ -7,13 +7,11 @@ import {
7
7
  type BaseBadgeSize,
8
8
  type BaseBadgeProps,
9
9
  } from './BaseBadge'
10
- import { getSemanticColors } from '../../../theme/tokens/semantic'
11
- import { greyRamp } from '../../../theme/tokens/primitives'
10
+ import { resolveColor } from '../../../theme/resolve-color'
12
11
 
13
12
  export type WeightBadgeSize = BaseBadgeSize
14
13
 
15
- export interface WeightBadgeProps
16
- extends Omit<BaseBadgeProps, 'variant' | 'icon' | 'children'> {
14
+ export interface WeightBadgeProps extends Omit<BaseBadgeProps, 'variant' | 'icon' | 'children'> {
17
15
  value: number
18
16
  unit?: 'lbs' | 'kg'
19
17
  /**
@@ -31,6 +29,9 @@ export interface WeightBadgeProps
31
29
  className?: string
32
30
  }
33
31
 
32
+ /**
33
+ * @deprecated Use `<Pill tone="neutral">` — removed after AW-127 consumer migration.
34
+ */
34
35
  export function WeightBadge({
35
36
  value,
36
37
  unit = 'lbs',
@@ -44,15 +45,14 @@ export function WeightBadge({
44
45
  ...props
45
46
  }: WeightBadgeProps) {
46
47
  const config = baseBadgeSizeConfig[size]
47
- const textColor = isPr ? getSemanticColors('dark')['brand-primary'] : greyRamp[400]
48
+ const textColor = isPr ? resolveColor('brand-primary') : resolveColor('text-secondary')
48
49
  // Dumbbell reads clearly as a weight/strength metric and is unit-agnostic
49
50
  // across estimated-1RM and N-rep-max contexts (vs. TrendingUp which implies
50
51
  // change/trend rather than a load value).
51
52
  const iconColor = textColor
52
53
 
53
54
  const repMaxClause = reps != null ? `${reps} rep max` : 'Estimated one rep max'
54
- const deltaLabel =
55
- delta != null ? `, ${delta >= 0 ? '+' : ''}${delta}% change` : ''
55
+ const deltaLabel = delta != null ? `, ${delta >= 0 ? '+' : ''}${delta}% change` : ''
56
56
  const fullLabel = `${repMaxClause}: ${value} ${unit}${deltaLabel}`
57
57
 
58
58
  return (
@@ -70,22 +70,24 @@ export function WeightBadge({
70
70
  testID="weight-badge"
71
71
  {...props}
72
72
  >
73
- <Text
74
- style={{
75
- fontFamily: '"Space Grotesk", sans-serif',
76
- fontWeight: '600',
77
- fontSize: config.fontSize,
78
- color: textColor,
79
- }}
73
+ {/* `body2` + `font-heading`, NOT `h6`: a load value is not a heading, and the
74
+ heading variants make Typography render `role="heading"`. The size prop still
75
+ drives the step, so the fontSize stays computed (BaseBadge's 9/10/12 config). */}
76
+ <Typography
77
+ variant="body2"
78
+ color="inherit"
79
+ className="font-heading font-semibold leading-[normal]"
80
+ style={{ fontSize: config.fontSize, color: textColor }}
80
81
  >
81
82
  {isPr ? '✳ ' : ''}
82
83
  {value} {unit}
83
- </Text>
84
+ </Typography>
84
85
  {reps != null && (
85
- <Text
86
+ <Typography
87
+ variant="body2"
88
+ color="inherit"
89
+ className="font-heading font-semibold leading-[normal]"
86
90
  style={{
87
- fontFamily: '"Space Grotesk", sans-serif',
88
- fontWeight: '600',
89
91
  fontSize: config.fontSize,
90
92
  marginLeft: 2,
91
93
  color: textColor,
@@ -94,24 +96,23 @@ export function WeightBadge({
94
96
  testID="weight-badge-repmax"
95
97
  >
96
98
  {reps}RM
97
- </Text>
99
+ </Typography>
98
100
  )}
99
101
  {delta != null && (
100
- <Text
102
+ <Typography
103
+ variant="body2"
104
+ color="inherit"
105
+ className="font-heading leading-[normal]"
101
106
  style={{
102
- fontFamily: '"Space Grotesk", sans-serif',
103
107
  fontSize: config.fontSize,
104
108
  marginLeft: 4,
105
- color:
106
- delta >= 0
107
- ? getSemanticColors('dark')['result-improve']
108
- : getSemanticColors('dark')['result-degrade'],
109
+ color: resolveColor(delta >= 0 ? 'result-improve' : 'result-degrade'),
109
110
  }}
110
111
  testID="weight-badge-delta"
111
112
  >
112
113
  {delta >= 0 ? '+' : ''}
113
114
  {delta}%
114
- </Text>
115
+ </Typography>
115
116
  )}
116
117
  </BaseBadge>
117
118
  )
@@ -4,8 +4,13 @@ import { WorkoutCard } from './WorkoutCard'
4
4
  import type { ExerciseCardProps } from './ExerciseCard'
5
5
 
6
6
  const meta: Meta<typeof WorkoutCard> = {
7
- title: 'Workout/WorkoutCard',
7
+ title: 'Custom/Workout/WorkoutCard',
8
8
  component: WorkoutCard,
9
+ parameters: {
10
+ docs: {
11
+ description: { component: 'Composes **Card** · **ExerciseCard** · **MuscleGroupChip**.' },
12
+ },
13
+ },
9
14
  tags: ['autodocs'],
10
15
  argTypes: {
11
16
  status: {
@@ -3,6 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { WorkoutCard, type WorkoutCardProps } from './WorkoutCard'
5
5
  import type { ExerciseCardProps } from './ExerciseCard'
6
+ import { resolveColor } from '../../../theme/resolve-color'
6
7
 
7
8
  const baseProps: WorkoutCardProps = {
8
9
  name: 'Upper A',
@@ -110,6 +111,23 @@ describe('WorkoutCard', () => {
110
111
  opacity: 0.6,
111
112
  })
112
113
  })
114
+
115
+ it.each([
116
+ ['completed', 'status-success'],
117
+ ['today', 'brand-primary'],
118
+ ['upcoming', 'hairline-default'],
119
+ ] as const)('draws the %s accent stripe from the %s token', (status, token) => {
120
+ render(<WorkoutCard {...baseProps} status={status} />)
121
+ const style = screen.getByTestId('workout-card').getAttribute('style') ?? ''
122
+ expect(style).toContain(`border-left-color: ${resolveColor(token)}`)
123
+ })
124
+
125
+ it("washes today's card with the brand subtle token", () => {
126
+ render(<WorkoutCard {...baseProps} status="today" />)
127
+ expect(screen.getByTestId('workout-card')).toHaveStyle({
128
+ backgroundColor: resolveColor('brand-primary-subtle'),
129
+ })
130
+ })
113
131
  })
114
132
 
115
133
  describe('accessibility', () => {
@@ -1,25 +1,15 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, ScrollView, Pressable, type ViewProps } from 'react-native'
2
+ import { View, ScrollView, Pressable, type ViewProps } from 'react-native'
3
3
  import { Card } from '../../ui/card'
4
4
  import { ExerciseCard, type ExerciseCardProps } from './ExerciseCard'
5
- import {
6
- MuscleGroupChip,
7
- type VolumeStatus,
8
- } from './MuscleGroupChip'
9
- import { getSemanticColors } from '../../../theme/tokens/semantic'
10
-
11
- const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
12
-
13
- const t = getSemanticColors('dark')
5
+ import { MuscleGroupChip, type VolumeStatus } from './MuscleGroupChip'
6
+ import { Typography } from '../Typography'
7
+ import { resolveColor } from '../../../theme/resolve-color'
14
8
 
15
9
  export type WorkoutStatus = 'completed' | 'today' | 'upcoming'
16
10
 
17
11
  /** Spec volume-status vocabulary; mapped to MuscleGroupChip's VolumeStatus internally. */
18
- export type WorkoutMuscleVolumeStatus =
19
- | 'under'
20
- | 'maintenance'
21
- | 'productive'
22
- | 'over'
12
+ export type WorkoutMuscleVolumeStatus = 'under' | 'maintenance' | 'productive' | 'over'
23
13
 
24
14
  export interface WorkoutMuscleGroup {
25
15
  /** Muscle group identifier (free-form to match plan data). */
@@ -47,17 +37,14 @@ export interface WorkoutCardProps extends ViewProps {
47
37
  className?: string
48
38
  }
49
39
 
50
- const COLORS = {
51
- statusSuccess: t['status-success'],
52
- brandPrimary: t['brand-primary'],
53
- borderDefault: HAIRLINE_DEFAULT,
54
- brandPrimarySubtle: 'rgba(255,121,0,0.06)',
55
- }
56
-
57
- const statusBorderColors: Record<WorkoutStatus, string> = {
58
- completed: COLORS.statusSuccess,
59
- today: COLORS.brandPrimary,
60
- upcoming: COLORS.borderDefault,
40
+ /** Accent-stripe token per status, resolved per theme at render. */
41
+ const statusAccentToken: Record<
42
+ WorkoutStatus,
43
+ 'status-success' | 'brand-primary' | 'hairline-default'
44
+ > = {
45
+ completed: 'status-success',
46
+ today: 'brand-primary',
47
+ upcoming: 'hairline-default',
61
48
  }
62
49
 
63
50
  /** Maps the spec's volume-status vocabulary onto MuscleGroupChip's enum. */
@@ -72,7 +59,7 @@ function formatStats(
72
59
  totalSets: number,
73
60
  totalVolume: number | undefined,
74
61
  unit: 'lbs' | 'kg',
75
- duration: string | undefined,
62
+ duration: string | undefined
76
63
  ): string {
77
64
  const parts = [`${totalSets} sets`]
78
65
  if (totalVolume != null) parts.push(`${totalVolume} ${unit}`)
@@ -123,41 +110,36 @@ export function WorkoutCard({
123
110
  const summary = (
124
111
  <View style={{ padding: 14 }} testID="workout-card-body">
125
112
  <View className="flex-row items-center" testID="workout-card-header">
126
- <Text
127
- className="text-text-primary"
128
- style={{
129
- fontSize: 15,
130
- fontFamily: '"Space Grotesk", sans-serif',
131
- fontWeight: '700',
132
- }}
113
+ {/* The card name was 15px/700 Space Grotesk — off the type scale between
114
+ `sm` and `base`. `h6` carries the heading face; `base` is the step up. */}
115
+ <Typography
116
+ variant="h6"
117
+ color="primary"
118
+ className="text-base font-bold leading-[normal]"
133
119
  testID="workout-card-name"
134
120
  >
135
121
  {name}
136
- </Text>
122
+ </Typography>
137
123
  <View className="flex-1" />
138
- <Text
139
- className="text-text-tertiary"
140
- style={{
141
- fontSize: 11,
142
- fontFamily: 'Inter, sans-serif',
143
- }}
124
+ {/* 11px is off the scale too; `2xs` (10px) is the step below. */}
125
+ <Typography
126
+ variant="caption"
127
+ color="tertiary"
128
+ className="text-2xs leading-[normal]"
144
129
  testID="workout-card-date"
145
130
  >
146
131
  {date}
147
- </Text>
132
+ </Typography>
148
133
  </View>
149
134
 
150
- <Text
151
- className="text-text-secondary"
152
- style={{
153
- marginTop: 4,
154
- fontSize: 12,
155
- fontFamily: 'Inter, sans-serif',
156
- }}
135
+ <Typography
136
+ variant="caption"
137
+ color="secondary"
138
+ className="mt-1 leading-[normal]"
157
139
  testID="workout-card-stats"
158
140
  >
159
141
  {formatStats(totalSets, totalVolume, unit, duration)}
160
- </Text>
142
+ </Typography>
161
143
 
162
144
  {muscleGroups.length > 0 && (
163
145
  <ScrollView
@@ -171,11 +153,7 @@ export function WorkoutCard({
171
153
  <MuscleGroupChip
172
154
  key={muscle.group}
173
155
  name={muscle.label}
174
- volumeStatus={
175
- muscle.volumeStatus
176
- ? volumeStatusMap[muscle.volumeStatus]
177
- : undefined
178
- }
156
+ volumeStatus={muscle.volumeStatus ? volumeStatusMap[muscle.volumeStatus] : undefined}
179
157
  />
180
158
  ))}
181
159
  </ScrollView>
@@ -187,8 +165,8 @@ export function WorkoutCard({
187
165
  <Card
188
166
  variant="accent"
189
167
  elevation={1}
190
- accentColor={statusBorderColors[status]}
191
- bgColor={isToday ? COLORS.brandPrimarySubtle : undefined}
168
+ accentColor={resolveColor(statusAccentToken[status])}
169
+ bgColor={isToday ? resolveColor('brand-primary-subtle') : undefined}
192
170
  className={className}
193
171
  style={isUpcoming ? { opacity: 0.6 } : undefined}
194
172
  testID="workout-card"