@titan-design/react-ui 0.12.1 → 0.13.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 (509) hide show
  1. package/README.md +46 -47
  2. package/dist/bodymap.js +768 -701
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +770 -703
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +1319 -216
  7. package/dist/index.d.ts +1319 -216
  8. package/dist/index.js +5996 -4498
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +5773 -4357
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-CvsFqNtx.d.mts → pages-CpW3OB3g.d.mts} +21 -3
  13. package/dist/{pages-B1hqnGIC.d.ts → pages-_3Ajpiqu.d.ts} +21 -3
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +1435 -1428
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +1443 -1436
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +195 -134
  21. package/dist/theme/index.d.ts +195 -134
  22. package/dist/theme/index.js +215 -300
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +202 -296
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +62 -5
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +62 -5
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +73 -5
  31. package/dist/theme/tokens.d.ts +73 -5
  32. package/dist/theme/tokens.js +55 -7
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +55 -8
  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/package.json +1 -1
  40. package/src/arch/arch-graph.freshness.test.ts +52 -0
  41. package/src/arch/arch-graph.json +5021 -1617
  42. package/src/components/custom/ActiveWork/CoChangeChip.stories.tsx +44 -0
  43. package/src/components/custom/ActiveWork/CoChangeChip.test.tsx +29 -0
  44. package/src/components/custom/ActiveWork/CoChangeChip.tsx +48 -0
  45. package/src/components/custom/ActiveWork/Eyebrow.stories.tsx +25 -0
  46. package/src/components/custom/ActiveWork/Eyebrow.test.tsx +16 -0
  47. package/src/components/custom/ActiveWork/Eyebrow.tsx +26 -0
  48. package/src/components/custom/ActiveWork/FileActivityDetail.stories.tsx +50 -0
  49. package/src/components/custom/ActiveWork/FileActivityDetail.test.tsx +66 -0
  50. package/src/components/custom/ActiveWork/FileActivityDetail.tsx +202 -0
  51. package/src/components/custom/ActiveWork/FileActivityRow.stories.tsx +70 -0
  52. package/src/components/custom/ActiveWork/FileActivityRow.test.tsx +67 -0
  53. package/src/components/custom/ActiveWork/FileActivityRow.tsx +130 -0
  54. package/src/components/custom/ActiveWork/FileHistoryExplorer.stories.tsx +84 -0
  55. package/src/components/custom/ActiveWork/FileHistoryExplorer.test.tsx +99 -0
  56. package/src/components/custom/ActiveWork/FileHistoryExplorer.tsx +138 -0
  57. package/src/components/custom/ActiveWork/FilePathLabel.stories.tsx +39 -0
  58. package/src/components/custom/ActiveWork/FilePathLabel.test.tsx +53 -0
  59. package/src/components/custom/ActiveWork/FilePathLabel.tsx +63 -0
  60. package/src/components/custom/ActiveWork/InitiativeBrief.stories.tsx +50 -0
  61. package/src/components/custom/ActiveWork/InitiativeBrief.test.tsx +62 -0
  62. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +181 -0
  63. package/src/components/custom/ActiveWork/InitiativeCard.stories.tsx +73 -0
  64. package/src/components/custom/ActiveWork/InitiativeCard.test.tsx +68 -0
  65. package/src/components/custom/ActiveWork/InitiativeCard.tsx +136 -0
  66. package/src/components/custom/ActiveWork/InitiativeHeader.stories.tsx +59 -0
  67. package/src/components/custom/ActiveWork/InitiativeHeader.test.tsx +40 -0
  68. package/src/components/custom/ActiveWork/InitiativeHeader.tsx +73 -0
  69. package/src/components/custom/ActiveWork/InitiativeReader.stories.tsx +145 -0
  70. package/src/components/custom/ActiveWork/OpenLoops.stories.tsx +58 -0
  71. package/src/components/custom/ActiveWork/OpenLoops.test.tsx +61 -0
  72. package/src/components/custom/ActiveWork/OpenLoops.tsx +107 -0
  73. package/src/components/custom/ActiveWork/PortfolioOverview.stories.tsx +101 -0
  74. package/src/components/custom/ActiveWork/PortfolioOverview.test.tsx +81 -0
  75. package/src/components/custom/ActiveWork/PortfolioOverview.tsx +83 -0
  76. package/src/components/custom/ActiveWork/README.md +305 -0
  77. package/src/components/custom/ActiveWork/SessionDetail.stories.tsx +71 -0
  78. package/src/components/custom/ActiveWork/SessionDetail.test.tsx +84 -0
  79. package/src/components/custom/ActiveWork/SessionDetail.tsx +180 -0
  80. package/src/components/custom/ActiveWork/SessionList.stories.tsx +69 -0
  81. package/src/components/custom/ActiveWork/SessionList.test.tsx +78 -0
  82. package/src/components/custom/ActiveWork/SessionList.tsx +103 -0
  83. package/src/components/custom/ActiveWork/SessionListItem.stories.tsx +64 -0
  84. package/src/components/custom/ActiveWork/SessionListItem.test.tsx +62 -0
  85. package/src/components/custom/ActiveWork/SessionListItem.tsx +152 -0
  86. package/src/components/custom/ActiveWork/SessionReader.stories.tsx +113 -0
  87. package/src/components/custom/ActiveWork/SeverityLabel.stories.tsx +51 -0
  88. package/src/components/custom/ActiveWork/SeverityLabel.test.tsx +68 -0
  89. package/src/components/custom/ActiveWork/SeverityLabel.tsx +97 -0
  90. package/src/components/custom/ActiveWork/TaskRow.stories.tsx +63 -0
  91. package/src/components/custom/ActiveWork/TaskRow.test.tsx +112 -0
  92. package/src/components/custom/ActiveWork/TaskRow.tsx +176 -0
  93. package/src/components/custom/ActiveWork/TaskTable.stories.tsx +123 -0
  94. package/src/components/custom/ActiveWork/TaskTable.test.tsx +233 -0
  95. package/src/components/custom/ActiveWork/TaskTable.tsx +321 -0
  96. package/src/components/custom/ActiveWork/active-work-depth.test.tsx +102 -0
  97. package/src/components/custom/ActiveWork/file-history-fixture.ts +91 -0
  98. package/src/components/custom/ActiveWork/format-time.test.ts +23 -0
  99. package/src/components/custom/ActiveWork/format-time.ts +32 -0
  100. package/src/components/custom/ActiveWork/index.ts +19 -0
  101. package/src/components/custom/ActiveWork/initiative-fixture.ts +104 -0
  102. package/src/components/custom/ActiveWork/session-fixture.ts +267 -0
  103. package/src/components/custom/ActiveWork/session-linkers.ts +56 -0
  104. package/src/components/custom/ActiveWork/task-list-fixture.ts +97 -0
  105. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +4 -3
  106. package/src/components/custom/DateTime/DateTime.stories.tsx +2 -1
  107. package/src/components/custom/DateTime/DateTime.test.tsx +3 -1
  108. package/src/components/custom/EmptyState/EmptyState.stories.tsx +22 -9
  109. package/src/components/custom/EmptyState/EmptyState.test.tsx +7 -13
  110. package/src/components/custom/EmptyState/EmptyState.tsx +4 -18
  111. package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +4 -3
  112. package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
  113. package/src/components/custom/Fatigue/GhostSpark.stories.tsx +4 -3
  114. package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +2 -2
  115. package/src/components/custom/Fatigue/LiveFatigueCard.tsx +8 -4
  116. package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +116 -6
  117. package/src/components/custom/Fatigue/LiveFatiguePanel.test.tsx +134 -0
  118. package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +54 -83
  119. package/src/components/custom/Fatigue/README.md +40 -3
  120. package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
  121. package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +2 -1
  122. package/src/components/custom/Fatigue/VelocityHero.stories.tsx +4 -3
  123. package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
  124. package/src/components/custom/Fatigue/fatigue-mock.test.ts +0 -5
  125. package/src/components/custom/Fatigue/fatigue-mock.ts +2 -10
  126. package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -0
  127. package/src/components/custom/Fatigue/index.ts +17 -5
  128. package/src/components/custom/Fatigue/panel-layout.test.ts +149 -0
  129. package/src/components/custom/Fatigue/panel-layout.ts +185 -0
  130. package/src/components/custom/Gauge/Gauge.stories.tsx +2 -2
  131. package/src/components/custom/Gauge/Gauge.test.tsx +1 -1
  132. package/src/components/custom/Gauge/Gauge.tsx +4 -1
  133. package/src/components/custom/Metric/Metric.stories.tsx +10 -1
  134. package/src/components/custom/Metric/Metric.test.tsx +1 -3
  135. package/src/components/custom/Metric/Metric.tsx +5 -20
  136. package/src/components/custom/Prose/MarkdownProse.stories.tsx +88 -0
  137. package/src/components/custom/Prose/MarkdownProse.test.tsx +87 -0
  138. package/src/components/custom/Prose/MarkdownProse.tsx +201 -0
  139. package/src/components/custom/Prose/README.md +58 -0
  140. package/src/components/custom/Prose/index.ts +1 -0
  141. package/src/components/custom/Scatter/Scatter.stories.tsx +46 -8
  142. package/src/components/custom/Scatter/Scatter.test.tsx +2 -2
  143. package/src/components/custom/Scatter/Scatter.tsx +116 -14
  144. package/src/components/custom/Sidebar/Sidebar.stories.tsx +40 -7
  145. package/src/components/custom/Sidebar/Sidebar.test.tsx +5 -1
  146. package/src/components/custom/Sidebar/Sidebar.tsx +16 -33
  147. package/src/components/custom/Table/Table.stories.tsx +98 -15
  148. package/src/components/custom/Table/Table.test.tsx +273 -86
  149. package/src/components/custom/Table/Table.tsx +159 -67
  150. package/src/components/custom/Table/column-fit.test.ts +75 -0
  151. package/src/components/custom/Table/column-fit.ts +116 -0
  152. package/src/components/custom/Table/index.ts +9 -0
  153. package/src/components/custom/TimerReadout/TimerReadout.stories.tsx +8 -6
  154. package/src/components/custom/Treemap/Treemap.stories.tsx +2 -2
  155. package/src/components/custom/Treemap/Treemap.test.tsx +1 -1
  156. package/src/components/custom/Treemap/Treemap.tsx +7 -2
  157. package/src/components/custom/Typography/Typography.stories.tsx +28 -1
  158. package/src/components/custom/Typography/Typography.test.tsx +34 -0
  159. package/src/components/custom/Typography/Typography.tsx +6 -0
  160. package/src/components/custom/Typography/index.ts +1 -8
  161. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +9 -1
  162. package/src/components/custom/Workout/BaseBadge.stories.tsx +19 -7
  163. package/src/components/custom/Workout/BaseBadge.test.tsx +31 -11
  164. package/src/components/custom/Workout/BaseBadge.tsx +3 -0
  165. package/src/components/custom/Workout/BodyMap.stories.tsx +4 -1
  166. package/src/components/custom/Workout/BodyMap.tsx +3 -1
  167. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +16 -4
  168. package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +11 -15
  169. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +45 -9
  170. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +10 -8
  171. package/src/components/custom/Workout/CapacityBandChart.test.tsx +24 -51
  172. package/src/components/custom/Workout/CapacityBandChart.tsx +139 -108
  173. package/src/components/custom/Workout/DeviationBar.stories.tsx +1 -1
  174. package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
  175. package/src/components/custom/Workout/DeviationBar.tsx +21 -16
  176. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +182 -0
  177. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +3 -2
  178. package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -7
  179. package/src/components/custom/Workout/ExerciseCard.test.tsx +13 -3
  180. package/src/components/custom/Workout/ExerciseCard.tsx +42 -51
  181. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +7 -7
  182. package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +9 -1
  183. package/src/components/custom/Workout/ExerciseHeading.stories.tsx +8 -8
  184. package/src/components/custom/Workout/ExerciseHeading.tsx +8 -5
  185. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -4
  186. package/src/components/custom/Workout/ExerciseIndicator.test.tsx +27 -11
  187. package/src/components/custom/Workout/ExerciseIndicator.tsx +14 -15
  188. package/src/components/custom/Workout/FatigueMeter.stories.tsx +5 -5
  189. package/src/components/custom/Workout/InputBar.stories.tsx +4 -5
  190. package/src/components/custom/Workout/IntensityBar.stories.tsx +1 -1
  191. package/src/components/custom/Workout/IntensityBar.test.tsx +5 -1
  192. package/src/components/custom/Workout/IntensityBar.tsx +4 -2
  193. package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +5 -4
  194. package/src/components/custom/Workout/MesoCard.stories.tsx +4 -1
  195. package/src/components/custom/Workout/MesoCard.test.tsx +7 -18
  196. package/src/components/custom/Workout/MesoCard.tsx +12 -9
  197. package/src/components/custom/Workout/MesoProgressBar.stories.tsx +1 -1
  198. package/src/components/custom/Workout/MesoProgressBar.test.tsx +18 -11
  199. package/src/components/custom/Workout/MesoProgressBar.tsx +15 -13
  200. package/src/components/custom/Workout/MesoStatusCard.stories.tsx +2 -1
  201. package/src/components/custom/Workout/MesoStatusCard.test.tsx +14 -44
  202. package/src/components/custom/Workout/MesoStatusCard.tsx +9 -23
  203. package/src/components/custom/Workout/MetricTiles.stories.tsx +3 -2
  204. package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +2 -8
  205. package/src/components/custom/Workout/MuscleGroupChip.test.tsx +23 -31
  206. package/src/components/custom/Workout/MuscleGroupChip.tsx +28 -67
  207. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +1 -1
  208. package/src/components/custom/Workout/PlaceholderStrip.test.tsx +17 -10
  209. package/src/components/custom/Workout/PlaceholderStrip.tsx +9 -8
  210. package/src/components/custom/Workout/PrBadge.stories.tsx +2 -1
  211. package/src/components/custom/Workout/PrBadge.test.tsx +3 -9
  212. package/src/components/custom/Workout/PrBadge.tsx +13 -17
  213. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +4 -1
  214. package/src/components/custom/Workout/PrHistoryModal.test.tsx +13 -36
  215. package/src/components/custom/Workout/PrHistoryModal.tsx +8 -4
  216. package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +6 -1
  217. package/src/components/custom/Workout/README.md +11 -6
  218. package/src/components/custom/Workout/ReadinessCheck.stories.tsx +3 -2
  219. package/src/components/custom/Workout/ReadinessCheck.test.tsx +12 -18
  220. package/src/components/custom/Workout/ReadinessCheck.tsx +19 -3
  221. package/src/components/custom/Workout/RestTimer.stories.tsx +3 -2
  222. package/src/components/custom/Workout/ScheduleTiles.stories.tsx +3 -2
  223. package/src/components/custom/Workout/ScheduleTiles.test.tsx +3 -4
  224. package/src/components/custom/Workout/ScheduleTiles.tsx +4 -7
  225. package/src/components/custom/Workout/SegmentedBar.stories.tsx +5 -5
  226. package/src/components/custom/Workout/SegmentedBar.tsx +6 -2
  227. package/src/components/custom/Workout/SegmentedProgressBar.stories.tsx +5 -5
  228. package/src/components/custom/Workout/SessionHeader.stories.tsx +6 -6
  229. package/src/components/custom/Workout/SessionHeader.tsx +12 -12
  230. package/src/components/custom/Workout/SessionRail.stories.tsx +12 -11
  231. package/src/components/custom/Workout/SessionRail.tsx +7 -9
  232. package/src/components/custom/Workout/SetBar.stories.tsx +10 -6
  233. package/src/components/custom/Workout/SetBar.test.tsx +49 -4
  234. package/src/components/custom/Workout/SetBar.tsx +38 -12
  235. package/src/components/custom/Workout/SetRow.stories.tsx +3 -2
  236. package/src/components/custom/Workout/SetRow.test.tsx +16 -2
  237. package/src/components/custom/Workout/SetRow.tsx +61 -23
  238. package/src/components/custom/Workout/SetStrip.stories.tsx +60 -5
  239. package/src/components/custom/Workout/SetStrip.test.tsx +25 -0
  240. package/src/components/custom/Workout/SetStrip.tsx +24 -4
  241. package/src/components/custom/Workout/SetTableHeader.stories.tsx +6 -6
  242. package/src/components/custom/Workout/SetTableHeader.tsx +9 -12
  243. package/src/components/custom/Workout/SetsRepsLoad.stories.tsx +7 -7
  244. package/src/components/custom/Workout/SetsRepsLoad.tsx +3 -5
  245. package/src/components/custom/Workout/Sparkline.stories.tsx +1 -1
  246. package/src/components/custom/Workout/Sparkline.test.tsx +37 -19
  247. package/src/components/custom/Workout/Sparkline.tsx +14 -18
  248. package/src/components/custom/Workout/StatusDot.stories.tsx +1 -1
  249. package/src/components/custom/Workout/StatusDot.test.tsx +44 -5
  250. package/src/components/custom/Workout/StatusDot.tsx +93 -63
  251. package/src/components/custom/Workout/StatusPill.stories.tsx +1 -1
  252. package/src/components/custom/Workout/StatusPill.test.tsx +6 -15
  253. package/src/components/custom/Workout/StatusPill.tsx +27 -38
  254. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +9 -3
  255. package/src/components/custom/Workout/StrengthTrendChart.test.tsx +15 -37
  256. package/src/components/custom/Workout/StrengthTrendChart.tsx +31 -30
  257. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +27 -64
  258. package/src/components/custom/Workout/SupersetWrapper.test.tsx +16 -14
  259. package/src/components/custom/Workout/SupersetWrapper.tsx +17 -21
  260. package/src/components/custom/Workout/TempoDisplay.stories.tsx +22 -7
  261. package/src/components/custom/Workout/TempoDisplay.test.tsx +18 -4
  262. package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +9 -1
  263. package/src/components/custom/Workout/VelocityStrip.compact.stories.tsx +4 -1
  264. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +7 -4
  265. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +4 -1
  266. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +4 -1
  267. package/src/components/custom/Workout/VelocityStrip.stories.tsx +12 -6
  268. package/src/components/custom/Workout/VelocityStrip.tsx +3 -0
  269. package/src/components/custom/Workout/VolumeLandmarkBar.stories.tsx +6 -7
  270. package/src/components/custom/Workout/VolumeLandmarkBar.tsx +15 -6
  271. package/src/components/custom/Workout/WeekRow.stories.tsx +4 -1
  272. package/src/components/custom/Workout/WeekRow.test.tsx +18 -15
  273. package/src/components/custom/Workout/WeekRow.tsx +23 -16
  274. package/src/components/custom/Workout/WeightBadge.stories.tsx +2 -1
  275. package/src/components/custom/Workout/WeightBadge.test.tsx +50 -19
  276. package/src/components/custom/Workout/WeightBadge.tsx +28 -27
  277. package/src/components/custom/Workout/WorkoutCard.stories.tsx +6 -1
  278. package/src/components/custom/Workout/WorkoutCard.test.tsx +18 -0
  279. package/src/components/custom/Workout/WorkoutCard.tsx +35 -57
  280. package/src/components/custom/Workout/WorkoutPill.stories.tsx +5 -2
  281. package/src/components/custom/Workout/WorkoutPill.test.tsx +39 -7
  282. package/src/components/custom/Workout/WorkoutPill.tsx +88 -57
  283. package/src/components/custom/Workout/ZoneTrack.stories.tsx +7 -6
  284. package/src/components/custom/Workout/ZoneTrack.tsx +10 -9
  285. package/src/components/custom/Workout/index.ts +5 -0
  286. package/src/components/custom/Workout/metricText.stories.tsx +6 -5
  287. package/src/components/custom/Workout/metricText.tsx +4 -0
  288. package/src/components/custom/Workout/setHeadingKit.tsx +207 -26
  289. package/src/components/custom/charts/README.md +61 -0
  290. package/src/components/custom/charts/SetBarChart.test.tsx +30 -1
  291. package/src/components/custom/charts/SetBarChart.tsx +42 -4
  292. package/src/components/custom/charts/SparkBars.stories.tsx +55 -0
  293. package/src/components/custom/charts/SparkBars.test.tsx +69 -0
  294. package/src/components/custom/charts/SparkBars.tsx +85 -0
  295. package/src/components/custom/charts/index.ts +3 -0
  296. package/src/components/custom/index.ts +3 -0
  297. package/src/components/custom/stepper/Stepper.stories.tsx +13 -10
  298. package/src/components/custom/stepper/Stepper.test.tsx +1 -7
  299. package/src/components/custom/stepper/Stepper.tsx +13 -28
  300. package/src/components/icons/icons.stories.tsx +1 -1
  301. package/src/components/icons/icons.tsx +51 -0
  302. package/src/components/shell/AppShell.stories.tsx +150 -0
  303. package/src/components/shell/AppShell.test.tsx +70 -0
  304. package/src/components/shell/AppShell.tsx +90 -0
  305. package/src/components/shell/BrandLockup.stories.tsx +66 -5
  306. package/src/components/shell/BrandLockup.test.tsx +34 -1
  307. package/src/components/shell/BrandLockup.tsx +27 -11
  308. package/src/components/shell/NavItem.stories.tsx +4 -4
  309. package/src/components/shell/NavItem.test.tsx +17 -0
  310. package/src/components/shell/NavItem.tsx +17 -5
  311. package/src/components/shell/README.md +89 -26
  312. package/src/components/shell/SideNav.stories.tsx +38 -7
  313. package/src/components/shell/SideNav.test.tsx +16 -8
  314. package/src/components/shell/SideNav.tsx +17 -17
  315. package/src/components/shell/TopBar.stories.tsx +44 -19
  316. package/src/components/shell/TopBar.test.tsx +34 -12
  317. package/src/components/shell/TopBar.tsx +61 -40
  318. package/src/components/shell/brands.tsx +66 -0
  319. package/src/components/shell/index.ts +9 -8
  320. package/src/components/shell/{DeviceIndicator.stories.tsx → workout/DeviceIndicator.stories.tsx} +2 -2
  321. package/src/components/shell/{DeviceIndicator.tsx → workout/DeviceIndicator.tsx} +2 -2
  322. package/src/components/shell/{DeviceMenu.stories.tsx → workout/DeviceMenu.stories.tsx} +2 -2
  323. package/src/components/shell/{DeviceMenu.tsx → workout/DeviceMenu.tsx} +3 -3
  324. package/src/components/shell/{DeviceRow.stories.tsx → workout/DeviceRow.stories.tsx} +2 -2
  325. package/src/components/shell/{DeviceRow.tsx → workout/DeviceRow.tsx} +3 -3
  326. package/src/components/shell/{SessionStatePill.stories.tsx → workout/SessionStatePill.stories.tsx} +2 -2
  327. package/src/components/shell/{SessionStatePill.tsx → workout/SessionStatePill.tsx} +5 -3
  328. package/src/components/shell/workout/WorkoutShell.stories.tsx +71 -0
  329. package/src/components/shell/{DashboardShell.test.tsx → workout/WorkoutShell.test.tsx} +19 -8
  330. package/src/components/shell/workout/WorkoutShell.tsx +77 -0
  331. package/src/components/shell/workout/WorkoutTopBar.stories.tsx +58 -0
  332. package/src/components/shell/workout/WorkoutTopBar.test.tsx +39 -0
  333. package/src/components/shell/workout/WorkoutTopBar.tsx +52 -0
  334. package/src/components/shell/workout/index.ts +9 -0
  335. package/src/components/shell/workout/workoutNavItems.tsx +16 -0
  336. package/src/components/ui/README.md +110 -0
  337. package/src/components/ui/alert/Alert.stories.tsx +1 -1
  338. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +11 -13
  339. package/src/components/ui/autocomplete/Autocomplete.test.tsx +7 -25
  340. package/src/components/ui/autocomplete/Autocomplete.tsx +21 -28
  341. package/src/components/ui/avatar/Avatar.stories.tsx +1 -1
  342. package/src/components/ui/avatar/Avatar.tsx +2 -10
  343. package/src/components/ui/badge/Badge.stories.tsx +83 -28
  344. package/src/components/ui/badge/Badge.test.tsx +19 -3
  345. package/src/components/ui/badge/Badge.tsx +48 -55
  346. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  347. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +4 -16
  348. package/src/components/ui/button/Button.stories.tsx +46 -12
  349. package/src/components/ui/button/Button.tsx +32 -25
  350. package/src/components/ui/card/Card.stories.tsx +90 -94
  351. package/src/components/ui/card/Card.test.tsx +80 -6
  352. package/src/components/ui/card/Card.tsx +150 -203
  353. package/src/components/ui/checkbox/Checkbox.stories.tsx +1 -1
  354. package/src/components/ui/checkbox/Checkbox.tsx +6 -26
  355. package/src/components/ui/chip/Chip.stories.tsx +37 -13
  356. package/src/components/ui/chip/Chip.test.tsx +10 -6
  357. package/src/components/ui/chip/Chip.tsx +76 -91
  358. package/src/components/ui/collapse/Collapse.stories.tsx +23 -23
  359. package/src/components/ui/collapse/Collapse.tsx +6 -11
  360. package/src/components/ui/data-row/DataRow.stories.tsx +9 -2
  361. package/src/components/ui/data-row/DataRow.test.tsx +5 -15
  362. package/src/components/ui/data-row/DataRow.tsx +18 -7
  363. package/src/components/ui/divider/Divider.stories.tsx +1 -1
  364. package/src/components/ui/divider/Divider.tsx +1 -5
  365. package/src/components/ui/drawer/Drawer.stories.tsx +5 -16
  366. package/src/components/ui/drawer/Drawer.tsx +13 -30
  367. package/src/components/ui/floating-overlay-depth.test.tsx +143 -0
  368. package/src/components/ui/form-field/FormField.stories.tsx +6 -20
  369. package/src/components/ui/form-field/FormField.test.tsx +1 -5
  370. package/src/components/ui/form-field/FormField.tsx +10 -46
  371. package/src/components/ui/help-tip/HelpTip.stories.tsx +8 -19
  372. package/src/components/ui/help-tip/HelpTip.test.tsx +1 -3
  373. package/src/components/ui/help-tip/HelpTip.tsx +21 -22
  374. package/src/components/ui/help-tip/index.ts +7 -1
  375. package/src/components/ui/icon-box/IconBox.stories.tsx +1 -1
  376. package/src/components/ui/icon-box/IconBox.test.tsx +1 -7
  377. package/src/components/ui/icon-box/IconBox.tsx +2 -12
  378. package/src/components/ui/indicator/Indicator.stories.tsx +1 -1
  379. package/src/components/ui/indicator/Indicator.tsx +20 -14
  380. package/src/components/ui/input/Input.stories.tsx +3 -11
  381. package/src/components/ui/input/Input.test.tsx +2 -10
  382. package/src/components/ui/input/Input.tsx +10 -25
  383. package/src/components/ui/input/index.ts +7 -1
  384. package/src/components/ui/link/Link.stories.tsx +19 -7
  385. package/src/components/ui/list-item/ListItem.stories.tsx +11 -2
  386. package/src/components/ui/list-item/ListItem.tsx +2 -9
  387. package/src/components/ui/menu/Menu.stories.tsx +21 -15
  388. package/src/components/ui/menu/Menu.test.tsx +2 -11
  389. package/src/components/ui/menu/Menu.tsx +12 -14
  390. package/src/components/ui/menu/index.ts +8 -1
  391. package/src/components/ui/modal/Modal.stories.tsx +2 -2
  392. package/src/components/ui/modal/Modal.tsx +29 -19
  393. package/src/components/ui/pill/Pill.stories.tsx +58 -23
  394. package/src/components/ui/pill/Pill.test.tsx +101 -3
  395. package/src/components/ui/pill/Pill.tsx +164 -49
  396. package/src/components/ui/pill/index.ts +1 -1
  397. package/src/components/ui/popover/Popover.stories.tsx +5 -7
  398. package/src/components/ui/popover/Popover.test.tsx +1 -6
  399. package/src/components/ui/popover/Popover.tsx +20 -15
  400. package/src/components/ui/popover/index.ts +7 -1
  401. package/src/components/ui/progress/Progress.stories.tsx +3 -12
  402. package/src/components/ui/progress/Progress.test.tsx +1 -5
  403. package/src/components/ui/radio/Radio.stories.tsx +42 -21
  404. package/src/components/ui/radio/Radio.test.tsx +6 -2
  405. package/src/components/ui/radio/Radio.tsx +23 -22
  406. package/src/components/ui/section/Section.stories.tsx +5 -14
  407. package/src/components/ui/section/Section.test.tsx +1 -6
  408. package/src/components/ui/section/Section.tsx +1 -3
  409. package/src/components/ui/section/index.ts +1 -5
  410. package/src/components/ui/select/Select.stories.tsx +3 -13
  411. package/src/components/ui/select/Select.test.tsx +2 -11
  412. package/src/components/ui/select/Select.tsx +14 -14
  413. package/src/components/ui/skeleton/Skeleton.stories.tsx +3 -11
  414. package/src/components/ui/skeleton/Skeleton.tsx +3 -14
  415. package/src/components/ui/skeleton/index.ts +1 -7
  416. package/src/components/ui/spinner/Spinner.stories.tsx +1 -1
  417. package/src/components/ui/spinner/Spinner.tsx +1 -4
  418. package/src/components/ui/stack/Stack.stories.tsx +1 -1
  419. package/src/components/ui/stack/Stack.tsx +1 -1
  420. package/src/components/ui/surface/Surface.stories.tsx +17 -5
  421. package/src/components/ui/surface/Surface.test.tsx +88 -28
  422. package/src/components/ui/surface/Surface.tsx +37 -87
  423. package/src/components/ui/surface/SurfaceContext.ts +22 -62
  424. package/src/components/ui/surface/index.ts +8 -0
  425. package/src/components/ui/surface/resolveSurface.ts +98 -0
  426. package/src/components/ui/surface/surface.contract.test.ts +6 -1
  427. package/src/components/ui/switch/Switch.stories.tsx +1 -1
  428. package/src/components/ui/switch/Switch.tsx +7 -6
  429. package/src/components/ui/tabs/Tabs.stories.tsx +1 -1
  430. package/src/components/ui/tabs/Tabs.tsx +7 -22
  431. package/src/components/ui/tabs/index.ts +1 -7
  432. package/src/components/ui/tile/Tile.stories.tsx +1 -1
  433. package/src/components/ui/tile/Tile.tsx +3 -0
  434. package/src/components/ui/toast/Toast.stories.tsx +1 -1
  435. package/src/components/ui/toast/Toast.test.tsx +2 -8
  436. package/src/components/ui/toast/Toast.tsx +43 -53
  437. package/src/components/ui/toast/index.ts +0 -1
  438. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +92 -71
  439. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +3 -18
  440. package/src/components/ui/toolbar-button/ToolbarButton.tsx +25 -43
  441. package/src/components/ui/tooltip/Tooltip.stories.tsx +12 -3
  442. package/src/components/ui/tooltip/Tooltip.test.tsx +21 -3
  443. package/src/components/ui/tooltip/Tooltip.tsx +55 -36
  444. package/src/components/ui/tooltip/index.ts +1 -1
  445. package/src/components/ui/trigger/TriggerSurface.test.tsx +215 -0
  446. package/src/components/ui/trigger/TriggerSurface.tsx +81 -0
  447. package/src/components/ui/trigger/index.ts +2 -0
  448. package/src/stories/PresetShowcase.stories.tsx +100 -47
  449. package/src/test/no-device-internals.test.ts +3 -3
  450. package/src/theme/ChoosingTokens.stories.tsx +48 -0
  451. package/src/theme/ColorPalettes.stories.tsx +62 -45
  452. package/src/theme/ColorPrimitives.stories.tsx +35 -35
  453. package/src/theme/Depth.stories.tsx +132 -65
  454. package/src/theme/DepthCalibration.stories.tsx +23 -21
  455. package/src/theme/ThemeProvider.test.tsx +4 -4
  456. package/src/theme/barrel.ts +7 -6
  457. package/src/theme/color-stories.coverage.test.ts +1 -1
  458. package/src/theme/color-utils.ts +41 -29
  459. package/src/theme/config.completeness.test.ts +1 -1
  460. package/src/theme/config.ts +28 -0
  461. package/src/theme/elevation.test.ts +82 -56
  462. package/src/theme/elevation.ts +77 -561
  463. package/src/theme/global.css +30 -2
  464. package/src/theme/grey-ramp.test.ts +24 -8
  465. package/src/theme/lift.test.ts +39 -0
  466. package/src/theme/lift.ts +102 -0
  467. package/src/theme/materials.test.ts +5 -2
  468. package/src/theme/materials.ts +38 -4
  469. package/src/theme/native-theming-guard.test.ts +5 -5
  470. package/src/theme/presets/apply.ts +4 -0
  471. package/src/theme/presets/audiobook.ts +5 -2
  472. package/src/theme/presets/default.ts +4 -0
  473. package/src/theme/presets/index.ts +14 -1
  474. package/src/theme/presets/types.ts +1 -1
  475. package/src/theme/semantic-contrast.test.ts +13 -4
  476. package/src/theme/status-distinctness.test.ts +66 -0
  477. package/src/theme/surface-planes.test.ts +37 -0
  478. package/src/theme/surface-planes.ts +68 -0
  479. package/src/theme/token-export-guard.test.ts +1 -3
  480. package/src/theme/tokens/primitives.test.ts +15 -7
  481. package/src/theme/tokens/primitives.ts +65 -33
  482. package/src/theme/tokens/semantic.ts +71 -37
  483. package/src/theme/wash-ladder.test.ts +58 -0
  484. package/src/theme/workout-tokens.ts +1 -7
  485. package/src/types/jest-axe.d.ts +2 -6
  486. package/src/types/raw-md.d.ts +5 -0
  487. package/src/utils/colors.ts +4 -4
  488. package/src/utils/form.ts +1 -3
  489. package/src/utils/index.ts +2 -7
  490. package/src/utils/number-format.ts +17 -0
  491. package/src/utils/workout-format.ts +15 -6
  492. package/src/web-jsx/jsx-runtime.ts +1 -5
  493. package/tailwind.config.js +5 -0
  494. package/src/components/custom/Workout/ExerciseCardUnified.stories.tsx +0 -151
  495. package/src/components/custom/Workout/RowInventory.stories.tsx +0 -408
  496. package/src/components/custom/Workout/S3FullRail.stories.tsx +0 -46
  497. package/src/components/custom/Workout/S3SessionPace.stories.tsx +0 -95
  498. package/src/components/custom/Workout/S3SessionRailHeading.stories.tsx +0 -61
  499. package/src/components/custom/Workout/S3SetTypes.stories.tsx +0 -176
  500. package/src/components/custom/Workout/S3WorkoutExpansion.stories.tsx +0 -240
  501. package/src/components/shell/DashboardShell.stories.tsx +0 -65
  502. package/src/components/shell/DashboardShell.tsx +0 -75
  503. package/src/components/shell/SessionRailSpecimen.stories.tsx +0 -753
  504. package/src/theme/elevation.stories.tsx +0 -346
  505. package/src/utils/useTheme.ts +0 -72
  506. /package/src/components/shell/{DeviceIndicator.test.tsx → workout/DeviceIndicator.test.tsx} +0 -0
  507. /package/src/components/shell/{DeviceMenu.test.tsx → workout/DeviceMenu.test.tsx} +0 -0
  508. /package/src/components/shell/{DeviceRow.test.tsx → workout/DeviceRow.test.tsx} +0 -0
  509. /package/src/components/shell/{SessionStatePill.test.tsx → workout/SessionStatePill.test.tsx} +0 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The surface planes: the grey-ramp steps a container can sit on, darkest first.
