@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
@@ -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"
@@ -3,7 +3,7 @@ import { View } from 'react-native'
3
3
  import { WorkoutPill } from './WorkoutPill'
4
4
 
5
5
  const meta: Meta<typeof WorkoutPill> = {
6
- title: 'Workout/WorkoutPill',
6
+ title: 'Custom/Workout/WorkoutPill',
7
7
  component: WorkoutPill,
8
8
  tags: ['autodocs'],
9
9
  argTypes: {
@@ -14,7 +14,10 @@ const meta: Meta<typeof WorkoutPill> = {
14
14
  description: 'Pill status',
15
15
  },
16
16
  pulse: { control: 'boolean', description: 'Pulsing animation (independent of status)' },
17
- highlighted: { control: 'boolean', description: 'Highlighted with thicker border and slight scale' },
17
+ highlighted: {
18
+ control: 'boolean',
19
+ description: 'Highlighted with thicker border and slight scale',
20
+ },
18
21
  },
19
22
  }
20
23
 
@@ -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 { WorkoutPill } from './WorkoutPill'
5
+ import { resolveColor } from '../../../theme/resolve-color'
5
6
 
6
7
  describe('WorkoutPill', () => {
7
8
  it('renders the workout name', () => {
@@ -80,6 +81,41 @@ describe('WorkoutPill', () => {
80
81
  }
81
82
  })
82
83
 
84
+ // The rim is a whole rung above the wash. Asserting both rules out the
85
+ // flattening that collapses a two-step pill onto one `-subtle`.
86
+ describe('wash ladder', () => {
87
+ it.each([
88
+ ['completed', 'status-success'],
89
+ ['current', 'brand-primary'],
90
+ ['missed', 'status-error'],
91
+ ] as const)('washes the %s pill with %s -subtle inside a -muted rim', (status, role) => {
92
+ render(<WorkoutPill name="Upper A" status={status} />)
93
+ const pill = screen.getByLabelText(`Upper A workout, ${status}`)
94
+ expect(pill).toHaveStyle({ backgroundColor: resolveColor(`${role}-subtle`) })
95
+ expect(pill.style.borderTopColor).toBe(resolveColor(`${role}-muted`))
96
+ })
97
+
98
+ it('gives the washless next pill the -strong rim, a rung above current', () => {
99
+ render(<WorkoutPill name="Upper A" status="next" />)
100
+ const pill = screen.getByLabelText('Upper A workout, next')
101
+ // jsdom normalises the `transparent` keyword to its rgba() form.
102
+ expect(pill.style.backgroundColor).toBe('rgba(0, 0, 0, 0)')
103
+ expect(pill.style.borderTopColor).toBe(resolveColor('brand-primary-strong'))
104
+ })
105
+
106
+ it.each([
107
+ ['completed', 'status-success'],
108
+ ['current', 'brand-primary'],
109
+ ['upcoming', 'text-tertiary'],
110
+ ['missed', 'status-error-dark'],
111
+ ] as const)('labels the %s pill from the %s token', (status, token) => {
112
+ render(<WorkoutPill name="Upper A" status={status} />)
113
+ expect(screen.getByTestId('workout-pill-name')).toHaveStyle({
114
+ color: resolveColor(token),
115
+ })
116
+ })
117
+ })
118
+
83
119
  describe('pulse', () => {
84
120
  it('pulses by default on current status', () => {
85
121
  render(<WorkoutPill name="Upper A" status="current" />)
@@ -128,16 +164,14 @@ describe('WorkoutPill', () => {
128
164
  })
129
165
 
130
166
  it('has no accessibility violations as static pill', async () => {
131
- const { container } = render(
132
- <WorkoutPill name="Lower B" status="upcoming" />,
133
- )
167
+ const { container } = render(<WorkoutPill name="Lower B" status="upcoming" />)
134
168
  const results = await axe(container)
135
169
  expect(results).toHaveNoViolations()
136
170
  })
137
171
 
138
172
  it('has no accessibility violations for missed status', async () => {
139
173
  const { container } = render(
140
- <WorkoutPill name="Rest Day" status="missed" onPress={() => {}} />,
174
+ <WorkoutPill name="Rest Day" status="missed" onPress={() => {}} />
141
175
  )
142
176
  const results = await axe(container)
143
177
  expect(results).toHaveNoViolations()
@@ -145,9 +179,7 @@ describe('WorkoutPill', () => {
145
179
 
146
180
  it('pressable has correct accessibility label', () => {
147
181
  render(<WorkoutPill name="Upper A" status="completed" onPress={() => {}} />)
148
- expect(
149
- screen.getByLabelText('Upper A workout, completed'),
150
- ).toBeInTheDocument()
182
+ expect(screen.getByLabelText('Upper A workout, completed')).toBeInTheDocument()
151
183
  })
152
184
  })
153
185
  })