@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,414 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { View, Text } from 'react-native'
3
+ import { alpha } from '../utils/colors'
4
+ import { getSemanticColors } from './tokens/semantic'
5
+ import { greyRamp, primitiveColors, primitiveRamps } from './tokens/primitives'
6
+ import { SectionIntro, SectionTitle, SWATCH_BORDER } from './color-story-kit'
7
+ import { ToolbarButton } from '../components/ui/toolbar-button'
8
+ import { Treemap } from '../components/custom/Treemap'
9
+
10
+ const t = getSemanticColors('dark')
11
+
12
+ /**
13
+ * Foundations/Color/Proposed VW-82 tokens — a DECISION story, not documentation.
14
+ *
15
+ * VW-82 (#200) migrated component source off raw colours. Every literal that had
16
+ * an exact token was swapped; the ones with no token were deliberately left raw
17
+ * and proposed rather than invented, because adding a token is a design call.
18
+ * This story renders those candidates in the context they would actually ship
19
+ * in, so the proposal can be accepted or rejected by eye instead of by hex.
20
+ *
21
+ * WHY THE SCRIMS ARE TOKENS AND NOT `bg-black/50`: Tailwind v3 cannot apply an
22
+ * opacity modifier to a `var()` colour — it fails to parse the value and emits
23
+ * NO rule at all. So a translucent role has to ship as its own rgba token, which
24
+ * is exactly what `hairline-*` already does (`rgba(255, 255, 255, 0.15)`, not
25
+ * `white/15`). The four scrims below are rendered through
26
+ * `alpha(primitiveColors.black, …)`, which is the value each token would carry.
27
+ *
28
+ * The two candidates that have NO token and no primitive — `on-control-idle`
29
+ * (#D1D1D1) and `on-data-strong` (#0B0B0B) — cannot be written as literals here:
30
+ * `no-raw-color` allots this file zero, and funding a new allowance to document
31
+ * a proposal would push the ratchet the wrong way. They are instead shown by
32
+ * rendering the REAL components that carry them today, side by side with the
33
+ * existing tokens that might replace them. That is stronger evidence anyway.
34
+ */
35
+ const meta: Meta<ProposedTokensArgs> = {
36
+ title: 'Foundations/Color/Proposed VW-82 tokens',
37
+ tags: ['autodocs'],
38
+ argTypes: {
39
+ showScrims: {
40
+ control: 'boolean',
41
+ description: 'Lift all four scrims off the backdrop, to judge their impact.',
42
+ },
43
+ backdrop: {
44
+ control: 'radio',
45
+ options: ['pattern', 'gradient', 'surface'],
46
+ description:
47
+ 'What the scrims sit on. `pattern` is the honest worst case — a scrim over a flat dark surface always looks fine.',
48
+ },
49
+ },
50
+ args: { showScrims: true, backdrop: 'pattern' },
51
+ }
52
+
53
+ export default meta
54
+
55
+ interface ProposedTokensArgs {
56
+ showScrims: boolean
57
+ backdrop: 'pattern' | 'gradient' | 'surface'
58
+ }
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // 1 — scrims
62
+ // ---------------------------------------------------------------------------
63
+
64
+ /**
65
+ * The four candidates, as the value each token would carry. Derived through
66
+ * `alpha()` from the black primitive rather than written as rgba triples, so
67
+ * they track the primitive the way the real token would.
68
+ */
69
+ const SCRIMS = [
70
+ {
71
+ name: 'scrim-press',
72
+ opacity: 0.1,
73
+ usedBy: 'Alert close button — web:hover',
74
+ },
75
+ {
76
+ name: 'scrim-press-strong',
77
+ opacity: 0.2,
78
+ usedBy: 'Alert close button — active',
79
+ },
80
+ {
81
+ name: 'scrim-subtle',
82
+ opacity: 0.3,
83
+ usedBy: 'Modal backdrop (blurred), Select filled fill',
84
+ },
85
+ {
86
+ name: 'scrim-default',
87
+ opacity: 0.5,
88
+ usedBy: 'Modal backdrop, Drawer overlay',
89
+ },
90
+ ] as const
91
+
92
+ const CHECKER_LIGHT = greyRamp[50]
93
+ const CHECKER_DARK = greyRamp[950]
94
+ const CHECKER_STEP = 20
95
+ const PANEL_W = 196
96
+ const PANEL_H = 140
97
+
98
+ /**
99
+ * A deliberately hostile backdrop. A scrim over a flat dark plane reads as
100
+ * "fine" no matter what its opacity is; the only way to judge one is to put
101
+ * something with real tonal range under it.
102
+ */
103
+ function Checkerboard({ rows, columns }: { rows: number; columns: number }) {
104
+ return (
105
+ <View style={{ position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 }}>
106
+ {Array.from({ length: rows }, (_, row) => (
107
+ <View key={row} style={{ flexDirection: 'row' }}>
108
+ {Array.from({ length: columns }, (_, column) => (
109
+ <View
110
+ key={column}
111
+ style={{
112
+ width: CHECKER_STEP,
113
+ height: CHECKER_STEP,
114
+ backgroundColor: (row + column) % 2 === 0 ? CHECKER_LIGHT : CHECKER_DARK,
115
+ }}
116
+ />
117
+ ))}
118
+ </View>
119
+ ))}
120
+ </View>
121
+ )
122
+ }
123
+
124
+ /** Stand-in for the photographic mid-tones a real app backdrop has. */
125
+ function GradientWash() {
126
+ return (
127
+ <View
128
+ style={{
129
+ position: 'absolute',
130
+ top: 0,
131
+ right: 0,
132
+ bottom: 0,
133
+ left: 0,
134
+ // react-native-web renders backgroundImage at runtime (see DeviationBar).
135
+ backgroundImage: `linear-gradient(135deg, ${primitiveRamps.blue[300]} 0%, ${primitiveRamps.magenta[300]} 38%, ${primitiveRamps.amber[200]} 72%, ${greyRamp[950]} 100%)`,
136
+ }}
137
+ />
138
+ )
139
+ }
140
+
141
+ /** Cards + copy on top of the backdrop, so the scrim has content to obscure. */
142
+ function BackdropContent() {
143
+ return (
144
+ <View style={{ flexDirection: 'row', gap: 6, padding: 10 }}>
145
+ {[primitiveRamps.orange[400], primitiveRamps.green[300], primitiveRamps.blue[300]].map(
146
+ (fill) => (
147
+ <View
148
+ key={fill}
149
+ style={{
150
+ width: 56,
151
+ height: 48,
152
+ borderRadius: 6,
153
+ backgroundColor: fill,
154
+ padding: 5,
155
+ }}
156
+ >
157
+ <Text style={{ color: greyRamp[975], fontSize: 10, fontWeight: '700' }}>Card</Text>
158
+ </View>
159
+ )
160
+ )}
161
+ </View>
162
+ )
163
+ }
164
+
165
+ function ScrimPanel({ showScrims, backdrop }: ProposedTokensArgs) {
166
+ return (
167
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 14 }}>
168
+ {SCRIMS.map((scrim) => (
169
+ <View key={scrim.name} style={{ width: PANEL_W }}>
170
+ <View
171
+ style={{
172
+ height: PANEL_H,
173
+ borderRadius: 8,
174
+ overflow: 'hidden',
175
+ borderWidth: 1,
176
+ borderColor: SWATCH_BORDER,
177
+ backgroundColor: t['surface-base'],
178
+ }}
179
+ >
180
+ {backdrop === 'pattern' && (
181
+ <Checkerboard
182
+ rows={Math.ceil(PANEL_H / CHECKER_STEP)}
183
+ columns={Math.ceil(PANEL_W / CHECKER_STEP)}
184
+ />
185
+ )}
186
+ {backdrop === 'gradient' && <GradientWash />}
187
+ <BackdropContent />
188
+ {showScrims && (
189
+ <View
190
+ style={{
191
+ position: 'absolute',
192
+ top: 0,
193
+ right: 0,
194
+ bottom: 0,
195
+ left: 0,
196
+ backgroundColor: alpha(primitiveColors.black, scrim.opacity),
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ }}
200
+ >
201
+ <Text style={{ color: primitiveColors.white, fontSize: 12, fontWeight: '700' }}>
202
+ {scrim.name}
203
+ </Text>
204
+ </View>
205
+ )}
206
+ </View>
207
+ <Text className="text-text-primary text-xs font-semibold mt-2">{scrim.name}</Text>
208
+ <Text className="text-text-tertiary" style={{ fontSize: 10 }}>
209
+ {alpha(primitiveColors.black, scrim.opacity)}
210
+ </Text>
211
+ <Text className="text-text-secondary" style={{ fontSize: 10, marginTop: 2 }}>
212
+ {scrim.usedBy}
213
+ </Text>
214
+ </View>
215
+ ))}
216
+ </View>
217
+ )
218
+ }
219
+
220
+ // ---------------------------------------------------------------------------
221
+ // 2 — on-control
222
+ // ---------------------------------------------------------------------------
223
+
224
+ const TOOLBAR_FACE = greyRamp[800]
225
+
226
+ /**
227
+ * A toolbar-button face rendered with a chosen label colour, matching the real
228
+ * `ToolbarButton` geometry so the comparison beside it is like-for-like.
229
+ */
230
+ function LabelChip({ label, color, caption }: { label: string; color: string; caption: string }) {
231
+ return (
232
+ <View style={{ alignItems: 'center', width: 116 }}>
233
+ <View
234
+ style={{
235
+ backgroundColor: TOOLBAR_FACE,
236
+ borderRadius: 4,
237
+ paddingHorizontal: 10,
238
+ paddingVertical: 4,
239
+ minHeight: 30,
240
+ justifyContent: 'center',
241
+ }}
242
+ >
243
+ <Text style={{ color, fontSize: 14, fontWeight: '700' }}>{label}</Text>
244
+ </View>
245
+ <Text className="text-text-secondary" style={{ fontSize: 10, marginTop: 4 }}>
246
+ {caption}
247
+ </Text>
248
+ <Text className="text-text-tertiary" style={{ fontSize: 9 }}>
249
+ {color.toUpperCase()}
250
+ </Text>
251
+ </View>
252
+ )
253
+ }
254
+
255
+ function OnControlPanel() {
256
+ return (
257
+ <View className="bg-surface-elevated" style={{ borderRadius: 8, padding: 16 }}>
258
+ <Text className="text-text-secondary text-xs mb-3">
259
+ Left pair: the LIVE ToolbarButton, which is what the proposal is about. Right: the existing
260
+ tokens, drawn on the same face at the same size — does one of them already do the job?
261
+ </Text>
262
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 14, alignItems: 'flex-start' }}>
263
+ <View style={{ alignItems: 'center', width: 116 }}>
264
+ <ToolbarButton label="Idle" isActive={false} variant="raised" />
265
+ <Text className="text-text-secondary" style={{ fontSize: 10, marginTop: 4 }}>
266
+ live · on-control-idle
267
+ </Text>
268
+ <Text className="text-text-tertiary" style={{ fontSize: 9 }}>
269
+ #D1D1D1
270
+ </Text>
271
+ </View>
272
+ <View style={{ alignItems: 'center', width: 116 }}>
273
+ <ToolbarButton label="Active" isActive variant="raised" />
274
+ <Text className="text-text-secondary" style={{ fontSize: 10, marginTop: 4 }}>
275
+ live · on-control-active
276
+ </Text>
277
+ <Text className="text-text-tertiary" style={{ fontSize: 9 }}>
278
+ #FFFFFF
279
+ </Text>
280
+ </View>
281
+ <LabelChip label="Idle" color={greyRamp[200]} caption="greyRamp[200]" />
282
+ <LabelChip label="Idle" color={t['text-secondary']} caption="text-secondary" />
283
+ <LabelChip label="Idle" color={t['text-primary']} caption="text-primary" />
284
+ <LabelChip label="Active" color={primitiveColors.white} caption="primitiveColors.white" />
285
+ </View>
286
+ <Text className="text-text-tertiary text-xs mt-3">
287
+ `on-control-active` is exactly `primitiveColors.white`, so the only real question is whether
288
+ it deserves a NAME — the toolbar face is a grey plane, not a brand fill, so borrowing
289
+ `on-brand-primary` for it would be the wrong role at the right value.
290
+ </Text>
291
+ </View>
292
+ )
293
+ }
294
+
295
+ // ---------------------------------------------------------------------------
296
+ // 3 — on-data-strong
297
+ // ---------------------------------------------------------------------------
298
+
299
+ const LIGHT_TILES = [
300
+ { id: 'chest', value: 34, color: primitiveRamps.amber[200], label: 'Chest' },
301
+ { id: 'back', value: 28, color: primitiveRamps.green[200], label: 'Back' },
302
+ { id: 'quads', value: 21, color: primitiveRamps.blue[200], label: 'Quads' },
303
+ { id: 'delts', value: 14, color: primitiveRamps.orange[200], label: 'Delts' },
304
+ { id: 'arms', value: 9, color: primitiveRamps.magenta[200], label: 'Arms' },
305
+ ]
306
+
307
+ function TileRow({ color, caption }: { color: string; caption: string }) {
308
+ return (
309
+ <View>
310
+ <View style={{ flexDirection: 'row', gap: 2 }}>
311
+ {LIGHT_TILES.map((tile) => (
312
+ <View
313
+ key={tile.id}
314
+ style={{
315
+ width: 62,
316
+ height: 44,
317
+ borderRadius: 3,
318
+ backgroundColor: tile.color,
319
+ opacity: 0.9,
320
+ padding: 4,
321
+ }}
322
+ >
323
+ <Text style={{ color, fontSize: 10, fontWeight: '600' }}>{tile.label}</Text>
324
+ </View>
325
+ ))}
326
+ </View>
327
+ <Text className="text-text-secondary" style={{ fontSize: 10, marginTop: 4 }}>
328
+ {caption} · {color.toUpperCase()}
329
+ </Text>
330
+ </View>
331
+ )
332
+ }
333
+
334
+ function OnDataPanel() {
335
+ return (
336
+ <View className="bg-surface-elevated" style={{ borderRadius: 8, padding: 16 }}>
337
+ <Text className="text-text-secondary text-xs mb-3">
338
+ Top: the LIVE Treemap, whose label is the proposed `on-data-strong`. Below: the same tiles
339
+ with the nearest existing dark tokens, so the difference (or lack of one) is visible at
340
+ label size rather than as a swatch.
341
+ </Text>
342
+ <View style={{ gap: 12 }}>
343
+ <View>
344
+ <Treemap data={LIGHT_TILES} width={320} height={92} maxTiles={5} />
345
+ <Text className="text-text-secondary" style={{ fontSize: 10, marginTop: 4 }}>
346
+ live Treemap · on-data-strong · #0B0B0B
347
+ </Text>
348
+ </View>
349
+ <TileRow color={t['background-frame']} caption="background-frame" />
350
+ <TileRow color={greyRamp[950]} caption="greyRamp[950]" />
351
+ <TileRow color={primitiveColors.black} caption="primitiveColors.black" />
352
+ </View>
353
+ </View>
354
+ )
355
+ }
356
+
357
+ // ---------------------------------------------------------------------------
358
+
359
+ function ApprovedSnaps() {
360
+ return (
361
+ <View
362
+ style={{
363
+ backgroundColor: t['surface-raised'],
364
+ borderRadius: 8,
365
+ padding: 12,
366
+ marginBottom: 20,
367
+ }}
368
+ >
369
+ <Text className="text-text-primary text-sm font-semibold mb-1">
370
+ Already approved — the six grey snaps (follow-up, NOT in #200)
371
+ </Text>
372
+ <Text className="text-text-secondary text-xs">
373
+ These need no new token: they are drifted Tailwind and ad-hoc neutrals that snap onto the
374
+ existing warm ramp. Approved for the follow-up PR because each one moves pixels.
375
+ {'\n\n'}
376
+ #6B7280 (Tailwind gray-500) → result-neutral, at IntensityBar, TempoDisplay, setHeadingKit,
377
+ Spinner · #9CA3AF (Tailwind gray-400) → text-secondary, at TempoDisplay ×4 · #333333 →
378
+ surface-raised · #2C2C2C → surface-elevated · #3A3A3A → surface-overlay · #0A0A0A →
379
+ background-frame
380
+ </Text>
381
+ </View>
382
+ )
383
+ }
384
+
385
+ type Story = StoryObj<ProposedTokensArgs>
386
+
387
+ export const Default: Story = {
388
+ render: (args) => (
389
+ <View style={{ padding: 24, maxWidth: 900 }}>
390
+ <Text className="text-2xl font-bold text-text-primary mb-2">Proposed VW-82 tokens</Text>
391
+ <SectionIntro>
392
+ Seven candidates from #200, rendered where they would ship. Nothing here is in the token
393
+ layer — `semantic.ts`, `primitives.ts`, `global.css` and `tailwind.config.js` are untouched
394
+ until these are approved.
395
+ </SectionIntro>
396
+
397
+ <ApprovedSnaps />
398
+
399
+ <SectionTitle>1 · Scrims (4 candidates)</SectionTitle>
400
+ <Text className="text-text-secondary text-xs mb-3">
401
+ Toggle `showScrims` off to see what each one is hiding, and switch `backdrop` between the
402
+ checkerboard, a photographic gradient, and a plain surface. The plain surface is the case
403
+ that flatters every opacity equally — judge on the other two.
404
+ </Text>
405
+ <ScrimPanel {...args} />
406
+
407
+ <SectionTitle>2 · on-control (2 candidates)</SectionTitle>
408
+ <OnControlPanel />
409
+
410
+ <SectionTitle>3 · on-data-strong (1 candidate)</SectionTitle>
411
+ <OnDataPanel />
412
+ </View>
413
+ ),
414
+ }
@@ -14,7 +14,7 @@ describe('ThemeProvider', () => {
14
14
  render(
15
15
  <ThemeProvider>
16
16
  <Text>inside</Text>
17
- </ThemeProvider>,
17
+ </ThemeProvider>
18
18
  )
19
19
  expect(screen.getByText('inside')).toBeInTheDocument()
20
20
  })
@@ -23,7 +23,7 @@ describe('ThemeProvider', () => {
23
23
  render(
24
24
  <ThemeProvider mode="light">
25
25
  <Text>light</Text>
26
- </ThemeProvider>,
26
+ </ThemeProvider>
27
27
  )
28
28
  expect(screen.getByText('light')).toBeInTheDocument()
29
29
  })
@@ -32,7 +32,7 @@ describe('ThemeProvider', () => {
32
32
  render(
33
33
  <ThemeProvider mode="light">
34
34
  <ModeProbe />
35
- </ThemeProvider>,
35
+ </ThemeProvider>
36
36
  )
37
37
  expect(screen.getByText('light')).toBeInTheDocument()
38
38
  })
@@ -41,7 +41,7 @@ describe('ThemeProvider', () => {
41
41
  render(
42
42
  <ThemeProvider mode="system">
43
43
  <ModeProbe />
44
- </ThemeProvider>,
44
+ </ThemeProvider>
45
45
  )
46
46
  expect(screen.getByText('dark')).toBeInTheDocument()
47
47
  })
@@ -34,14 +34,15 @@ export {
34
34
  type ShadowIntensity,
35
35
  } from './color-utils'
36
36
  export {
37
- getElevationConfig,
37
+ ELEVATION_PLANE,
38
+ elevationPlane,
38
39
  getElevationSurface,
39
40
  getElevationShadow,
40
- getBaseSurfaceColor,
41
- getValidatedElevation,
42
- componentElevationRanges,
41
+ getPressedRecessShadow,
42
+ FLOATING_ELEVATION_MIN,
43
+ PRESSED_ELEVATION_LEVEL,
43
44
  type ElevationLevel,
44
- type ElevationConfig,
45
- type ComponentType,
46
45
  } from './elevation'
46
+ export * from './lift'
47
+ export * from './surface-planes'
47
48
  export * from './presets'
@@ -60,7 +60,7 @@ const DOCUMENTED_ELSEWHERE: Record<string, string> = {
60
60
  const isSurfaceToken = (name: string) => /^(surface|background)-/.test(name)
61
61
 
62
62
  /** Must mirror VARIANT_SUFFIXES in ColorPalettes.stories.tsx (asserted below). */
63
- const VARIANT_SUFFIXES = ['light', 'dark', 'subtle', 'hover', 'active', 'muted'] as const
63
+ const VARIANT_SUFFIXES = ['light', 'dark', 'subtle', 'muted', 'strong', 'hover', 'active'] as const
64
64
 
65
65
  describe('Foundations/Color story coverage', () => {
66
66
  it('every surface/background token resolves to a plane in the shipped ramp', () => {
@@ -26,9 +26,9 @@
26
26
  */
27
27
  export function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
28
28
  const cleaned = hex.replace('#', '')
29
-
29
+
30
30
  let r: number, g: number, b: number
31
-
31
+
32
32
  if (cleaned.length === 3) {
33
33
  r = parseInt(cleaned[0] + cleaned[0], 16)
34
34
  g = parseInt(cleaned[1] + cleaned[1], 16)
@@ -40,7 +40,7 @@ export function hexToRgb(hex: string): { r: number; g: number; b: number } | nul
40
40
  } else {
41
41
  return null
42
42
  }
43
-
43
+
44
44
  return { r, g, b }
45
45
  }
46
46
 
@@ -51,17 +51,17 @@ export function rgbToHsv(r: number, g: number, b: number): { h: number; s: numbe
51
51
  const rNorm = r / 255
52
52
  const gNorm = g / 255
53
53
  const bNorm = b / 255
54
-
54
+
55
55
  const max = Math.max(rNorm, gNorm, bNorm)
56
56
  const min = Math.min(rNorm, gNorm, bNorm)
57
57
  const delta = max - min
58
-
58
+
59
59
  // Value
60
60
  const v = max
61
-
61
+
62
62
  // Saturation
63
63
  const s = max === 0 ? 0 : delta / max
64
-
64
+
65
65
  // Hue
66
66
  let h = 0
67
67
  if (delta !== 0) {
@@ -74,7 +74,7 @@ export function rgbToHsv(r: number, g: number, b: number): { h: number; s: numbe
74
74
  }
75
75
  }
76
76
  if (h < 0) h += 360
77
-
77
+
78
78
  return { h, s, v }
79
79
  }
80
80
 
@@ -83,25 +83,37 @@ export function rgbToHsv(r: number, g: number, b: number): { h: number; s: numbe
83
83
  */
84
84
  export function hsvToRgb(h: number, s: number, v: number): { r: number; g: number; b: number } {
85
85
  const c = v * s
86
- const x = c * (1 - Math.abs((h / 60) % 2 - 1))
86
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
87
87
  const m = v - c
88
-
88
+
89
89
  let rPrime: number, gPrime: number, bPrime: number
90
90
 
91
91
  if (h >= 0 && h < 60) {
92
- rPrime = c; gPrime = x; bPrime = 0
92
+ rPrime = c
93
+ gPrime = x
94
+ bPrime = 0
93
95
  } else if (h >= 60 && h < 120) {
94
- rPrime = x; gPrime = c; bPrime = 0
96
+ rPrime = x
97
+ gPrime = c
98
+ bPrime = 0
95
99
  } else if (h >= 120 && h < 180) {
96
- rPrime = 0; gPrime = c; bPrime = x
100
+ rPrime = 0
101
+ gPrime = c
102
+ bPrime = x
97
103
  } else if (h >= 180 && h < 240) {
98
- rPrime = 0; gPrime = x; bPrime = c
104
+ rPrime = 0
105
+ gPrime = x
106
+ bPrime = c
99
107
  } else if (h >= 240 && h < 300) {
100
- rPrime = x; gPrime = 0; bPrime = c
108
+ rPrime = x
109
+ gPrime = 0
110
+ bPrime = c
101
111
  } else {
102
- rPrime = c; gPrime = 0; bPrime = x
112
+ rPrime = c
113
+ gPrime = 0
114
+ bPrime = x
103
115
  }
104
-
116
+
105
117
  return {
106
118
  r: Math.round((rPrime + m) * 255),
107
119
  g: Math.round((gPrime + m) * 255),
@@ -122,34 +134,34 @@ export function rgbToHex(r: number, g: number, b: number): string {
122
134
 
123
135
  /**
124
136
  * Lighten a color by increasing its HSV Value component.
125
- *
137
+ *
126
138
  * @param hex - The hex color to lighten
127
139
  * @param amount - Amount to increase V by (0-1)
128
140
  */
129
141
  export function lighten(hex: string, amount: number = 0.1): string {
130
142
  const rgb = hexToRgb(hex)
131
143
  if (!rgb) return hex
132
-
144
+
133
145
  const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
134
146
  const newV = Math.min(1, hsv.v + amount)
135
-
147
+
136
148
  const newRgb = hsvToRgb(hsv.h, hsv.s, newV)
137
149
  return rgbToHex(newRgb.r, newRgb.g, newRgb.b)
138
150
  }
139
151
 
140
152
  /**
141
153
  * Darken a color by decreasing its HSV Value component.
142
- *
154
+ *
143
155
  * @param hex - The hex color to darken
144
156
  * @param amount - Amount to decrease V by (0-1)
145
157
  */
146
158
  export function darken(hex: string, amount: number = 0.1): string {
147
159
  const rgb = hexToRgb(hex)
148
160
  if (!rgb) return hex
149
-
161
+
150
162
  const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
151
163
  const newV = Math.max(0, hsv.v - amount)
152
-
164
+
153
165
  const newRgb = hsvToRgb(hsv.h, hsv.s, newV)
154
166
  return rgbToHex(newRgb.r, newRgb.g, newRgb.b)
155
167
  }
@@ -157,12 +169,12 @@ export function darken(hex: string, amount: number = 0.1): string {
157
169
  /**
158
170
  * Get hover colors for a button based on its background color.
159
171
  * Automatically adapts intensity based on the color's luminance.
160
- *
172
+ *
161
173
  * @param bgColor - The button's background color (hex)
162
174
  * @param intensity - How strong the hover effect should be ('subtle' | 'medium' | 'strong')
163
175
  */
164
176
  export function getHoverColors(
165
- bgColor: string,
177
+ bgColor: string,
166
178
  intensity: 'subtle' | 'medium' | 'strong' = 'medium'
167
179
  ): { raised: string; pressed: string } {
168
180
  const amounts = {
@@ -170,12 +182,12 @@ export function getHoverColors(
170
182
  medium: 0.02,
171
183
  strong: 0.03,
172
184
  }
173
-
185
+
174
186
  const amount = amounts[intensity]
175
-
187
+
176
188
  return {
177
- raised: lighten(bgColor, amount), // Raised hover: lighten (lifting up)
178
- pressed: darken(bgColor, amount), // Pressed hover: darken (pressing down)
189
+ raised: lighten(bgColor, amount), // Raised hover: lighten (lifting up)
190
+ pressed: darken(bgColor, amount), // Pressed hover: darken (pressing down)
179
191
  }
180
192
  }
181
193
 
@@ -69,7 +69,7 @@ describe('token codegen completeness (config.ts vs global.css)', () => {
69
69
 
70
70
  it(`${mode}: config emits no var absent from global.css`, () => {
71
71
  const extra = Object.keys(cfg).filter(
72
- (name) => !globalVars.has(name) && !CODEGEN_ALLOWLIST.has(name),
72
+ (name) => !globalVars.has(name) && !CODEGEN_ALLOWLIST.has(name)
73
73
  )
74
74
  expect(extra, 'config.ts vars not present in global.css :root').toEqual([])
75
75
  })