@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
@@ -9,23 +9,33 @@ describe('MuscleGroupChip', () => {
9
9
  expect(screen.getByText('Chest')).toBeInTheDocument()
10
10
  })
11
11
 
12
- it('renders the colored dot', () => {
12
+ it('renders the coloured dot under the chip’s own testID', () => {
13
13
  render(<MuscleGroupChip name="Quads" volumeStatus="ontrack" />)
14
14
  expect(screen.getByTestId('muscle-group-chip-dot')).toBeInTheDocument()
15
15
  })
16
16
 
17
+ it('keeps one capsule shape across every status, changing only the dot', () => {
18
+ // The dot carries status; the capsule stays neutral, so a row of chips reads
19
+ // as one family. jsdom cannot compute a nativewind class background, so the
20
+ // fill itself is asserted by the HTML-vs-React parity layer; what is
21
+ // checkable here is that the status never reaches the capsule element.
22
+ const { rerender } = render(<MuscleGroupChip name="Quads" volumeStatus="untrained" />)
23
+ const shape = screen.getByTestId('muscle-group-chip').getAttribute('class')
24
+ for (const status of ['behind', 'ontrack', 'target', 'over'] as const) {
25
+ rerender(<MuscleGroupChip name="Quads" volumeStatus={status} />)
26
+ expect(screen.getByTestId('muscle-group-chip').getAttribute('class'), status).toBe(shape)
27
+ expect(screen.getByTestId('muscle-group-chip-dot')).toBeInTheDocument()
28
+ }
29
+ })
30
+
17
31
  it('sets accessibility label with name and status', () => {
18
32
  render(<MuscleGroupChip name="Back" volumeStatus="behind" />)
19
- expect(
20
- screen.getByLabelText('Back, volume status: behind'),
21
- ).toBeInTheDocument()
33
+ expect(screen.getByLabelText('Back, volume status: behind')).toBeInTheDocument()
22
34
  })
23
35
 
24
36
  it('sets accessibility label with no status when omitted', () => {
25
37
  render(<MuscleGroupChip name="Biceps" />)
26
- expect(
27
- screen.getByLabelText('Biceps, volume status: no status'),
28
- ).toBeInTheDocument()
38
+ expect(screen.getByLabelText('Biceps, volume status: no status')).toBeInTheDocument()
29
39
  })
30
40
 
31
41
  it('wraps in Pressable when onPress is provided', () => {
@@ -38,22 +48,12 @@ describe('MuscleGroupChip', () => {
38
48
 
39
49
  it('does not render Pressable when onPress is omitted', () => {
40
50
  render(<MuscleGroupChip name="Chest" />)
41
- expect(
42
- screen.queryByTestId('muscle-group-chip-pressable'),
43
- ).not.toBeInTheDocument()
51
+ expect(screen.queryByTestId('muscle-group-chip-pressable')).not.toBeInTheDocument()
44
52
  })
45
53
 
46
54
  it('renders all volume status variants without error', () => {
47
- const statuses = [
48
- 'untrained',
49
- 'behind',
50
- 'ontrack',
51
- 'target',
52
- 'over',
53
- ] as const
54
- const { rerender } = render(
55
- <MuscleGroupChip name="Test" volumeStatus="untrained" />,
56
- )
55
+ const statuses = ['untrained', 'behind', 'ontrack', 'target', 'over'] as const
56
+ const { rerender } = render(<MuscleGroupChip name="Test" volumeStatus="untrained" />)
57
57
  for (const status of statuses) {
58
58
  rerender(<MuscleGroupChip name="Test" volumeStatus={status} />)
59
59
  expect(screen.getByText('Test')).toBeInTheDocument()
@@ -63,28 +63,20 @@ describe('MuscleGroupChip', () => {
63
63
  describe('accessibility', () => {
64
64
  it('has no accessibility violations', async () => {
65
65
  const { container } = render(
66
- <MuscleGroupChip
67
- name="Chest"
68
- volumeStatus="ontrack"
69
- onPress={() => {}}
70
- />,
66
+ <MuscleGroupChip name="Chest" volumeStatus="ontrack" onPress={() => {}} />
71
67
  )
72
68
  const results = await axe(container)
73
69
  expect(results).toHaveNoViolations()
74
70
  })
75
71
 
76
72
  it('has no accessibility violations as static chip', async () => {
77
- const { container } = render(
78
- <MuscleGroupChip name="Quads" volumeStatus="behind" />,
79
- )
73
+ const { container } = render(<MuscleGroupChip name="Quads" volumeStatus="behind" />)
80
74
  const results = await axe(container)
81
75
  expect(results).toHaveNoViolations()
82
76
  })
83
77
 
84
78
  it('pressable has correct accessibility label', () => {
85
- render(
86
- <MuscleGroupChip name="Back" volumeStatus="target" onPress={() => {}} />,
87
- )
79
+ render(<MuscleGroupChip name="Back" volumeStatus="target" onPress={() => {}} />)
88
80
  expect(screen.getByLabelText('Back, volume status: target')).toBeInTheDocument()
89
81
  })
90
82
  })
@@ -1,9 +1,6 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
- import { View, Text, Pressable, type ViewProps } from 'react-native'
3
- import { getSemanticColors } from '../../../theme/tokens/semantic'
4
- import { greyRamp } from '../../../theme/tokens/primitives'
5
-
6
- const t = getSemanticColors('dark')
2
+ import { type ViewProps } from 'react-native'
3
+ import { Pill, type PillTone } from '../../ui/pill'
7
4
 
8
5
  // Aliases for spec compatibility: under=behind, maintenance=ontrack, productive=target
9
6
  export type VolumeStatus = 'untrained' | 'behind' | 'ontrack' | 'target' | 'over'
@@ -15,14 +12,18 @@ export interface MuscleGroupChipProps extends ViewProps {
15
12
  className?: string
16
13
  }
17
14
 
18
- const dotColorMap: Record<VolumeStatus, string> = {
19
- untrained: greyRamp[900],
20
- behind: t['brand-secondary'],
21
- ontrack: t['status-success'],
22
- target: t['brand-primary'],
23
- over: t['status-error'],
15
+ const dotTone: Record<VolumeStatus, PillTone> = {
16
+ untrained: 'neutral',
17
+ behind: 'brand-secondary',
18
+ ontrack: 'success',
19
+ target: 'brand',
20
+ over: 'error',
24
21
  }
25
22
 
23
+ /**
24
+ * MuscleGroupChip — a `Pill` preset that labels a muscle group with a
25
+ * volume-status dot. The dot tone is the pill's tone; the capsule stays neutral.
26
+ */
26
27
  export function MuscleGroupChip({
27
28
  name,
28
29
  volumeStatus,
@@ -30,65 +31,25 @@ export function MuscleGroupChip({
30
31
  className,
31
32
  ...props
32
33
  }: MuscleGroupChipProps) {
33
- const dotColor = volumeStatus
34
- ? dotColorMap[volumeStatus]
35
- : greyRamp[900]
36
-
37
34
  const statusLabel = volumeStatus ?? 'no status'
38
35
 
39
- const content = (
40
- <View
41
- className={['bg-surface-raised border-hairline', className].filter(Boolean).join(' ')}
42
- style={{
43
- flexDirection: 'row',
44
- alignItems: 'center',
45
- borderRadius: 9999,
46
- paddingHorizontal: 9,
47
- paddingVertical: 3,
48
- borderWidth: 1,
49
- }}
50
- accessibilityLabel={onPress ? undefined : `${name}, volume status: ${statusLabel}`}
51
- testID="muscle-group-chip"
36
+ return (
37
+ <Pill
38
+ tone="neutral"
39
+ dotTone={volumeStatus ? dotTone[volumeStatus] : 'neutral'}
40
+ variant="subtle"
41
+ size="md"
42
+ leading="dot"
43
+ onPress={onPress}
44
+ // Borderless, like every other tone: the tinted fill carries the capsule.
45
+ // `md` keeps the chip at its shipped ~24px height with 12px text.
46
+ className={['gap-1.5', className].filter(Boolean).join(' ')}
47
+ textClassName="font-sans font-medium text-text-secondary"
48
+ accessibilityLabel={`${name}, volume status: ${statusLabel}`}
49
+ testID={onPress ? 'muscle-group-chip-pressable' : 'muscle-group-chip'}
52
50
  {...props}
53
51
  >
54
- <View
55
- style={{
56
- width: 6,
57
- height: 6,
58
- borderRadius: 9999,
59
- marginRight: 5,
60
- flexShrink: 0,
61
- backgroundColor: dotColor,
62
- }}
63
- accessibilityElementsHidden
64
- testID="muscle-group-chip-dot"
65
- />
66
- <Text
67
- className="text-text-secondary"
68
- style={{
69
- fontFamily: 'Inter, sans-serif',
70
- fontWeight: '500',
71
- fontSize: 11,
72
- }}
73
- accessibilityElementsHidden
74
- >
75
- {name}
76
- </Text>
77
- </View>
52
+ {name}
53
+ </Pill>
78
54
  )
79
-
80
- if (onPress) {
81
- return (
82
- <Pressable
83
- onPress={onPress}
84
- accessibilityRole="button"
85
- accessibilityLabel={`${name}, volume status: ${statusLabel}`}
86
- testID="muscle-group-chip-pressable"
87
- >
88
- {content}
89
- </Pressable>
90
- )
91
- }
92
-
93
- return content
94
55
  }
@@ -1,9 +1,12 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { View } from 'react-native'
3
3
  import { PlaceholderStrip } from './PlaceholderStrip'
4
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
5
+
6
+ const t = getSemanticColors('dark')
4
7
 
5
8
  const meta: Meta<typeof PlaceholderStrip> = {
6
- title: 'Workout/PlaceholderStrip',
9
+ title: 'Custom/Workout/PlaceholderStrip',
7
10
  component: PlaceholderStrip,
8
11
  tags: ['autodocs'],
9
12
  argTypes: {
@@ -74,9 +77,11 @@ export const SegmentedThree: Story = {
74
77
  export const MultipleStrips: Story = {
75
78
  render: () => (
76
79
  <View style={{ flexDirection: 'row', gap: 4, width: 300 }}>
77
- <View style={{ flex: 1, height: 3, backgroundColor: '#2ed573', borderRadius: 1 }} />
78
- <View style={{ flex: 1, height: 3, backgroundColor: '#ffd43b', borderRadius: 1 }} />
79
- <View style={{ flex: 1, height: 3, backgroundColor: '#ffa502', borderRadius: 1 }} />
80
+ <View style={{ flex: 1, height: 3, backgroundColor: t['status-success'], borderRadius: 1 }} />
81
+ <View
82
+ style={{ flex: 1, height: 3, backgroundColor: t['status-warning-light'], borderRadius: 1 }}
83
+ />
84
+ <View style={{ flex: 1, height: 3, backgroundColor: t['status-warning'], borderRadius: 1 }} />
80
85
  <PlaceholderStrip />
81
86
  <PlaceholderStrip />
82
87
  </View>
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'
2
2
  import { render, screen } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
4
  import { PlaceholderStrip } from './PlaceholderStrip'
5
+ import { resolveColor } from '../../../theme/resolve-color'
5
6
 
6
7
  describe('PlaceholderStrip', () => {
7
8
  it('renders a strip element', () => {
@@ -11,9 +12,7 @@ describe('PlaceholderStrip', () => {
11
12
 
12
13
  it('sets accessibility label', () => {
13
14
  render(<PlaceholderStrip />)
14
- expect(
15
- screen.getByLabelText('Planned set, not yet completed'),
16
- ).toBeInTheDocument()
15
+ expect(screen.getByLabelText('Planned set, not yet completed')).toBeInTheDocument()
17
16
  })
18
17
 
19
18
  it('applies fixed width when provided', () => {
@@ -28,7 +27,7 @@ describe('PlaceholderStrip', () => {
28
27
  <PlaceholderStrip />
29
28
  <PlaceholderStrip />
30
29
  <PlaceholderStrip />
31
- </>,
30
+ </>
32
31
  )
33
32
  const strips = screen.getAllByTestId('placeholder-strip')
34
33
  expect(strips).toHaveLength(3)
@@ -39,6 +38,18 @@ describe('PlaceholderStrip', () => {
39
38
  expect(screen.queryAllByTestId('placeholder-segment')).toHaveLength(0)
40
39
  })
41
40
 
41
+ it('fills both modes from the border-prominent token, not a pinned hex', () => {
42
+ const rail = resolveColor('border-prominent')
43
+ const { unmount } = render(<PlaceholderStrip />)
44
+ expect(screen.getByTestId('placeholder-strip')).toHaveStyle({ backgroundColor: rail })
45
+ unmount()
46
+
47
+ render(<PlaceholderStrip mode="segmented" segments={2} />)
48
+ for (const segment of screen.getAllByTestId('placeholder-segment')) {
49
+ expect(segment).toHaveStyle({ backgroundColor: rail })
50
+ }
51
+ })
52
+
42
53
  describe('segmented mode', () => {
43
54
  it('renders segments when mode is segmented', () => {
44
55
  render(<PlaceholderStrip mode="segmented" segments={5} />)
@@ -59,9 +70,7 @@ describe('PlaceholderStrip', () => {
59
70
 
60
71
  it('sets accessibility label in segmented mode', () => {
61
72
  render(<PlaceholderStrip mode="segmented" segments={3} />)
62
- expect(
63
- screen.getByLabelText('Planned set, not yet completed'),
64
- ).toBeInTheDocument()
73
+ expect(screen.getByLabelText('Planned set, not yet completed')).toBeInTheDocument()
65
74
  })
66
75
  })
67
76
 
@@ -73,9 +82,7 @@ describe('PlaceholderStrip', () => {
73
82
  })
74
83
 
75
84
  it('has no accessibility violations in segmented mode', async () => {
76
- const { container } = render(
77
- <PlaceholderStrip mode="segmented" segments={4} />,
78
- )
85
+ const { container } = render(<PlaceholderStrip mode="segmented" segments={4} />)
79
86
  const results = await axe(container)
80
87
  expect(results).toHaveNoViolations()
81
88
  })
@@ -1,7 +1,12 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { View, type ViewProps } from 'react-native'
3
3
  import { cn } from '../../../utils/cn'
4
- import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
4
+ import { resolveColor } from '../../../theme/resolve-color'
5
+
6
+ // The planned-set rail is a visible hairline rule, not a plane, so it takes the
7
+ // one border token meant to be seen outright. It used to be a `#3A3A3A` pin in
8
+ // `workout-tokens`, which sat between two ramp steps and never flipped to light.
9
+ const RAIL_TOKEN = 'border-prominent' as const
5
10
 
6
11
  export interface PlaceholderStripProps extends ViewProps {
7
12
  width?: number | string
@@ -23,7 +28,7 @@ function SingleStrip({
23
28
  style={[
24
29
  {
25
30
  height: 3,
26
- backgroundColor: WORKOUT_TOKENS.placeholder.fill,
31
+ backgroundColor: resolveColor(RAIL_TOKEN),
27
32
  borderRadius: 2,
28
33
  opacity: 0.5,
29
34
  },
@@ -61,7 +66,7 @@ function SegmentedStrip({
61
66
  style={{
62
67
  flex: 1,
63
68
  height: 3,
64
- backgroundColor: WORKOUT_TOKENS.placeholder.fill,
69
+ backgroundColor: resolveColor(RAIL_TOKEN),
65
70
  borderRadius: 1,
66
71
  minWidth: 4,
67
72
  }}
@@ -73,11 +78,7 @@ function SegmentedStrip({
73
78
  )
74
79
  }
75
80
 
76
- export function PlaceholderStrip({
77
- mode = 'single',
78
- segments,
79
- ...props
80
- }: PlaceholderStripProps) {
81
+ export function PlaceholderStrip({ mode = 'single', segments, ...props }: PlaceholderStripProps) {
81
82
  if (mode === 'segmented') {
82
83
  return <SegmentedStrip segments={segments} {...props} />
83
84
  }
@@ -3,8 +3,9 @@ import { View } from 'react-native'
3
3
  import { PrBadge } from './PrBadge'
4
4
 
5
5
  const meta: Meta<typeof PrBadge> = {
6
- title: 'Components/Atoms/PrBadge',
6
+ title: 'Custom/Workout/PrBadge',
7
7
  component: PrBadge,
8
+ parameters: { docs: { description: { component: 'Composes **Icons** · **BaseBadge**.' } } },
8
9
  tags: ['autodocs'],
9
10
  argTypes: {
10
11
  type: {
@@ -26,9 +26,7 @@ describe('PrBadge', () => {
26
26
 
27
27
  it('sets accessibility label with resolved label', () => {
28
28
  render(<PrBadge animate={false} />)
29
- expect(
30
- screen.getByLabelText('Personal record: PR e1RM'),
31
- ).toBeInTheDocument()
29
+ expect(screen.getByLabelText('Personal record: PR e1RM')).toBeInTheDocument()
32
30
  })
33
31
 
34
32
  describe('type prop', () => {
@@ -66,9 +64,7 @@ describe('PrBadge', () => {
66
64
 
67
65
  it('sets accessibility label with custom label', () => {
68
66
  render(<PrBadge label="PR 5RM" animate={false} />)
69
- expect(
70
- screen.getByLabelText('Personal record: PR 5RM'),
71
- ).toBeInTheDocument()
67
+ expect(screen.getByLabelText('Personal record: PR 5RM')).toBeInTheDocument()
72
68
  })
73
69
  })
74
70
 
@@ -89,9 +85,7 @@ describe('PrBadge', () => {
89
85
 
90
86
  it('has correct accessibility label in compact mode', () => {
91
87
  render(<PrBadge compact animate={false} />)
92
- expect(
93
- screen.getByLabelText('Personal record: PR e1RM'),
94
- ).toBeInTheDocument()
88
+ expect(screen.getByLabelText('Personal record: PR e1RM')).toBeInTheDocument()
95
89
  })
96
90
  })
97
91
 
@@ -1,11 +1,10 @@
1
1
  // Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
2
2
  import { useEffect, useState } from 'react'
3
- import { View, Text, Animated, Easing, type ViewProps } from 'react-native'
3
+ import { View, Animated, Easing, type ViewProps } from 'react-native'
4
4
  import { StarIcon } from './icons'
5
5
  import { BaseBadge } from './BaseBadge'
6
- import { getSemanticColors } from '../../../theme/tokens/semantic'
7
-
8
- const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
6
+ import { Typography } from '../Typography'
7
+ import { resolveColor } from '../../../theme/resolve-color'
9
8
 
10
9
  export type PRType = 'e1rm' | 'weight' | 'reps' | 'volume' | 'velocity'
11
10
 
@@ -38,6 +37,7 @@ export function PrBadge({
38
37
  ...props
39
38
  }: PrBadgeProps) {
40
39
  const resolvedLabel = labelProp ?? typeLabels[type]
40
+ const brandPrimary = resolveColor('brand-primary')
41
41
  const [scale] = useState(() => new Animated.Value(animate ? 0.8 : 1))
42
42
  const [opacity] = useState(() => new Animated.Value(animate ? 0 : 1))
43
43
 
@@ -69,7 +69,7 @@ export function PrBadge({
69
69
  testID="pr-badge-star"
70
70
  {...props}
71
71
  >
72
- <StarIcon size={14} color={BRAND_PRIMARY} fill={BRAND_PRIMARY} strokeWidth={2} />
72
+ <StarIcon size={14} color={brandPrimary} fill={brandPrimary} strokeWidth={2} />
73
73
  </View>
74
74
  ) : (
75
75
  <BaseBadge
@@ -78,25 +78,21 @@ export function PrBadge({
78
78
  accessibilityLabel={`Personal record: ${resolvedLabel}`}
79
79
  {...props}
80
80
  >
81
- <Text
82
- style={{
83
- fontSize: 12,
84
- color: BRAND_PRIMARY,
85
- fontWeight: '700',
86
- fontFamily: 'Inter, sans-serif',
87
- }}
81
+ {/* `boldLabel` is the badge label exactly: 12px/700 Inter. The line box is pinned
82
+ because the raw <Text> it replaced carried no lineHeight. */}
83
+ <Typography
84
+ variant="boldLabel"
85
+ color="inherit"
86
+ className="leading-[normal] text-brand-primary"
88
87
  >
89
88
  {'\u2605'} {resolvedLabel}
90
- </Text>
89
+ </Typography>
91
90
  </BaseBadge>
92
91
  )
93
92
 
94
93
  if (animate) {
95
94
  return (
96
- <Animated.View
97
- style={{ transform: [{ scale }], opacity }}
98
- testID="pr-badge-animated"
99
- >
95
+ <Animated.View style={{ transform: [{ scale }], opacity }} testID="pr-badge-animated">
100
96
  {badge}
101
97
  </Animated.View>
102
98
  )
@@ -2,6 +2,10 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { useState } from 'react'
3
3
  import { View, Text, Pressable } from 'react-native'
4
4
  import { PrHistoryModal, type PrRecord } from './PrHistoryModal'
5
+ import { primitiveColors } from '../../../theme/tokens/primitives'
6
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
7
+
8
+ const t = getSemanticColors('dark')
5
9
 
6
10
  const records: PrRecord[] = [
7
11
  { type: 'e1rm', value: 245, unit: 'lbs', date: 'Mar 14', isRecent: true },
@@ -12,8 +16,11 @@ const records: PrRecord[] = [
12
16
  ]
13
17
 
14
18
  const meta: Meta<typeof PrHistoryModal> = {
15
- title: 'Workout/PrHistoryModal',
19
+ title: 'Custom/Workout/PrHistoryModal',
16
20
  component: PrHistoryModal,
21
+ parameters: {
22
+ docs: { description: { component: 'Composes **Icons** · **Drawer** · **DrawerBody**.' } },
23
+ },
17
24
  tags: ['autodocs'],
18
25
  argTypes: {
19
26
  exerciseId: {
@@ -90,13 +97,19 @@ function InteractivePrHistoryModal() {
90
97
  accessibilityRole="button"
91
98
  style={{
92
99
  alignSelf: 'flex-start',
93
- backgroundColor: '#FF7900',
100
+ backgroundColor: t['brand-primary'],
94
101
  paddingVertical: 10,
95
102
  paddingHorizontal: 20,
96
103
  borderRadius: 8,
97
104
  }}
98
105
  >
99
- <Text style={{ color: '#FFFFFF', fontWeight: '700', fontFamily: 'Inter, sans-serif' }}>
106
+ <Text
107
+ style={{
108
+ color: primitiveColors.white,
109
+ fontWeight: '700',
110
+ fontFamily: 'Inter, sans-serif',
111
+ }}
112
+ >
100
113
  Open PR history
101
114
  </Text>
102
115
  </Pressable>
@@ -23,7 +23,7 @@ describe('PrHistoryModal', () => {
23
23
  render(<PrHistoryModal {...baseProps} />)
24
24
  expect(screen.getByTestId('pr-history-modal')).toBeInTheDocument()
25
25
  expect(screen.getByTestId('pr-history-modal-title')).toHaveTextContent(
26
- 'Bench Press PR history',
26
+ 'Bench Press PR history'
27
27
  )
28
28
  })
29
29
 
@@ -40,23 +40,14 @@ describe('PrHistoryModal', () => {
40
40
  visible
41
41
  records={records}
42
42
  onClose={vi.fn()}
43
- />,
43
+ />
44
44
  )
45
45
  expect(screen.getByTestId('pr-history-modal')).toBeInTheDocument()
46
46
  })
47
47
 
48
48
  it('falls back to exerciseId when no name is given', () => {
49
- render(
50
- <PrHistoryModal
51
- exerciseId="deadlift"
52
- isOpen
53
- records={records}
54
- onClose={vi.fn()}
55
- />,
56
- )
57
- expect(screen.getByTestId('pr-history-modal-title')).toHaveTextContent(
58
- 'deadlift PR history',
59
- )
49
+ render(<PrHistoryModal exerciseId="deadlift" isOpen records={records} onClose={vi.fn()} />)
50
+ expect(screen.getByTestId('pr-history-modal-title')).toHaveTextContent('deadlift PR history')
60
51
  })
61
52
 
62
53
  it('renders a drag handle', () => {
@@ -68,22 +59,14 @@ describe('PrHistoryModal', () => {
68
59
  describe('records', () => {
69
60
  it('renders a row per record with type, value, and date', () => {
70
61
  render(<PrHistoryModal {...baseProps} />)
71
- expect(screen.getByTestId('pr-history-modal-record-0-type')).toHaveTextContent(
72
- 'e1RM',
73
- )
74
- expect(screen.getByTestId('pr-history-modal-record-0-value')).toHaveTextContent(
75
- '245 lbs',
76
- )
77
- expect(screen.getByTestId('pr-history-modal-record-0-date')).toHaveTextContent(
78
- 'Mar 14',
79
- )
62
+ expect(screen.getByTestId('pr-history-modal-record-0-type')).toHaveTextContent('e1RM')
63
+ expect(screen.getByTestId('pr-history-modal-record-0-value')).toHaveTextContent('245 lbs')
64
+ expect(screen.getByTestId('pr-history-modal-record-0-date')).toHaveTextContent('Mar 14')
80
65
  })
81
66
 
82
67
  it('renders pre-formatted string values verbatim', () => {
83
68
  render(<PrHistoryModal {...baseProps} />)
84
- expect(screen.getByTestId('pr-history-modal-record-1-value')).toHaveTextContent(
85
- '8 @ 185 lbs',
86
- )
69
+ expect(screen.getByTestId('pr-history-modal-record-1-value')).toHaveTextContent('8 @ 185 lbs')
87
70
  })
88
71
 
89
72
  it('renders one row per supplied record', () => {
@@ -91,27 +74,21 @@ describe('PrHistoryModal', () => {
91
74
  expect(screen.getByTestId('pr-history-modal-record-0')).toBeInTheDocument()
92
75
  expect(screen.getByTestId('pr-history-modal-record-1')).toBeInTheDocument()
93
76
  expect(screen.getByTestId('pr-history-modal-record-2')).toBeInTheDocument()
94
- expect(
95
- screen.queryByTestId('pr-history-modal-record-3'),
96
- ).not.toBeInTheDocument()
77
+ expect(screen.queryByTestId('pr-history-modal-record-3')).not.toBeInTheDocument()
97
78
  })
98
79
 
99
80
  it('marks recent records in their accessible label', () => {
100
81
  render(<PrHistoryModal {...baseProps} />)
101
82
  expect(
102
- screen.getByLabelText('e1RM personal record: 245 lbs, Mar 14, recent'),
103
- ).toBeInTheDocument()
104
- expect(
105
- screen.getByLabelText('Reps personal record: 8 @ 185 lbs, Feb 28'),
83
+ screen.getByLabelText('e1RM personal record: 245 lbs, Mar 14, recent')
106
84
  ).toBeInTheDocument()
85
+ expect(screen.getByLabelText('Reps personal record: 8 @ 185 lbs, Feb 28')).toBeInTheDocument()
107
86
  })
108
87
 
109
88
  it('shows an empty state when there are no records', () => {
110
89
  render(<PrHistoryModal {...baseProps} records={[]} />)
111
90
  expect(screen.getByTestId('pr-history-modal-empty')).toBeInTheDocument()
112
- expect(
113
- screen.queryByTestId('pr-history-modal-record-0'),
114
- ).not.toBeInTheDocument()
91
+ expect(screen.queryByTestId('pr-history-modal-record-0')).not.toBeInTheDocument()
115
92
  })
116
93
  })
117
94
 
@@ -153,7 +130,7 @@ describe('PrHistoryModal', () => {
153
130
  { type: 'weight', value: 365, unit: 'lbs', date: 'Last week' },
154
131
  { type: 'volume', value: '18,200 lbs', date: 'Mar 1' },
155
132
  ]}
156
- />,
133
+ />
157
134
  )
158
135
  expect(await axe(container)).toHaveNoViolations()
159
136
  })
@@ -4,10 +4,14 @@ import { StarIcon } from './icons'
4
4
  import { Drawer, DrawerBody } from '../../ui/drawer'
5
5
  import { resolveColor } from '../../../theme/resolve-color'
6
6
  import { getSemanticColors } from '../../../theme/tokens/semantic'
7
+ import { getGlowShadow } from '../../../theme/elevation'
8
+ import { alpha } from '../../../utils/colors'
7
9
 
8
- const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
9
- const RECENT_BORDER = 'rgba(249,180,21,0.3)'
10
- const RECENT_GLOW = '0 0 12px rgba(249,180,21,0.06)'
10
+ const SEMANTIC = getSemanticColors('dark')
11
+ const BRAND_PRIMARY = SEMANTIC['brand-primary']
12
+ const RECENT_BORDER = alpha(SEMANTIC['status-warning'], 0.3)
13
+ /** A recent PR is EMPHASIS, not depth: a warning-toned glow, shared builder. */
14
+ const RECENT_GLOW = getGlowShadow(SEMANTIC['status-warning'], 'subtle')
11
15
  // Native-safe fallback for the conditional row border (recent uses a computed
12
16
  // rgba, so className can't express both branches).
13
17
  const BORDER_DEFAULT = resolveColor('hairline-default')
@@ -81,7 +85,7 @@ function PrRecordRow({ record, index }: { record: PrRecord; index: number }) {
81
85
  borderRadius: 8,
82
86
  borderWidth: 1,
83
87
  borderColor: record.isRecent ? RECENT_BORDER : BORDER_DEFAULT,
84
- ...(record.isRecent ? { boxShadow: RECENT_GLOW } : {}),
88
+ ...(record.isRecent ? RECENT_GLOW : {}),
85
89
  }}
86
90
  testID={`pr-history-modal-record-${index}`}
87
91
  >
@@ -230,7 +230,12 @@ const mesos: PlanMeso[] = [accumulationDone, accumulation, intensification, peak
230
230
  const meta: Meta<typeof ProgramPlanningPage> = {
231
231
  title: 'Pages/Program Planning',
232
232
  component: ProgramPlanningPage,
233
- parameters: { layout: 'fullscreen' },
233
+ parameters: {
234
+ layout: 'fullscreen',
235
+ docs: {
236
+ description: { component: 'Composes **MesoProgressBar** · **MesoCard** · **WorkoutCard**.' },
237
+ },
238
+ },
234
239
  tags: ['autodocs'],
235
240
  args: { title: 'Program Plan', mesos },
236
241
  }