3
+ *
4
+ * Every background a `<Surface>` or `<Card>` paints resolves through this list,
5
+ * so a container can only ever land ON the ramp. Stepping up (a lifted card) or
6
+ * down (a pressed well) is indexing `PLANE_ORDER`, clamped at both ends.
7
+ *
8
+ * `frame` is the bezel the ramp sits inside and the floor a pressed surface
9
+ * clamps at. It replaced a separate `inset` level in TD-07.14: at ΔE 1.10 from
10
+ * the frame that level was an imperceptible duplicate.
11
+ */
12
+ import { getSemanticColors, type ThemeMode } from './tokens/semantic'
13
+
14
+ type ColorToken = keyof ReturnType<typeof getSemanticColors>
15
+
16
+ export type SurfaceLevel = 'frame' | 'background' | 'base' | 'elevated' | 'raised' | 'overlay'
17
+
18
+ // Which semantic token backs each plane. Values live in `semantic.ts` and stay
19
+ // in sync with the grey ramp; this map never carries literal hexes.
20
+ export const SURFACE_LEVEL_TOKEN = {
21
+ frame: 'background-frame',
22
+ background: 'background-base',
23
+ base: 'surface-base',
24
+ elevated: 'surface-elevated',
25
+ raised: 'surface-raised',
26
+ overlay: 'surface-overlay',
27
+ } as const satisfies Record<SurfaceLevel, ColorToken>
28
+
29
+ /** Darkest → lightest. Kept in lockstep with the grey ramp and surface.contract.test. */
30
+ export const PLANE_ORDER = [
31
+ 'frame',
32
+ 'background',
33
+ 'base',
34
+ 'elevated',
35
+ 'raised',
36
+ 'overlay',
37
+ ] as const satisfies readonly SurfaceLevel[]
38
+
39
+ /** The background hex for a plane under a theme mode (literal hex, never a var()). */
40
+ export function surfaceBackground(level: SurfaceLevel, mode: ThemeMode): string {
41
+ return getSemanticColors(mode)[SURFACE_LEVEL_TOKEN[level]]
42
+ }
43
+
44
+ function stepPlane(from: SurfaceLevel, delta: number): SurfaceLevel {
45
+ const index = PLANE_ORDER.indexOf(from)
46
+ const clamped = Math.min(PLANE_ORDER.length - 1, Math.max(0, index + delta))
47
+ return PLANE_ORDER[clamped]
48
+ }
49
+
50
+ /**
51
+ * The plane a `<Surface pressed>` resolves to: its parent's plane stepped one
52
+ * index DOWN the ramp, clamped at the `frame` floor.
53
+ * overlay→raised, raised→elevated, elevated→base, base→background,
54
+ * background→frame, frame→frame (clamped).
55
+ */
56
+ export function pressedLevel(parent: SurfaceLevel): SurfaceLevel {
57
+ return stepPlane(parent, -1)
58
+ }
59
+
60
+ /**
61
+ * The plane a lifted surface resolves to: its parent's plane stepped `steps`
62
+ * UP the ramp, clamped at `overlay`. The clamp is what makes raised-on-raised
63
+ * unrepresentable: a card inside an overlay stays on the overlay and relies on
64
+ * its lift treatment, never on a lighter grey that is not on the ramp.
65
+ */
66
+ export function raisedLevel(parent: SurfaceLevel, steps: number): SurfaceLevel {
67
+ return stepPlane(parent, Math.max(0, steps))
68
+ }
@@ -82,9 +82,7 @@ describe('token-only subpath export (TP-16)', () => {
82
82
 
83
83
  it('reaches no react / react-native / nativewind module in its import graph', () => {
84
84
  const { bare } = collectGraph(TOKEN_ENTRY)
85
- const leaked = bare.filter((s) =>
86
- FORBIDDEN.some((f) => s === f || s.startsWith(`${f}/`)),
87
- )
85
+ const leaked = bare.filter((s) => FORBIDDEN.some((f) => s === f || s.startsWith(`${f}/`)))
88
86
  expect(leaked).toEqual([])
89
87
  })
90
88
 
@@ -34,15 +34,17 @@ const mul = (M: number[], v: number[]) => [
34
34
  // Machado-2009 dichromacy simulation matrices, severity 1.0. The palette solver
35
35
  // optimizes worst-case deuteranopia/protanopia (red-green is the binding case) —
36
36
  // match that 2-way metric here, not a stricter 3-way including tritan.
37
- const DEUT = [0.367322, 0.860646, -0.227968, 0.280085, 0.672501, 0.047413, -0.01182, 0.04294, 0.968881]
38
- const PROT = [0.152286, 1.052583, -0.204868, 0.114503, 0.786281, 0.099216, -0.003882, -0.048116, 1.051998]
37
+ const DEUT = [
38
+ 0.367322, 0.860646, -0.227968, 0.280085, 0.672501, 0.047413, -0.01182, 0.04294, 0.968881,
39
+ ]
40
+ const PROT = [
41
+ 0.152286, 1.052583, -0.204868, 0.114503, 0.786281, 0.099216, -0.003882, -0.048116, 1.051998,
42
+ ]
39
43
  const dist = (a: number[], b: number[]) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]) * 100
40
44
  // worst-case perceived ΔE across deuteranopia and protanopia
41
45
  const cvdDelta = (x: string, y: string) => {
42
46
  const sim = (M: number[], hex: string) => toOklab(mul(M, hexToRgb(hex).map(lin)))
43
- return Math.min(
44
- ...[DEUT, PROT].map((M) => dist(sim(M, x), sim(M, y))),
45
- )
47
+ return Math.min(...[DEUT, PROT].map((M) => dist(sim(M, x), sim(M, y))))
46
48
  }
47
49
 
48
50
  const HEX6 = /^#[0-9A-F]{6}$/
@@ -115,7 +117,10 @@ describe('categoricalPalette', () => {
115
117
  worst = Math.min(worst, cvdDelta(colors[i], colors[j]))
116
118
  }
117
119
  }
118
- expect(worst, `${variant} min CVD deltaE (first ${CATEGORICAL_CVD_SAFE_MAX})`).toBeGreaterThanOrEqual(8)
120
+ expect(
121
+ worst,
122
+ `${variant} min CVD deltaE (first ${CATEGORICAL_CVD_SAFE_MAX})`
123
+ ).toBeGreaterThanOrEqual(8)
119
124
  }
