@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
@@ -0,0 +1,99 @@
1
+ # Library roadmap — decided 2026-09-08
2
+
3
+ The operator reviewed a deep critique of `packages/ui` (attached as
4
+ `docs/audits/2026-09-08-library-critique.md`) and answered its sixteen
5
+ competing-system questions. This file is the record. When a change touches
6
+ one of these areas, it follows the decision here or reopens it explicitly.
7
+
8
+ ## Standing policies
9
+
10
+ **Colour.** Every colour used anywhere comes from a colour ramp (`greyRamp`,
11
+ the OKLCH ramps in `theme/tokens/primitives.ts`). Semantic tokens and palettes
12
+ reference ramp steps; components use only semantic tokens. No file declares
13
+ its own colour. The eslint token-purity error block extends to every family;
14
+ the raw-colour baseline (`titan/no-raw-color`) shrinks per PR and is deleted at
15
+ zero. Workout and Fatigue are not dark-only by design: they become theme-correct.
16
+
17
+ **Consumers.** We own every downstream consumer of `@titan-design/react-ui`
18
+ (audiobook, source-linked; voltras-mcp; agent-chat, tokens only; voltras
19
+ mobile; codewatch; two stale forks). Breaking changes are allowed. A component
20
+ being retired is marked `@deprecated` in code first so no new usage appears,
21
+ and each consumer gets a follow-up task to migrate.
22
+
23
+ **Depth.** See Foundations/Depth. Tone plus lift (rim 0.12, ambient shadow
24
+ scaled by planes crossed) on every raised plane; floating surfaces carry the
25
+ rim and no hairline ring; lift sparingly, inset inside cards; light mode is
26
+ deferred to its own pass.
27
+
28
+ ## Decisions (critique §B)
29
+
30
+ | # | Question | Decision |
31
+ |---|---|---|
32
+ | 1 | Container | `Card` is the only content container. `Surface` is for shell roots (shell, page, rail). `Tile` becomes a Card stat preset. `Section`/`SectionHeader` become Card presets (voltras mobile uses them), not deleted. |
33
+ | 2 | numeric vs named elevation | Both public: `level` is absolute for shell roots, `raise`/`elevation` are relative. Landed in #166. |
34
+ | 3 | Pills | One `Pill` primitive (tone / size / leading slot). `Badge`, `Chip`, `StatusPill`, `PrBadge`, `MuscleGroupChip` survive as thin presets (external users). `BaseBadge`, `WorkoutPill`, `SessionStatePill`, `WeightBadge`, `SeverityLabel`, `CoChangeChip` fold in or are deleted. |
35
+ | 4 | Divider | Every rule is a `Divider`. `border-hairline*` is legal only on Card `outline`/`subtle`, Table rules, and input borders. |
36
+ | 5 | HelpTip | Delete `HelpTip` and `LabelWithHelp`; a Tooltip story shows the pattern. |
37
+ | 6 | Theme source | `SurfaceContext`/`ThemeProvider` only. `utils/useTheme.ts` is deleted (Card no longer uses it after #166). |
38
+ | 7 | Workout/Fatigue dark-only? | No. Full theme-correct port (E3), in the batches below. |
39
+ | 8 | Colour math | One `theme/color-math.ts` with CIELAB L* as the sanctioned metric; duplicate `hexToRgb` ×4, `mixHex` ×4, luminance ×3, test `lstar` ×3 deleted. |
40
+ | 9 | Typography | `Typography` and `Eyebrow` move into `ui/`; re-export shims stay in `custom/`. |
41
+ | 10 | Dot | `Indicator` survives. `StatusDot`'s consumers migrate; its inline glows route through `getGlowShadow`. Fatigue README corrected. |
42
+ | 11 | Metric | One `Metric` with size / align / trend. `MetricTiles` becomes a preset (one external site); `MetricCell` deleted (no consumers). |
43
+ | 12 | paperSheet | Stays hero-only. Cards use the lift (rim 0.12); grain is not on ordinary cards. |
44
+ | 13 | Formatters | `utils/format/{number,time,workout}`. No component-local `formatX` or inline `toFixed`; lint enforces after migration. |
45
+ | 14 | Storybook tree | Six groups: `Foundations/` · `Components/Atoms|Molecules|Organisms/` (ui/* only) · `Custom/<Family>/` · `Shell/` · `Pages/` · `Lab/<Family>/` (src/lab only). |
46
+ | 15 | Lint | One mechanism: the per-family token-purity error block, extended everywhere; baseline retired at zero. |
47
+ | 16 | Light mode | Deferred. Task filed. Not a target for any family until the dark port is done. |
48
+
49
+ ## Epics, in order
50
+
51
+ **E1 · Depth follow-ups** (after #166; starts now)
52
+ - 13 `ui/*` overlays (autocomplete, drawer, help-tip, indicator, menu, modal, popover, select, switch, tabs, toast, toolbar-button, tooltip) move from Tailwind `shadow-*` + hairline ring to the floating lift recipe. Done when `grep -rE "shadow-(sm|md|lg|xl|2xl)" src/components/ui` is empty.
53
+ - Delete `utils/useTheme.ts`.
54
+ - Replace literal-hex pins in `Surface.test.tsx`, `surface.contract.test.ts`, `Card.test.tsx`, `elevation.test.ts` with `greyRamp[...]` references.
55
+ - Purge stale comments: `SessionRail.stories.tsx:40`, `utils/colors.ts:4`, `SessionRail.tsx:16-17`, the skill's `gotchas.md` "Neumorphic depth" section.
56
+ - Delete the `lab/north-star/surfaces.ts` fork and `DualGhostLine`'s local copies; point lab at `theme/materials`.
57
+ - Glow shadows (StatusDot ×7, Indicator, MesoCard, BodyMap, ZoneTrack, IntensityBar, PrHistoryModal) route through `getGlowShadow`.
58
+
59
+ **E4 · Storybook reorganisation** (can run beside E1 on non-overlapping files)
60
+ - Retitle onto the six-group tree; fix `Composes` links.
61
+ - Move the 8 `Lab/`-titled files out of `src/components` (they publish to npm today).
62
+ - Delete duplicate `Lab/ActiveWork/*` (6), `Lab/Components/*` (3), `Lab/Archive/Surface`, each with a REJECTED.md entry.
63
+ - ASCII titles; one naming convention per family.
64
+ - `autodocs` + a `Composes` line on every hardened story; decorators compose `<Surface level="base">`.
65
+ - Thin per-state stories to Default + controls (Input 15, Card 14, Alert 14, Toast 13).
66
+
67
+ **E5 · Governance refresh** (after E4 so tags land on final titles)
68
+ - Regenerate `arch-graph.json`; CI fails when it is older than the newest `index.ts`.
69
+ - REJECTED.md backfill for the ten archived directions plus retired `inset`, `tonalFill`/`ditherTile`, `data-1..10`.
70
+ - `status:` tags; MATURITY review for `ui/*` (38 primitives, not 52).
71
+ - READMEs for `ui/*`, charts, Prose; fix Fatigue README's StatusDot line.
72
+ - Update the workflow skill's `gotchas.md` and `06-lint-guardrails.md`.
73
+
74
+ **E2 · Consolidation** (decisions 1, 3, 4, 5, 10, 11)
75
+ - Card as sole container; Tile and Section as presets; loose `bg-surface-*` Views (58 sites) onto Card.
76
+ - Pill primitive + presets; `Chip` loses `neutral-*`/`text-white`.
77
+ - Divider everywhere (21 rules). Indicator over StatusDot. Metric absorbs MetricTiles; MetricCell deleted. HelpTip deleted.
78
+ - Each retired export: `@deprecated` first, then a consumer-update task per downstream repo.
79
+
80
+ **E3 · Port Workout and Fatigue** (decisions 7, 8, 9, 13, 15)
81
+ - Typography + Eyebrow into `ui/`; `theme/color-math.ts`; `utils/format/*`.
82
+ - Workout batches from the 2026-09-08 audit, smallest drift and most consumers first:
83
+ - B1 (S): MetricCell, SetStrip, SetTableHeader, SetsRepsLoad, ExerciseHeading, ExerciseIndicator, ExerciseCardHeading, PrBadge.
84
+ - B2 (M): SegmentedBar, MuscleGroupChip, PlaceholderStrip, ScheduleTiles, StatusPill, SupersetWrapper, MesoProgressBar, Sparkline, VolumeLandmarkBar, SessionHeader, WeekRow, WorkoutCard.
85
+ - B3 (M): StatusDot (6 cross-family consumers), WeightBadge (the one render-path `getSemanticColors('dark')` bug, lines 46 and 105), ExerciseCard, SetRow, SetBar, WorkoutPill.
86
+ - B4 (M/L): InputBar, PrHistoryModal, RestTimer, SessionRail, BodyMap (the pattern to copy: three correct `resolveColor` calls; one leftover module constant), MesoCard.
87
+ - B5 (L): MesoStatusCard (drift 40, highest), TempoDisplay, BodyMapDetailPanel, StrengthTrendChart, ZoneTrack, CapacityBandChart, IntensityBar, ReadinessCheck, the five page organisms, VelocityStrip (1,468 lines).
88
+ - Fatigue: 9 module constants, local colour math.
89
+ - `ui/*`: 81 raw `Text` onto Typography; Button `#ffffff`, Alert `bg-black`, Tooltip `bg-neutral-800`, Spinner hex.
90
+ - Lint: every family in the error block; delete `raw-color-baseline.json` at zero.
91
+ - Move `velocity-story-kit.tsx` and `setHeadingKit.tsx` out of `components/`.
92
+
93
+ **E6 · Code-quality floor** (independent)
94
+ - Split the 29 functions over 100 lines (four charts >300 lines; VelocityStrip).
95
+ - eslint warnings 81 → 0. `axe` in the 16 test files lacking it. Delete the 18 gitignored `tmp-*.mjs`.
96
+
97
+ ## Consumer follow-ups (one task per repo when E2 lands)
98
+
99
+ audiobook/frontend (Badge, Chip, StatusPill) · voltras-mcp (Pill, Badge, PrBadge, MuscleGroupChip, MetricTiles, StatusPill type) · voltras mobile (Card, Metric, Section, SectionHeader, `getSemanticColors`/`alpha`) · codewatch (Badge, Metric) · voltras-mcp-wave0 and codewatch-wt-c76 (stale forks: retire or repoint).
@@ -0,0 +1,130 @@
1
+ # Render tests and the visual-baseline layers
2
+
3
+ What a render test proves in this library, what it doesn't, and when to reach for one of
4
+ the three visual layers instead.
5
+
6
+ ## What a render test is
7
+
8
+ Every component ships a `Component.test.tsx` next to it — 149 of them today, one per
9
+ component, each importing `render`/`screen` from `@testing-library/react`
10
+ (`grep -l "from '@testing-library/react'" src -r --include="*.test.tsx" | wc -l`). A
11
+ render test mounts the component with React Testing Library and asserts against the DOM
12
+ it produces: text is present, a role exists, a prop toggles an attribute, `jest-axe`
13
+ finds no violations. `src/components/ui/button/Button.test.tsx` is the reference shape —
14
+ mount variants, fire events, assert on `screen`, then an `accessibility` block running
15
+ `axe(container)`.
16
+
17
+ Coverage thresholds are enforced by Vitest, scoped to components only
18
+ (`vitest.config.ts:26-31`):
19
+
20
+ ```ts
21
+ coverage: {
22
+ include: ['src/components/**/*.{ts,tsx}'],
23
+ exclude: ['src/**/*.stories.tsx', 'src/**/*.test.tsx', 'src/**/index.ts'],
24
+ thresholds: { statements: 80, branches: 80, functions: 80, lines: 80 },
25
+ }
26
+ ```
27
+
28
+ ## What it does NOT prove
29
+
30
+ **A render test that asserts a component mounts and toggles does not prove it renders
31
+ correctly.** Testing Library's queries only see whatever the test environment's DOM
32
+ diffing actually produced — and that instrumentation can miss a real defect even when a
33
+ component is visibly broken. VW-100's fix for `AdvancedAccordion` hit this directly: a
34
+ deliberately-broken control produced **zero warnings** under the (React Native) jest-expo
35
+ harness, because the strict-mode instrumentation needed a real React fiber render pass
36
+ that harness wasn't doing. The render test only proved the component mounted and
37
+ toggled — not that it looked right. Titan's own render tests use a real browser DOM via
38
+ jsdom + RTL rather than jest-expo, which is a stronger environment, but the underlying
39
+ limit is the same: **DOM assertions do not check pixels.** Nothing in this suite is
40
+ visual proof. That's what the three layers below are for. Don't read a green render
41
+ suite, or green coverage numbers, as evidence a component looks correct — only that its
42
+ DOM/behavior contract holds.
43
+
44
+ ## The three visual layers, and when to reach for each
45
+
46
+ Titan runs `@playwright/test` `^1.58.2` (`packages/ui/package.json`) against four
47
+ configs. Three are wired into `.github/workflows/visual.yml`; the fourth is a local-only
48
+ dev harness (see below).
49
+
50
+ | Layer | Config | Script | What it actually checks |
51
+ | ---------------------------------- | ------------------------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | 1 — component screenshot baselines | `playwright.baseline.config.ts` | `test:visual:baseline` | Boots the specimen dev server (`pnpm specimen`, port 5200) and runs `specimen/baseline/**/*.screenshot.test.ts` against committed `*-chromium-linux.png` baselines, `maxDiffPixelRatio: 0.01`. 68 baselines exist today, generated from the pinned `mcr.microsoft.com/playwright:v1.58.2-noble` container so they're byte-comparable to CI. |
53
+ | 2 — Storybook story baselines | `playwright.config.ts` (the default) | `test:visual:stories` | Boots Storybook (`pnpm storybook --ci`, port 6006) and runs `tests/visual/stories.spec.ts`, which enumerates `index.json` and screenshots every story matching `SCOPE = /^(shell-\|icons-\|icons--)/` — today that's the shell family plus the icon primitive, not the whole library. The clock is frozen and CSS animations disabled so control-driven/animated stories snapshot deterministically. |
54
+ | 3 — HTML-vs-React parity | `playwright.comparison.config.ts` | `test:visual:compare` | Boots the specimen dev server and runs `specimen/**/*.visual.test.ts`: a computed-style comparison between a hand-written HTML reference and the React/NativeWind render of the same component, to catch NativeWind/RNW output drifting from the intended CSS. |
55
+ | — token-resolution harness | `playwright.tokens.config.ts` | `test:visual:tokens` | Runs `specimen/token-resolution.spec.ts` against the specimen server. Not referenced anywhere in `.github/workflows/`, so it is a **local-only dev tool** — it does not gate anything today. |
56
+
57
+ Two files under `tests/visual/` — `visual.spec.ts` (a bare "storybook loads" smoke check)
58
+ and `validation.spec.ts` (ad hoc, assertion-free screenshot capture to
59
+ `tests/visual/validation/`) — exist but aren't targeted by any `package.json` script or
60
+ CI step. They're leftover/manual tooling, not part of the gated pattern; don't treat them
61
+ as covering anything.
62
+
63
+ **When to reach for which:** a render test for behavior/a11y on every component, always.
64
+ Layer 3 when you're worried NativeWind's web output has drifted from what the class names
65
+ should compute to. Layer 1 when you want a pixel-exact regression gate on a specific
66
+ component in isolation. Layer 2 when the thing you're worried about only shows up in a
67
+ real Storybook story (composition, controls, live/animated state) rather than the
68
+ specimen harness.
69
+
70
+ ## Do the visual layers actually run on pull requests?
71
+
72
+ **Partially.** `.github/workflows/visual.yml` triggers on `push`/`pull_request` to
73
+ `main`, and Layers 1 and 3 run their real gating scripts (`test:visual:baseline`,
74
+ `test:visual:compare`) — a pixel or computed-style mismatch fails the PR. **Layer 2 does
75
+ not currently gate anything**: the CI step runs `test:visual:stories:update` (the
76
+ `--update-snapshots` seed variant), not the gate, because baselines for it aren't
77
+ committed yet — the workflow's own comment calls this out as a bootstrap step and
78
+ documents the two-line change (commit the seeded PNGs, swap the script to
79
+ `test:visual:stories`) that would turn it into a real gate. Until that happens, a story
80
+ visual regression in the shell/icons scope will not fail your PR.
81
+
82
+ ## Where a new component's test goes, and its minimal shape
83
+
84
+ `Component.test.tsx` sits beside `Component.tsx` in `src/components/ui/<name>/` (or
85
+ `custom/<Family>/`), per the file-structure convention in the repo's `CLAUDE.md`.
86
+ Minimal shape, following `Button.test.tsx`:
87
+
88
+ ```tsx
89
+ import { describe, it, expect } from 'vitest'
90
+ import { render, screen } from '@testing-library/react'
91
+ import { axe } from 'jest-axe'
92
+ import { Component } from './Component'
93
+
94
+ describe('Component', () => {
95
+ it('renders correctly', () => {
96
+ render(<Component />)
97
+ // assert on screen.getBy...
98
+ })
99
+
100
+ it('has no accessibility violations', async () => {
101
+ const { container } = render(<Component />)
102
+ expect(await axe(container)).toHaveNoViolations()
103
+ })
104
+ })
105
+ ```
106
+
107
+ A new component does not automatically get a Layer 1/2/3 visual test — those are opted
108
+ into per-component (Layer 1) or per-story-scope (Layer 2), and per hand-written reference
109
+ (Layer 3). Nothing in the current config auto-enrolls a new component into any of them.
110
+
111
+ ## How the visual baselines are updated, and what a moved baseline means in review
112
+
113
+ - **Layer 1** (`test:visual:baseline:update`) regenerates `*-chromium-linux.png` under
114
+ `specimen/baseline/**/*.screenshot.test.ts-snapshots/`. `visual.yml` always runs this
115
+ step (`if: always()`) in the same pinned container as the gate and uploads the result
116
+ as the `component-visual-baselines` artifact, so a genuinely-changed component's
117
+ baseline comes back different while every unchanged one is byte-identical — no local
118
+ rendering noise. Refreshing means: download that artifact, commit the changed PNGs.
119
+ - **Layer 2** (`test:visual:stories:update`) regenerates
120
+ `tests/visual/reference/stories.spec.ts-snapshots/` the same way, uploaded as
121
+ `storybook-visual-baselines`; per the previous section this currently runs
122
+ unconditionally (it's the seed step, not a gate).
123
+ - **Layer 3** has no baseline images to move — it's a computed-style comparison, not a
124
+ screenshot diff.
125
+
126
+ **A moved baseline in review is a rendering change, not a rubber stamp.** Because Layer 1
127
+ baselines are only ever regenerated in the pinned container, a diff limited to the
128
+ changed component's PNG(s) is a real signal that its markup or styles moved — reviewers
129
+ should ask what changed and why, the same as any other diff, rather than treating "CI
130
+ regenerated it" as self-justifying.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@titan-design/react-ui",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "Cross-platform design system built on Gluestack UI",
5
5
  "author": "Henry Jewkes",
6
6
  "license": "MIT",
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import { useEffect, useMemo, useState } from 'react'
3
3
  import { getSemanticColors } from '../theme/tokens/semantic'
4
+ import { primitiveColors } from '../theme/tokens/primitives'
5
+ import { alpha } from '../utils/colors'
4
6
  import graph from './arch-graph.json'
5
7
 
6
8
  /**
@@ -396,7 +398,7 @@ function RenderModal({
396
398
  style={{
397
399
  position: 'fixed',
398
400
  inset: 0,
399
- background: 'rgba(0,0,0,0.66)',
401
+ background: alpha(primitiveColors.black, 0.66),
400
402
  display: 'flex',
401
403
  alignItems: 'center',
402
404
  justifyContent: 'center',
@@ -0,0 +1,52 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { describe, expect, it } from 'vitest'
5
+ // @ts-expect-error — plain-ESM build tooling, shared with scripts/arch-graph.mjs
6
+ import { componentBarrelHash } from '../../scripts/barrel-hash.mjs'
7
+ import graph from './arch-graph.json'
8
+
9
+ const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..')
10
+ const REPO_ROOT = path.resolve(PKG_ROOT, '../..')
11
+
12
+ describe('arch-graph.json freshness', () => {
13
+ it('records the barrel hash it was generated from', () => {
14
+ expect(graph.componentBarrelHash).toMatch(/^sha256:[0-9a-f]{64}$/)
15
+ })
16
+
17
+ it('matches the current component barrels', () => {
18
+ expect(
19
+ graph.componentBarrelHash,
20
+ 'arch-graph.json is stale: a component barrel changed since it was generated. ' +
21
+ 'Run `pnpm arch:graph -- --reindex` from the repo root and commit the result.'
22
+ ).toBe(componentBarrelHash(PKG_ROOT))
23
+ })
24
+
25
+ it('tallies every configured consumer, so none can be silently scored as zero usage', () => {
26
+ const configured = graph.summary.consumers.map((c) => c.name)
27
+ for (const component of graph.components) {
28
+ expect(
29
+ Object.keys(component.xproj)
30
+ .filter((k) => k !== 'total')
31
+ .sort(),
32
+ `${component.name}.xproj is missing a configured consumer. A consumer absent from ` +
33
+ 'the tally scores 0 usage, which lands its components on the `dead` list.'
34
+ ).toEqual([...configured].sort())
35
+ }
36
+ })
37
+
38
+ // The consumer list is committed precisely so every machine computes the same
39
+ // `dead` verdicts. Adding a consumer without regenerating leaves a graph that
40
+ // looks authoritative and is wrong about the repo that was just added.
41
+ it('was generated from the committed consumer list', () => {
42
+ const shared = JSON.parse(
43
+ readFileSync(path.join(REPO_ROOT, 'scripts/arch.config.json'), 'utf8')
44
+ ) as { consumers: { name: string }[] }
45
+
46
+ expect(
47
+ graph.summary.consumers.map((c) => c.name).sort(),
48
+ 'arch-graph.json was generated from a different consumer set than ' +
49
+ 'scripts/arch.config.json. Run `pnpm arch:graph -- --reindex` and commit the result.'
50
+ ).toEqual(shared.consumers.map((c) => c.name).sort())
51
+ })
52
+ })