@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
@@ -5,7 +5,16 @@ import { EmptyState } from './EmptyState'
5
5
  const meta: Meta<typeof EmptyState> = {
6
6
  title: 'Components/Molecules/EmptyState',
7
7
  component: EmptyState,
8
- tags: ['autodocs'],
8
+ tags: ['autodocs', 'status:candidate', '!status:review'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'Composes RN primitives only. The `icon` slot takes an icon component and the ' +
14
+ '`action` slot usually takes a **Button**.',
15
+ },
16
+ },
17
+ },
9
18
  argTypes: {
10
19
  title: {
11
20
  control: 'text',
@@ -30,7 +39,7 @@ export const Default: Story = {
30
39
  export const WithDescription: Story = {
31
40
  args: {
32
41
  title: 'No messages',
33
- description: 'You don\'t have any messages yet. Start a conversation to see them here.',
42
+ description: "You don't have any messages yet. Start a conversation to see them here.",
34
43
  },
35
44
  }
36
45
 
@@ -38,6 +47,7 @@ export const WithDescription: Story = {
38
47
  function PlaceholderIcon({ size = 32, className }: { size?: number; className?: string }) {
39
48
  return (
40
49
  <View
50
+ className={className}
41
51
  style={{
42
52
  width: size,
43
53
  height: size,
@@ -56,7 +66,14 @@ function PlaceholderIcon({ size = 32, className }: { size?: number; className?:
56
66
  alignItems: 'center',
57
67
  }}
58
68
  >
59
- <View style={{ width: size * 0.3, height: 2, backgroundColor: 'var(--color-text-tertiary)', borderRadius: 1 }} />
69
+ <View
70
+ style={{
71
+ width: size * 0.3,
72
+ height: 2,
73
+ backgroundColor: 'var(--color-text-tertiary)',
74
+ borderRadius: 1,
75
+ }}
76
+ />
60
77
  </View>
61
78
  </View>
62
79
  )
@@ -73,12 +90,8 @@ export const WithIcon: Story = {
73
90
  // Simple action button for story demos
74
91
  function ActionButton() {
75
92
  return (
76
- <View
77
- className="bg-brand-primary px-4 py-2 rounded-md"
78
- >
79
- <Text className="text-white font-semibold text-sm">
80
- Create New
81
- </Text>
93
+ <View className="bg-brand-primary px-4 py-2 rounded-md">
94
+ <Text className="text-on-brand-primary font-semibold text-sm">Create New</Text>
82
95
  </View>
83
96
  )
84
97
  }
@@ -4,7 +4,11 @@ import { axe } from 'jest-axe'
4
4
  import { EmptyState } from './EmptyState'
5
5
 
6
6
  function MockIcon({ size, className }: { size?: number; className?: string }) {
7
- return <span data-testid="mock-icon" data-size={size} className={className}>Icon</span>
7
+ return (
8
+ <span data-testid="mock-icon" data-size={size} className={className}>
9
+ Icon
10
+ </span>
11
+ )
8
12
  }
9
13
 
10
14
  describe('EmptyState', () => {
@@ -14,12 +18,7 @@ describe('EmptyState', () => {
14
18
  })
15
19
 
16
20
  it('renders with description', () => {
17
- render(
18
- <EmptyState
19
- title="No messages"
20
- description="You don't have any messages yet."
21
- />
22
- )
21
+ render(<EmptyState title="No messages" description="You don't have any messages yet." />)
23
22
  expect(screen.getByText("You don't have any messages yet.")).toBeInTheDocument()
24
23
  })
25
24
 
@@ -45,12 +44,7 @@ describe('EmptyState', () => {
45
44
 
46
45
  it('renders action element', () => {
47
46
  const onPress = vi.fn()
48
- render(
49
- <EmptyState
50
- title="No results"
51
- action={<button onClick={onPress}>Create New</button>}
52
- />
53
- )
47
+ render(<EmptyState title="No results" action={<button onClick={onPress}>Create New</button>} />)
54
48
  const actionButton = screen.getByText('Create New')
55
49
  expect(actionButton).toBeInTheDocument()
56
50
 
@@ -35,34 +35,20 @@ export function EmptyState({
35
35
  ...props
36
36
  }: EmptyStateProps) {
37
37
  return (
38
- <View
39
- className={cn(
40
- 'items-center justify-center py-12 px-6',
41
- className
42
- )}
43
- {...props}
44
- >
38
+ <View className={cn('items-center justify-center py-12 px-6', className)} {...props}>
45
39
  {Icon && (
46
40
  <View className="mb-4 p-4 rounded-full bg-background-subtle">
47
41
  <Icon size={32} className="text-text-tertiary" />
48
42
  </View>
49
43
  )}
50
44
 
51
- <Text className="text-lg font-semibold text-text-primary text-center mb-2">
52
- {title}
53
- </Text>
45
+ <Text className="text-lg font-semibold text-text-primary text-center mb-2">{title}</Text>
54
46
 
55
47
  {description && (
56
- <Text className="text-sm text-text-secondary text-center max-w-xs mb-6">
57
- {description}
58
- </Text>
48
+ <Text className="text-sm text-text-secondary text-center max-w-xs mb-6">{description}</Text>
59
49
  )}
60
50
 
61
- {action && (
62
- <View className="mt-2">
63
- {action}
64
- </View>
65
- )}
51
+ {action && <View className="mt-2">{action}</View>}
66
52
  </View>
67
53
  )
68
54
  }
@@ -12,9 +12,9 @@ const PAGE_BG = greyRamp[975]
12
12
  const t = getSemanticColors('dark')
13
13
 
14
14
  const meta: Meta<typeof DualGhostSpark> = {
15
- title: 'Workout/Fatigue/Dual Ghost Spark',
15
+ title: 'Custom/Fatigue/Dual Ghost Spark',
16
16
  component: DualGhostSpark,
17
- tags: ['autodocs'],
17
+ tags: ['autodocs', 'status:candidate', '!status:review'],
18
18
  parameters: {
19
19
  docs: {
20
20
  description: {
@@ -24,7 +24,8 @@ const meta: Meta<typeof DualGhostSpark> = {
24
24
  'wings share one time scale and one magnitude scale, so an L/R imbalance reads as bloom size ' +
25
25
  'rather than as two independently normalized charts. Each wing is tinted INDEPENDENTLY by its ' +
26
26
  'own current rep — silver while the rep stays controlled, warming through shades of red once ' +
27
- 'that side crosses the grind threshold — so one arm can go red while the other stays silver.',
27
+ 'that side crosses the grind threshold — so one arm can go red while the other stays silver.' +
28
+ ' Composes **GhostBand** · **GhostBloom**.',
28
29
  },
29
30
  },
30
31
  },
@@ -9,9 +9,9 @@ const PAGE_BG = greyRamp[975]
9
9
  const t = getSemanticColors('dark')
10
10
 
11
11
  const meta: Meta<typeof FatigueLights> = {
12
- title: 'Workout/Fatigue/Fatigue Lights',
12
+ title: 'Custom/Fatigue/Fatigue Lights',
13
13
  component: FatigueLights,
14
- tags: ['autodocs'],
14
+ tags: ['autodocs', 'status:candidate', '!status:review'],
15
15
  parameters: {
16
16
  docs: {
17
17
  description: {
@@ -12,9 +12,9 @@ const PAGE_BG = greyRamp[975]
12
12
  const t = getSemanticColors('dark')
13
13
 
14
14
  const meta: Meta<typeof GhostSpark> = {
15
- title: 'Workout/Fatigue/Ghost Spark',
15
+ title: 'Custom/Fatigue/Ghost Spark',
16
16
  component: GhostSpark,
17
- tags: ['autodocs'],
17
+ tags: ['autodocs', 'status:candidate', '!status:review'],
18
18
  parameters: {
19
19
  docs: {
20
20
  description: {
@@ -26,7 +26,8 @@ const meta: Meta<typeof GhostSpark> = {
26
26
  'collapse), over a soft paper-inspired ground (no hard outline). Given `targetTempoSeconds` ' +
27
27
  'the band also PACES: each run fills across `elapsed / target` of its own width and its label ' +
28
28
  'takes the ahead / on-pace / over tone. Every story here passes the prescription the mock reps ' +
29
- 'were generated against, so the fill you see is the real relationship, not a decoration.',
29
+ 'were generated against, so the fill you see is the real relationship, not a decoration.' +
30
+ ' Composes **GhostBand** · **GhostBloom**.',
30
31
  },
31
32
  },
32
33
  },
@@ -8,9 +8,9 @@ import { FATIGUE_STATES, WARMING_UP_MODEL, buildMockModel } from './fatigue-mock
8
8
  const t = getSemanticColors('dark')
9
9
 
10
10
  const meta: Meta<typeof LiveFatigueCard> = {
11
- title: 'Workout/Fatigue/Live Fatigue Card',
11
+ title: 'Custom/Fatigue/Live Fatigue Card',
12
12
  component: LiveFatigueCard,
13
- tags: ['autodocs'],
13
+ tags: ['autodocs', 'status:candidate', '!status:review'],
14
14
  parameters: {
15
15
  docs: {
16
16
  description: {
@@ -16,19 +16,22 @@
16
16
  * grain + crisp top rim-light + soft contact shadow) — the same material the ROM /
17
17
  * velocity bars are made of, now carrying the sheet they sit on. Token-sourced fill, no
18
18
  * hardcoded surface hex.
19
+ *
20
+ * MODE (TD-03.59). The edge and the paper fill resolve for the mode the enclosing
21
+ * `<Surface>` publishes, not for a module-scope dark pin. Outside any Surface the context
22
+ * still defaults to dark, so the wall display is unchanged.
19
23
  */
20
24
  import { View } from 'react-native'
21
- import { Surface } from '../../ui/surface/Surface'
25
+ import { Surface, useSurfaceMode } from '../../ui/surface'
22
26
  import { getSemanticColors } from '../../../theme/tokens/semantic'
23
27
  import { barPaper } from '../../../theme/materials'
28
+ import { cardChartHeight } from './panel-layout'
24
29
  import { VerdictHero } from './VerdictHero'
25
30
  import { FatigueLights } from './FatigueLights'
26
31
  import { RomProgressionChart } from './RomProgressionChart'
27
32
  import { GhostSpark } from './GhostSpark'
28
33
  import type { LiveFatigueModel } from './fatigue-model'
29
34
 
30
- const t = getSemanticColors('dark')
31
-
32
35
  export interface LiveFatigueCardProps {
33
36
  /** The live fatigue read-model for the current set. */
34
37
  model: LiveFatigueModel
@@ -42,8 +45,9 @@ const PAD = 18
42
45
  const GHOST_GUTTER = 4 // GhostSpark carries this L/R padding internally
43
46
 
44
47
  export function LiveFatigueCard({ model, width = 318, height }: LiveFatigueCardProps) {
48
+ const t = getSemanticColors(useSurfaceMode())
45
49
  const chartW = width - PAD * 2 - GHOST_GUTTER * 2
46
- const chartH = height != null ? Math.round(Math.min(240, Math.max(168, height * 0.4))) : 172
50
+ const chartH = cardChartHeight(height)
47
51
  return (
48
52
  <Surface
49
53
  level="base"
@@ -1,17 +1,19 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { View, Text } from 'react-native'
3
3
  import { LiveFatiguePanel } from './LiveFatiguePanel'
4
+ import { Surface } from '../../ui/surface'
4
5
  import { greyRamp } from '../../../theme/tokens/primitives'
5
6
  import { getSemanticColors } from '../../../theme/tokens/semantic'
6
7
  import { FATIGUE_STATES, buildMockPanelState } from './fatigue-mock'
8
+ import { PANEL_BREAKPOINTS } from './panel-layout'
7
9
 
8
10
  const PAGE_BG = greyRamp[975]
9
11
  const t = getSemanticColors('dark')
10
12
 
11
13
  const meta: Meta<typeof LiveFatiguePanel> = {
12
- title: 'Workout/Fatigue/Live Fatigue Panel',
14
+ title: 'Custom/Fatigue/Live Fatigue Panel',
13
15
  component: LiveFatiguePanel,
14
- tags: ['autodocs'],
16
+ tags: ['autodocs', 'status:candidate', '!status:review'],
15
17
  parameters: {
16
18
  layout: 'fullscreen',
17
19
  docs: {
@@ -33,18 +35,126 @@ type Story = StoryObj<typeof LiveFatiguePanel>
33
35
  export const LivePanelV2: Story = {
34
36
  name: 'Live panel v2 (composition)',
35
37
  render: () => {
36
- const { model, velocity, header } = buildMockPanelState(FATIGUE_STATES[3].current, {
38
+ const { model, velocity } = buildMockPanelState(FATIGUE_STATES[3].current, {
37
39
  rpe: 10,
38
40
  verdict: FATIGUE_STATES[3].model.verdict,
39
41
  })
40
42
  return (
41
43
  <View style={{ backgroundColor: PAGE_BG, padding: 24 }}>
42
- <LiveFatiguePanel model={model} velocity={velocity} header={header} />
44
+ <LiveFatiguePanel model={model} velocity={velocity} />
43
45
  </View>
44
46
  )
45
47
  },
46
48
  }
47
49
 
50
+ /**
51
+ * The stack/expand tiers side by side (TD-03.58). Each frame pins `containerWidth` so the
52
+ * tier is deterministic in the visual layer; live, the panel measures itself in `onLayout`
53
+ * and picks the same tier from the real width. Nothing transitions between tiers.
54
+ */
55
+ export const ResponsiveTiers: Story = {
56
+ name: 'Responsive tiers (stack → row → wall)',
57
+ render: () => {
58
+ const { model, velocity } = buildMockPanelState(FATIGUE_STATES[1].current, {
59
+ rpe: FATIGUE_STATES[1].model.rpe,
60
+ verdict: FATIGUE_STATES[1].model.verdict,
61
+ })
62
+ const frames: Array<{ label: string; width: number; bodyHeight: number }> = [
63
+ { label: `xs · 480px · stacked`, width: 480, bodyHeight: 560 },
64
+ {
65
+ label: `sm · ${PANEL_BREAKPOINTS.sm}px · stacked`,
66
+ width: PANEL_BREAKPOINTS.sm,
67
+ bodyHeight: 560,
68
+ },
69
+ {
70
+ label: `md · ${PANEL_BREAKPOINTS.md}px · row`,
71
+ width: PANEL_BREAKPOINTS.md,
72
+ bodyHeight: 508,
73
+ },
74
+ {
75
+ label: `lg · ${PANEL_BREAKPOINTS.lg}px · row`,
76
+ width: PANEL_BREAKPOINTS.lg,
77
+ bodyHeight: 508,
78
+ },
79
+ {
80
+ label: `xl · ${PANEL_BREAKPOINTS.xl}px · wall, card expands`,
81
+ width: PANEL_BREAKPOINTS.xl,
82
+ bodyHeight: 620,
83
+ },
84
+ ]
85
+ return (
86
+ <View style={{ backgroundColor: PAGE_BG, padding: 24, gap: 24 }}>
87
+ {frames.map((f) => (
88
+ <View key={f.label} style={{ gap: 6, width: f.width }}>
89
+ <Text
90
+ style={{
91
+ fontSize: 9,
92
+ letterSpacing: 1,
93
+ fontFamily: 'monospace',
94
+ color: t['text-tertiary'],
95
+ }}
96
+ >
97
+ {f.label}
98
+ </Text>
99
+ <LiveFatiguePanel
100
+ model={model}
101
+ velocity={velocity}
102
+ containerWidth={f.width}
103
+ bodyHeight={f.bodyHeight}
104
+ />
105
+ </View>
106
+ ))}
107
+ </View>
108
+ )
109
+ },
110
+ }
111
+
112
+ /**
113
+ * The wall case on its own, at the real 1920 stage the SPA renders on. This is the frame
114
+ * to look at from across a room: the card expands out of its 318 sliver and the hero keeps
115
+ * the lead.
116
+ */
117
+ export const WallWidth: Story = {
118
+ name: 'Wall width (1920)',
119
+ render: () => {
120
+ const { model, velocity } = buildMockPanelState(FATIGUE_STATES[2].current, {
121
+ rpe: FATIGUE_STATES[2].model.rpe,
122
+ verdict: FATIGUE_STATES[2].model.verdict,
123
+ })
124
+ return (
125
+ <View style={{ backgroundColor: PAGE_BG, width: PANEL_BREAKPOINTS.xl }}>
126
+ <LiveFatiguePanel
127
+ model={model}
128
+ velocity={velocity}
129
+ containerWidth={PANEL_BREAKPOINTS.xl}
130
+ bodyHeight={820}
131
+ />
132
+ </View>
133
+ )
134
+ },
135
+ }
136
+
137
+ /**
138
+ * The same panel on a LIGHT surface (TD-03.59). Nothing about the panel changes — the
139
+ * `<Surface theme="light">` publishes the mode and the panel's eyebrow and the card's
140
+ * edge/paper resolve against it. Before the Surface adoption both were pinned to dark
141
+ * and this story rendered dark-on-light.
142
+ */
143
+ export const OnLightSurface: Story = {
144
+ name: 'On a light surface',
145
+ render: () => {
146
+ const { model, velocity } = buildMockPanelState(FATIGUE_STATES[0].current, {
147
+ rpe: FATIGUE_STATES[0].model.rpe,
148
+ verdict: FATIGUE_STATES[0].model.verdict,
149
+ })
150
+ return (
151
+ <Surface level="background" theme="light" rounded={false} style={{ padding: 24 }}>
152
+ <LiveFatiguePanel model={model} velocity={velocity} />
153
+ </Surface>
154
+ )
155
+ },
156
+ }
157
+
48
158
  /** The whole system across the verdict spectrum — GOOD → SLOWING → GRINDING → FORM BREAKING DOWN. */
49
159
  export const LiveStates: Story = {
50
160
  name: 'Live states (Good → Breaking down)',
@@ -70,7 +180,7 @@ export const LiveStates: Story = {
70
180
  </Text>
71
181
  </View>
72
182
  {FATIGUE_STATES.map((s) => {
73
- const { model, velocity, header } = buildMockPanelState(s.current, {
183
+ const { model, velocity } = buildMockPanelState(s.current, {
74
184
  rpe: s.model.rpe,
75
185
  verdict: s.model.verdict,
76
186
  })
@@ -86,7 +196,7 @@ export const LiveStates: Story = {
86
196
  >
87
197
  {s.name}
88
198
  </Text>
89
- <LiveFatiguePanel model={model} velocity={velocity} header={header} />
199
+ <LiveFatiguePanel model={model} velocity={velocity} />
90
200
  </View>
91
201
  )
92
202
  })}
@@ -0,0 +1,134 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { LiveFatiguePanel } from './LiveFatiguePanel'
4
+ import { Surface } from '../../ui/surface'
5
+ import { getSemanticColors } from '../../../theme/tokens/semantic'
6
+ import { buildMockPanelState } from './fatigue-mock'
7
+ import { PANEL_BREAKPOINTS, CARD_WIDTH_BASE, panelLayout } from './panel-layout'
8
+
9
+ const { model, velocity } = buildMockPanelState(3)
10
+
11
+ const dark = getSemanticColors('dark')
12
+ const light = getSemanticColors('light')
13
+
14
+ describe('LiveFatiguePanel', () => {
15
+ it('composes the aura frame, the velocity hero and the fatigue card', () => {
16
+ render(<LiveFatiguePanel model={model} velocity={velocity} />)
17
+ expect(screen.getByTestId('live-fatigue-panel')).toBeInTheDocument()
18
+ expect(screen.getByTestId('live-fatigue-card')).toBeInTheDocument()
19
+ expect(screen.getByText('VELOCITY · this set')).toBeInTheDocument()
20
+ })
21
+
22
+ // The wall display is the only surface this ships on today, so a colour move here is a
23
+ // regression, not an improvement. Outside any Surface the context defaults to dark.
24
+ it('resolves dark on-surface colours with no enclosing Surface', () => {
25
+ render(<LiveFatiguePanel model={model} velocity={velocity} />)
26
+ expect(screen.getByTestId('live-fatigue-eyebrow')).toHaveStyle({
27
+ color: dark['text-tertiary'],
28
+ })
29
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({
30
+ borderTopColor: dark['hairline-default'],
31
+ backgroundColor: dark['surface-base'],
32
+ })
33
+ })
34
+
35
+ it('resolves the same dark colours inside a dark Surface', () => {
36
+ render(
37
+ <Surface level="background" theme="dark">
38
+ <LiveFatiguePanel model={model} velocity={velocity} />
39
+ </Surface>
40
+ )
41
+ expect(screen.getByTestId('live-fatigue-eyebrow')).toHaveStyle({
42
+ color: dark['text-tertiary'],
43
+ })
44
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({
45
+ borderTopColor: dark['hairline-default'],
46
+ backgroundColor: dark['surface-base'],
47
+ })
48
+ })
49
+
50
+ // The point of TD-03.59: a light surface used to render dark-pinned text on a light plane.
51
+ it('follows a light Surface instead of staying pinned to dark', () => {
52
+ render(
53
+ <Surface level="background" theme="light">
54
+ <LiveFatiguePanel model={model} velocity={velocity} />
55
+ </Surface>
56
+ )
57
+ expect(screen.getByTestId('live-fatigue-eyebrow')).toHaveStyle({
58
+ color: light['text-tertiary'],
59
+ })
60
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({
61
+ borderTopColor: light['hairline-default'],
62
+ backgroundColor: light['surface-base'],
63
+ })
64
+ expect(light['text-tertiary']).not.toBe(dark['text-tertiary'])
65
+ })
66
+ })
67
+
68
+ // `onLayout` never fires under jsdom (SIZE-D01 / gotcha 6), so the panel takes an explicit
69
+ // `containerWidth` override and these drive the live wiring through it. The breakpoint
70
+ // arithmetic itself is covered in `panel-layout.test.ts`.
71
+ describe('LiveFatiguePanel responsiveness (TD-03.58)', () => {
72
+ it('renders the shipped row geometry at the md and lg tiers', () => {
73
+ for (const width of [1000, 1440]) {
74
+ const { unmount } = render(
75
+ <LiveFatiguePanel model={model} velocity={velocity} containerWidth={width} />
76
+ )
77
+ expect(screen.getByTestId('live-fatigue-body')).toHaveStyle({
78
+ flexDirection: 'row',
79
+ padding: '24px 24px 24px 24px',
80
+ })
81
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({
82
+ width: `${CARD_WIDTH_BASE}px`,
83
+ })
84
+ unmount()
85
+ }
86
+ })
87
+
88
+ it('stacks the card under the hero below the md edge', () => {
89
+ render(
90
+ <LiveFatiguePanel
91
+ model={model}
92
+ velocity={velocity}
93
+ containerWidth={PANEL_BREAKPOINTS.md - 1}
94
+ />
95
+ )
96
+ expect(screen.getByTestId('live-fatigue-body')).toHaveStyle({ flexDirection: 'column' })
97
+ })
98
+
99
+ it('gives the stacked card the full content width instead of the 318 column', () => {
100
+ const width = 720
101
+ render(<LiveFatiguePanel model={model} velocity={velocity} containerWidth={width} />)
102
+ const { padding } = panelLayout(width)
103
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({
104
+ width: `${width - padding * 2}px`,
105
+ })
106
+ })
107
+
108
+ it('expands the card column at wall width', () => {
109
+ render(<LiveFatiguePanel model={model} velocity={velocity} containerWidth={1920} />)
110
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({
111
+ width: `${panelLayout(1920).cardWidth}px`,
112
+ })
113
+ expect(panelLayout(1920).cardWidth).toBeGreaterThan(CARD_WIDTH_BASE)
114
+ })
115
+
116
+ it('still honours an explicit cardWidth over the tier', () => {
117
+ render(
118
+ <LiveFatiguePanel model={model} velocity={velocity} containerWidth={1920} cardWidth={280} />
119
+ )
120
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({ width: '280px' })
121
+ })
122
+
123
+ // TD-03.60 through the rendered tree, not just the pure split.
124
+ it('moves the rendered card height when bodyHeight changes', () => {
125
+ const { rerender } = render(
126
+ <LiveFatiguePanel model={model} velocity={velocity} containerWidth={1440} bodyHeight={400} />
127
+ )
128
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({ height: '400px' })
129
+ rerender(
130
+ <LiveFatiguePanel model={model} velocity={velocity} containerWidth={1440} bodyHeight={700} />
131
+ )
132
+ expect(screen.getByTestId('live-fatigue-card')).toHaveStyle({ height: '700px' })
133
+ })
134
+ })