120
125
  })
121
126
 
@@ -212,7 +217,10 @@ describe('sequentialEffort', () => {
212
217
 
213
218
  it('keeps adjacent stops distinguishable', () => {
214
219
  for (let i = 0; i < sequentialEffort.length - 1; i++) {
215
- expect(cvdDelta(sequentialEffort[i], sequentialEffort[i + 1]), `stop ${i}->${i + 1}`).toBeGreaterThanOrEqual(4.5)
220
+ expect(
221
+ cvdDelta(sequentialEffort[i], sequentialEffort[i + 1]),
222
+ `stop ${i}->${i + 1}`
223
+ ).toBeGreaterThanOrEqual(4.5)
216
224
  }
217
225
  })
218
226
 
@@ -19,6 +19,18 @@ export const primitiveColors = {
19
19
  transparent: 'transparent',
20
20
  } as const
21
21
 
22
+ /**
23
+ * The vivid alert red: the one chromatic anchor outside the OKLCH ramps.
24
+ *
25
+ * It survived the TD-07.14 fold of the old `redVivid` scale only as the
26
+ * `-rgb`, `-subtle` and glow encodings of `status-error-vivid`, while the token
27
+ * itself was pinned to `red[600]` and collapsed onto `status-error`. Owning it
28
+ * here means the semantic layer references a primitive instead of carrying a
29
+ * literal. The red ramp has no step this saturated, and adding one means
30
+ * regenerating the ramp, so it stays a pin.
31
+ */
32
+ export const alertRedVivid = '#FF4757' as const
33
+
22
34
  /**
23
35
  * Unified warm-neutral grey ramp (TD-07.14)
24
36
  *
@@ -244,7 +256,10 @@ export type CategoricalVariant = keyof typeof categoricalPalette
244
256
  * @param variant - `default` (black-text / light surfaces) or `dark` (white text)
245
257
  * @returns The hex color string
246
258
  */
247
- export function getCategoricalColor(index: number, variant: CategoricalVariant = 'default'): string {
259
+ export function getCategoricalColor(
260
+ index: number,
261
+ variant: CategoricalVariant = 'default'
262
+ ): string {
248
263
  const palette = categoricalPalette[variant]
249
264
  index = Math.max(0, Math.floor(index))
250
265
  return palette[index % palette.length]
@@ -387,21 +402,38 @@ export function getDiscreteRainbowColor(index: number, size: number): string {
387
402
 
388
403
  export const primitiveTypography = {
389
404
  fontFamilies: {
390
- sans: ['Inter', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Helvetica', 'Arial', 'sans-serif'],
405
+ sans: [
406
+ 'Inter',
407
+ '-apple-system',
408
+ 'BlinkMacSystemFont',
409
+ 'Segoe UI',
410
+ 'Helvetica',
411
+ 'Arial',
412
+ 'sans-serif',
413
+ ],
391
414
  body: ['Nunito Sans', 'sans-serif'],
392
415
  heading: ['Space Grotesk', 'sans-serif'],
393
- mono: ['SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', 'monospace'],
416
+ mono: [
417
+ 'SF Mono',
418
+ 'Monaco',
419
+ 'Inconsolata',
420
+ 'Fira Mono',
421
+ 'Droid Sans Mono',
422
+ 'Source Code Pro',
423
+ 'monospace',
424
+ ],
394
425
  },
395
426
  fontSizes: {
396
- xs: '0.75rem', // 12px
397
- sm: '0.875rem', // 14px
398
- base: '1rem', // 16px
399
- lg: '1.125rem', // 18px
400
- xl: '1.5rem', // 24px
401
- '2xl': '2rem', // 32px
427
+ '2xs': '0.625rem', // 10px
428
+ xs: '0.75rem', // 12px
429
+ sm: '0.875rem', // 14px
430
+ base: '1rem', // 16px
431
+ lg: '1.125rem', // 18px
432
+ xl: '1.5rem', // 24px
433
+ '2xl': '2rem', // 32px
402
434
  '3xl': '2.25rem', // 36px
403
- '4xl': '3rem', // 48px
404
- '5xl': '3.5rem', // 56px
435
+ '4xl': '3rem', // 48px
436
+ '5xl': '3.5rem', // 56px
405
437
  },
406
438
  fontWeights: {
407
439
  normal: 400,
@@ -430,28 +462,28 @@ export const primitiveTypography = {
430
462
  export const primitiveSpacing = {
431
463
  0: '0',
432
464
  px: '1px',
433
- 0.5: '0.125rem', // 2px
434
- 1: '0.25rem', // 4px
435
- 1.5: '0.375rem', // 6px
436
- 2: '0.5rem', // 8px
437
- 2.5: '0.625rem', // 10px
438
- 3: '0.75rem', // 12px
439
- 3.5: '0.875rem', // 14px
440
- 4: '1rem', // 16px
441
- 5: '1.25rem', // 20px
442
- 6: '1.5rem', // 24px
443
- 7: '1.75rem', // 28px
444
- 8: '2rem', // 32px
445
- 9: '2.25rem', // 36px
446
- 10: '2.5rem', // 40px
447
- 11: '2.75rem', // 44px
448
- 12: '3rem', // 48px
449
- 14: '3.5rem', // 56px
450
- 16: '4rem', // 64px
451
- 20: '5rem', // 80px
452
- 24: '6rem', // 96px
453
- 28: '7rem', // 112px
454
- 32: '8rem', // 128px
465
+ 0.5: '0.125rem', // 2px
466
+ 1: '0.25rem', // 4px
467
+ 1.5: '0.375rem', // 6px
468
+ 2: '0.5rem', // 8px
469
+ 2.5: '0.625rem', // 10px
470
+ 3: '0.75rem', // 12px
471
+ 3.5: '0.875rem', // 14px
472
+ 4: '1rem', // 16px
473
+ 5: '1.25rem', // 20px
474
+ 6: '1.5rem', // 24px
475
+ 7: '1.75rem', // 28px
476
+ 8: '2rem', // 32px
477
+ 9: '2.25rem', // 36px
478
+ 10: '2.5rem', // 40px
479
+ 11: '2.75rem', // 44px
480
+ 12: '3rem', // 48px
481
+ 14: '3.5rem', // 56px
482
+ 16: '4rem', // 64px
483
+ 20: '5rem', // 80px
484
+ 24: '6rem', // 96px
485
+ 28: '7rem', // 112px
486
+ 32: '8rem', // 128px
455
487
  } as const
456
488
 
457
489
  export const primitiveBorderRadius = {
@@ -17,7 +17,13 @@
17
17
  * - interactive-* : Hover/focus/active/disabled states
18
18
  */
19
19
 
20
- import { primitiveColors as p, primitiveRamps as ramp, greyRamp, discreteRainbow } from './primitives'
20
+ import {
21
+ primitiveColors as p,
22
+ primitiveRamps as ramp,
23
+ greyRamp,
24
+ discreteRainbow,
25
+ alertRedVivid,
26
+ } from './primitives'
21
27
 
22
28
  /**
23
29
  * GREY MAPPING (TD-07.14) — how the old cool scales resolved onto `greyRamp`.
@@ -53,6 +59,8 @@ export const semanticColorsLight = {
53
59
  'brand-primary-light': ramp.orange[300],
54
60
  'brand-primary-dark': ramp.orange[500],
55
61
  'brand-primary-subtle': 'rgba(255, 121, 0, 0.08)',
62
+ 'brand-primary-muted': 'rgba(255, 121, 0, 0.30)',
63
+ 'brand-primary-strong': 'rgba(255, 121, 0, 0.50)',
56
64
  'brand-primary-hover': ramp.orange[500],
57
65
  'brand-primary-active': ramp.orange[600],
58
66
 
@@ -60,6 +68,8 @@ export const semanticColorsLight = {
60
68
  'brand-secondary-light': ramp.cyan[500],
61
69
  'brand-secondary-dark': ramp.cyan[700],
62
70
  'brand-secondary-subtle': 'rgba(48, 123, 155, 0.08)',
71
+ 'brand-secondary-muted': 'rgba(48, 123, 155, 0.30)',
72
+ 'brand-secondary-strong': 'rgba(48, 123, 155, 0.50)',
63
73
  'brand-secondary-hover': ramp.cyan[700],
64
74
  'brand-secondary-active': ramp.cyan[800],
65
75
 
@@ -72,6 +82,8 @@ export const semanticColorsLight = {
72
82
  'status-success-light': ramp.green[200],
73
83
  'status-success-dark': ramp.green[600],
74
84
  'status-success-subtle': ramp.green[50],
85
+ 'status-success-muted': 'rgba(46, 213, 115, 0.30)',
86
+ 'status-success-strong': 'rgba(46, 213, 115, 0.50)',
75
87
 
76
88
  // Live-session accent — its OWN role, decoupled from success so the two can diverge
77
89
  'status-live': ramp.green[300],
@@ -81,21 +93,29 @@ export const semanticColorsLight = {
81
93
  'status-error-light': ramp.red[500],
82
94
  'status-error-dark': ramp.red[700],
83
95
  'status-error-subtle': ramp.red[50],
96
+ 'status-error-muted': 'rgba(209, 67, 67, 0.30)',
97
+ 'status-error-strong': 'rgba(209, 67, 67, 0.50)',
84
98
 
85
- 'status-error-vivid': ramp.red[600], // vivid alert red
99
+ 'status-error-vivid': alertRedVivid, // NOT red[600], that is status-error
86
100
  'status-error-vivid-light': ramp.red[500],
87
101
  'status-error-vivid-dark': ramp.red[700],
88
102
  'status-error-vivid-subtle': 'rgba(255, 71, 87, 0.12)',
103
+ 'status-error-vivid-muted': 'rgba(255, 71, 87, 0.30)',
104
+ 'status-error-vivid-strong': 'rgba(255, 71, 87, 0.50)',
89
105
 
90
106
  'status-warning': ramp.amber[300],
91
107
  'status-warning-light': ramp.amber[200],
92
108
  'status-warning-dark': ramp.amber[500],
93
109
  'status-warning-subtle': ramp.amber[50],
110
+ 'status-warning-muted': 'rgba(249, 180, 21, 0.30)',
111
+ 'status-warning-strong': 'rgba(249, 180, 21, 0.50)',
94
112
 
95
113
  'status-info': ramp.blue[500],
96
114
  'status-info-light': ramp.blue[300],
97
115
  'status-info-dark': ramp.blue[600],
98
116
  'status-info-subtle': ramp.blue[50],
117
+ 'status-info-muted': 'rgba(33, 150, 243, 0.30)',
118
+ 'status-info-strong': 'rgba(33, 150, 243, 0.50)',
99
119
 
100
120
  // Text on status backgrounds (on-status-*)
101
121
  'on-status-success': p.white,
@@ -104,15 +124,15 @@ export const semanticColorsLight = {
104
124
  'on-status-info': p.white,
105
125
 
106
126
  // Result/outcome indicators (result-*)
107
- 'result-improve': '#4caf50', // Green - positive outcome
127
+ 'result-improve': '#4caf50', // Green - positive outcome
108
128
  'result-improve-light': 'rgba(76, 175, 80, 0.12)',
109
129
  'result-improve-dark': '#248a24',
110
- 'result-degrade': '#ef5350', // Red - negative outcome
130
+ 'result-degrade': '#ef5350', // Red - negative outcome
111
131
  'result-degrade-light': 'rgba(239, 83, 80, 0.12)',
112
132
  'result-degrade-dark': '#b30000',
113
- 'result-inconclusive': '#9E9A97', // Gray - no clear result
133
+ 'result-inconclusive': '#9E9A97', // Gray - no clear result
114
134
  'result-inconclusive-light': 'rgba(158, 154, 151, 0.12)',
115
- 'result-neutral': greyRamp[600], // Neutral baseline
135
+ 'result-neutral': greyRamp[600], // Neutral baseline
116
136
 
117
137
  // Text on result backgrounds (on-result-*)
118
138
  'on-result-improve': p.white,
@@ -121,16 +141,16 @@ export const semanticColorsLight = {
121
141
 
122
142
  // Data visualization colors (data-*)
123
143
  // First 10 colors from discrete rainbow optimized for charts
124
- 'data-1': discreteRainbow[9], // Blue
125
- 'data-2': discreteRainbow[14], // Green
126
- 'data-3': discreteRainbow[17], // Yellow
127
- 'data-4': discreteRainbow[25], // Red
128
- 'data-5': discreteRainbow[8], // Purple
129
- 'data-6': discreteRainbow[20], // Orange
130
- 'data-7': discreteRainbow[13], // Light Blue
131
- 'data-8': discreteRainbow[15], // Light Green
132
- 'data-9': discreteRainbow[3], // Lavender
133
- 'data-10': discreteRainbow[22], // Dark Orange
144
+ 'data-1': discreteRainbow[9], // Blue
145
+ 'data-2': discreteRainbow[14], // Green
146
+ 'data-3': discreteRainbow[17], // Yellow
147
+ 'data-4': discreteRainbow[25], // Red
148
+ 'data-5': discreteRainbow[8], // Purple
149
+ 'data-6': discreteRainbow[20], // Orange
150
+ 'data-7': discreteRainbow[13], // Light Blue
151
+ 'data-8': discreteRainbow[15], // Light Green
152
+ 'data-9': discreteRainbow[3], // Lavender
153
+ 'data-10': discreteRainbow[22], // Dark Orange
134
154
 
135
155
  // Text colors (text-*)
136
156
  'text-primary': '#121828',
@@ -143,10 +163,10 @@ export const semanticColorsLight = {
143
163
 
144
164
  // Surface colors (surface-*) - for elevated containers
145
165
  'surface-base': p.white,
146
- 'surface-elevated': greyRamp[50], // slightly off-white for elevated cards
147
- 'surface-raised': greyRamp[100], // light gray for raised cards
166
+ 'surface-elevated': greyRamp[50], // slightly off-white for elevated cards
167
+ 'surface-raised': greyRamp[100], // light gray for raised cards
148
168
  'surface-overlay': p.white,
149
- 'surface-input': greyRamp[50], // Input field background (filled variant)
169
+ 'surface-input': greyRamp[50], // Input field background (filled variant)
150
170
 
151
171
  // Background colors (background-*)
152
172
  'background-base': '#EBEBEB',
@@ -157,12 +177,12 @@ export const semanticColorsLight = {
157
177
  'background-frame': greyRamp[400],
158
178
 
159
179
  // Border colors (border-*)
160
- 'border-prominent': greyRamp[400], // high-visibility divider
180
+ 'border-prominent': greyRamp[400], // high-visibility divider
161
181
  'border-focus': ramp.blue[600],
162
- 'border-input': greyRamp[200], // Input field border
163
- 'border-input-hover': greyRamp[400], // Input field border on hover
164
- 'border-input-focus': ramp.blue[600], // Input field border on focus
165
- 'border-input-error': ramp.red[600], // Input field border on error
182
+ 'border-input': greyRamp[200], // Input field border
183
+ 'border-input-hover': greyRamp[400], // Input field border on hover
184
+ 'border-input-focus': ramp.blue[600], // Input field border on focus
185
+ 'border-input-error': ramp.red[600], // Input field border on error
166
186
 
167
187
  // Alpha hairline separators (surface-independent — composite toward black on
168
188
  // light surfaces, mirroring the dark-mode white-alpha family). See §4/S-2.
@@ -179,7 +199,7 @@ export const semanticColorsLight = {
179
199
  'interactive-disabled-text': 'rgba(55, 65, 81, 0.26)',
180
200
 
181
201
  // Divider
182
- 'divider': '#E8E9EB',
202
+ divider: '#E8E9EB',
183
203
 
184
204
  // Avatar default
185
205
  'avatar-background': greyRamp[600],
@@ -193,6 +213,8 @@ export const semanticColorsDark = {
193
213
  'brand-primary-light': ramp.orange[300],
194
214
  'brand-primary-dark': ramp.orange[500],
195
215
  'brand-primary-subtle': 'rgba(255, 121, 0, 0.12)',
216
+ 'brand-primary-muted': 'rgba(255, 121, 0, 0.30)',
217
+ 'brand-primary-strong': 'rgba(255, 121, 0, 0.50)',
196
218
  'brand-primary-hover': ramp.orange[300],
197
219
  'brand-primary-active': ramp.orange[200],
198
220
 
@@ -200,6 +222,8 @@ export const semanticColorsDark = {
200
222
  'brand-secondary-light': ramp.cyan[500],
201
223
  'brand-secondary-dark': ramp.cyan[700],
202
224
  'brand-secondary-subtle': 'rgba(48, 123, 155, 0.12)',
225
+ 'brand-secondary-muted': 'rgba(48, 123, 155, 0.30)',
226
+ 'brand-secondary-strong': 'rgba(48, 123, 155, 0.50)',
203
227
  'brand-secondary-hover': ramp.cyan[500],
204
228
  'brand-secondary-active': ramp.cyan[400],
205
229
 
@@ -212,6 +236,8 @@ export const semanticColorsDark = {
212
236
  'status-success-light': ramp.green[200],
213
237
  'status-success-dark': ramp.green[600],
214
238
  'status-success-subtle': 'rgba(46, 213, 115, 0.12)',
239
+ 'status-success-muted': 'rgba(46, 213, 115, 0.30)',
240
+ 'status-success-strong': 'rgba(46, 213, 115, 0.50)',
215
241
 
216
242
  // Live-session accent — its OWN role, decoupled from success so the two can diverge
217
243
  'status-live': ramp.green[300],
@@ -221,21 +247,29 @@ export const semanticColorsDark = {
221
247
  'status-error-light': ramp.red[500],
222
248
  'status-error-dark': ramp.red[700],
223
249
  'status-error-subtle': 'rgba(209, 67, 67, 0.12)',
250
+ 'status-error-muted': 'rgba(209, 67, 67, 0.30)',
251
+ 'status-error-strong': 'rgba(209, 67, 67, 0.50)',
224
252
 
225
- 'status-error-vivid': ramp.red[600],
253
+ 'status-error-vivid': alertRedVivid,
226
254
  'status-error-vivid-light': ramp.red[500],
227
255
  'status-error-vivid-dark': ramp.red[700],
228
256
  'status-error-vivid-subtle': 'rgba(255, 71, 87, 0.12)',
257
+ 'status-error-vivid-muted': 'rgba(255, 71, 87, 0.30)',
258
+ 'status-error-vivid-strong': 'rgba(255, 71, 87, 0.50)',
229
259
 
230
260
  'status-warning': ramp.amber[300],
231
261
  'status-warning-light': ramp.amber[200],
232
262
  'status-warning-dark': ramp.amber[500],
233
263
  'status-warning-subtle': 'rgba(249, 180, 21, 0.12)',
264
+ 'status-warning-muted': 'rgba(249, 180, 21, 0.30)',
265
+ 'status-warning-strong': 'rgba(249, 180, 21, 0.50)',
234
266
 
235
267
  'status-info': ramp.blue[500],
236
268
  'status-info-light': ramp.blue[300],
237
269
  'status-info-dark': ramp.blue[600],
238
270
  'status-info-subtle': 'rgba(33, 150, 243, 0.12)',
271
+ 'status-info-muted': 'rgba(33, 150, 243, 0.30)',
272
+ 'status-info-strong': 'rgba(33, 150, 243, 0.50)',
239
273
 
240
274
  // Text on status backgrounds
241
275
  'on-status-success': p.white,
@@ -295,20 +329,20 @@ export const semanticColorsDark = {
295
329
  // `background-base` backs `Surface level="background"` (SurfaceContext.SURFACE_LEVEL_TOKEN),
296
330
  // so it takes the ramp's shell role. The deepest plane is `background-frame`,
297
331
  // which is also the `SurfaceLevel` floor a pressed surface clamps at.
298
- 'surface-base': greyRamp[925], // main surface (#252321, L*13.9)
299
- 'surface-elevated': greyRamp[900], // elevated surface (#2C2A28, L*17.2 — nav/rail)
300
- 'surface-raised': greyRamp[875], // raised surface (#31302F, L*19.9 — cards)
301
- 'surface-overlay': greyRamp[850], // overlay surface (#373635, L*22.7 — hero/popover)
302
- 'surface-input': greyRamp[900], // input surface (#2C2A28 — one plane above base)
332
+ 'surface-base': greyRamp[925], // main surface (#252321, L*13.9)
333
+ 'surface-elevated': greyRamp[900], // elevated surface (#2C2A28, L*17.2 — nav/rail)
334
+ 'surface-raised': greyRamp[875], // raised surface (#31302F, L*19.9 — cards)
335
+ 'surface-overlay': greyRamp[850], // overlay surface (#373635, L*22.7 — hero/popover)
336
+ 'surface-input': greyRamp[900], // input surface (#2C2A28 — one plane above base)
303
337
 
304
338
  // Background colors — same ramp, the frame/shell end of it.
305
- 'background-base': greyRamp[950], // shell (#1C1916, L*9 — Surface level="background")
306
- 'background-default': greyRamp[925], // main background (#252321, L*13.9 — matches surface-base)
307
- 'background-subtle': greyRamp[900], // subtle background (#2C2A28, L*17.2 — matches surface-elevated)
339
+ 'background-base': greyRamp[950], // shell (#1C1916, L*9 — Surface level="background")
340
+ 'background-default': greyRamp[925], // main background (#252321, L*13.9 — matches surface-base)
341
+ 'background-subtle': greyRamp[900], // subtle background (#2C2A28, L*17.2 — matches surface-elevated)
308
342
  // Frame/bezel chrome — the top bar + side nav shell, one step BELOW
309
343
  // `background-base`. It used to be described as sitting OUTSIDE the ramp; it
310
344
  // is simply the ramp's last step now, and the floor `<Surface pressed>` clamps at.
311
- 'background-frame': greyRamp[975], // frame / bezel (#100D0A, L*3.8)
345
+ 'background-frame': greyRamp[975], // frame / bezel (#100D0A, L*3.8)
312
346
 
313
347
  // Border colors — solid dark borders are RETIRED (TD-07.14). Not a preference — a structural
314
348
  // consequence of one ramp. Every step from 850 to 975 is now a surface plane,
@@ -326,7 +360,7 @@ export const semanticColorsDark = {
326
360
  // `border-prominent` stays SOLID: it is the one border meant to be seen
327
361
  // outright (4 call sites, high-visibility dividers), and grey-800 is not a
328
362
  // plane, so it keeps its job without collision.
329
- 'border-prominent': greyRamp[800], // high-visibility divider
363
+ 'border-prominent': greyRamp[800], // high-visibility divider
330
364
  'border-focus': '#828DF8',
331
365
  'border-input': greyRamp[700],
332
366
  'border-input-hover': greyRamp[600],
@@ -376,7 +410,7 @@ export const semanticColorsDark = {
376
410
  'interactive-disabled-text': 'rgba(255, 255, 255, 0.26)',
377
411
 
378
412
  // Divider
379
- 'divider': 'rgba(255, 255, 255, 0.09)',
413
+ divider: 'rgba(255, 255, 255, 0.09)',
380
414
 
381
415
  // Avatar default
382
416
  'avatar-background': greyRamp[700],
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { semanticColorsDark, semanticColorsLight } from './tokens/semantic'
3
+
4
+ /**
5
+ * Status and brand roles publish three wash rungs. They exist so a component can
6
+ * express a weight ramp without hand-mixing alpha: MesoProgressBar's segment
7
+ * track collapsed to one weight in three hues when only `-subtle` was available.
8
+ *
9
+ * A rung that drifts out of order silently flattens every consumer, and the
10
+ * consumer's own test cannot catch it — under the RNW vitest alias the rendered
11
+ * value is a `var(...)` reference, not an rgba triplet.
12
+ */
13
+ const WASH_ROLES = [
14
+ 'brand-primary',
15
+ 'brand-secondary',
16
+ 'status-success',
17
+ 'status-error',
18
+ 'status-error-vivid',
19
+ 'status-warning',
20
+ 'status-info',
21
+ ] as const
22
+
23
+ const RUNGS = ['subtle', 'muted', 'strong'] as const
24
+
25
+ function alphaOf(value: string): number {
26
+ const parts = value.match(/rgba?\(([^)]+)\)/)?.[1].split(',')
27
+ if (!parts) return 1 // a solid ramp pin is fully opaque
28
+ return parts.length === 4 ? Number(parts[3].trim()) : 1
29
+ }
30
+
31
+ describe('wash ladder', () => {
32
+ for (const [mode, palette] of [
33
+ ['dark', semanticColorsDark],
34
+ ['light', semanticColorsLight],
35
+ ] as const) {
36
+ describe(mode, () => {
37
+ for (const role of WASH_ROLES) {
38
+ it(`${role} publishes all three rungs`, () => {
39
+ for (const rung of RUNGS) {
40
+ expect(palette, `${role}-${rung} is missing`).toHaveProperty(`${role}-${rung}`)
41
+ }
42
+ })
43
+ }
44
+ })
45
+ }
46
+
47
+ // Light mode's `-subtle` is still a solid ramp tint rather than a wash, so its
48
+ // ladder is not comparable yet. AW-121 owns bringing light onto this model.
49
+ for (const role of WASH_ROLES) {
50
+ it(`${role} rungs ascend in weight (dark)`, () => {
51
+ const [subtle, muted, strong] = RUNGS.map((r) =>
52
+ alphaOf(semanticColorsDark[`${role}-${r}` as keyof typeof semanticColorsDark] as string)
53
+ )
54
+ expect(muted, `${role}-muted must outweigh -subtle`).toBeGreaterThan(subtle)
55
+ expect(strong, `${role}-strong must outweigh -muted`).toBeGreaterThan(muted)
56
+ })
57
+ }
58
+ })
@@ -48,13 +48,7 @@ export const WORKOUT_TOKENS = {
48
48
  over2: ramp.red[700],
49
49
  over3: ramp.red[800],
50
50
  targetLine: 'rgba(33, 150, 243, 0.5)',
51
- atTargetGlow:
52
- '0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)',
53
- },
54
-
55
- // Placeholder strip
56
- placeholder: {
57
- fill: '#3A3A3A',
51
+ atTargetGlow: '0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)',
58
52
  },
59
53
 
60
54
  // Deviation bar
@@ -5,12 +5,8 @@ declare module 'jest-axe' {
5
5
  ): Promise<{ violations: unknown[] }>
6
6
 
7
7
  export const toHaveNoViolations: {
8
- toHaveNoViolations(
9
- results: { violations: unknown[] }
10
- ): { pass: boolean; message(): string }
8
+ toHaveNoViolations(results: { violations: unknown[] }): { pass: boolean; message(): string }
11
9
  }
12
10
 
13
- export function configureAxe(
14
- options?: Record<string, unknown>
15
- ): typeof axe
11
+ export function configureAxe(options?: Record<string, unknown>): typeof axe
16
12
  }
@@ -0,0 +1,5 @@
1
+ /** Vite's `?raw` suffix — imports a file's contents as a string. */
2
+ declare module '*.md?raw' {
3
+ const content: string
4
+ export default content
5
+ }
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Color utility functions for the design system
2
+ * Color utility functions for the design system.
3
3
  *
4
- * lighten/darken are re-exported from shadows.ts which uses HSV-based
5
- * color math for more perceptually consistent results across hues.
4
+ * `lighten`/`darken` are re-exported from `theme/color-utils`, which does the
5
+ * HSV math. They are hover/state adjustments, not a depth ladder — planes come
6
+ * from the grey ramp.
6
7
  */
7
8
 
8
9
  export { lighten, darken } from '../theme/color-utils'
@@ -137,4 +138,3 @@ export function getLuminance(color: string): 'light' | 'dark' {
137
138
  export function getContrastText(backgroundColor: string): string {
138
139
  return getLuminance(backgroundColor) === 'light' ? '#000000' : '#FFFFFF'
139
140
  }
140
-
package/src/utils/form.ts CHANGED
@@ -160,9 +160,7 @@ export const validationRules = {
160
160
  /**
161
161
  * Compose multiple validation functions
162
162
  */
163
- export function composeValidators(
164
- ...validators: Array<(value: any) => string | undefined>
165
- ) {
163
+ export function composeValidators(...validators: Array<(value: any) => string | undefined>) {
166
164
  return (value: any): string | undefined => {
167
165
  for (const validator of validators) {
168
166
  const error = validator(value)