@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
@@ -10,7 +10,7 @@ describe('Pill', () => {
10
10
  })
11
11
 
12
12
  it('renders with all variants', () => {
13
- const variants = ['subtle', 'outline'] as const
13
+ const variants = ['solid', 'subtle', 'outline'] as const
14
14
  variants.forEach((variant) => {
15
15
  const { unmount } = render(<Pill variant={variant}>Test</Pill>)
16
16
  expect(screen.getByText('Test')).toBeInTheDocument()
@@ -19,7 +19,15 @@ describe('Pill', () => {
19
19
  })
20
20
 
21
21
  it('renders with all colors', () => {
22
- const colors = ['default', 'primary', 'secondary', 'success', 'error', 'warning', 'info'] as const
22
+ const colors = [
23
+ 'default',
24
+ 'primary',
25
+ 'secondary',
26
+ 'success',
27
+ 'error',
28
+ 'warning',
29
+ 'info',
30
+ ] as const
23
31
  colors.forEach((color) => {
24
32
  const { unmount } = render(<Pill color={color}>Test</Pill>)
25
33
  expect(screen.getByText('Test')).toBeInTheDocument()
@@ -27,8 +35,25 @@ describe('Pill', () => {
27
35
  })
28
36
  })
29
37
 
38
+ it('renders with all tones', () => {
39
+ const tones = [
40
+ 'neutral',
41
+ 'brand',
42
+ 'brand-secondary',
43
+ 'success',
44
+ 'warning',
45
+ 'error',
46
+ 'info',
47
+ ] as const
48
+ tones.forEach((tone) => {
49
+ const { unmount } = render(<Pill tone={tone}>Test</Pill>)
50
+ expect(screen.getByText('Test')).toBeInTheDocument()
51
+ unmount()
52
+ })
53
+ })
54
+
30
55
  it('renders with all sizes', () => {
31
- const sizes = ['xs', 'sm', 'md'] as const
56
+ const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const
32
57
  sizes.forEach((size) => {
33
58
  const { unmount } = render(<Pill size={size}>Test</Pill>)
34
59
  expect(screen.getByText('Test')).toBeInTheDocument()
@@ -36,6 +61,60 @@ describe('Pill', () => {
36
61
  })
37
62
  })
38
63
 
64
+ it('renders a tone-matched dot for leading="dot"', () => {
65
+ render(
66
+ <Pill tone="success" leading="dot">
67
+ Active
68
+ </Pill>
69
+ )
70
+ expect(screen.getByTestId('pill-dot')).toBeInTheDocument()
71
+ })
72
+
73
+ it('renders each tone with a leading dot', () => {
74
+ const tones = ['neutral', 'brand', 'brand-secondary', 'error'] as const
75
+ tones.forEach((tone) => {
76
+ const { unmount } = render(
77
+ <Pill tone={tone} leading="dot">
78
+ Test
79
+ </Pill>
80
+ )
81
+ expect(screen.getByTestId('pill-dot')).toBeInTheDocument()
82
+ unmount()
83
+ })
84
+ })
85
+
86
+ it('renders a leading icon node', () => {
87
+ render(<Pill leading={<span data-testid="icon" />}>Label</Pill>)
88
+ expect(screen.getByTestId('icon')).toBeInTheDocument()
89
+ expect(screen.queryByTestId('pill-dot')).not.toBeInTheDocument()
90
+ })
91
+
92
+ it('renders a trailing slot', () => {
93
+ render(<Pill trailing={<span data-testid="dismiss" />}>Label</Pill>)
94
+ expect(screen.getByTestId('dismiss')).toBeInTheDocument()
95
+ })
96
+
97
+ it('does not fire onPress when disabled', () => {
98
+ const handler = vi.fn()
99
+ render(
100
+ <Pill onPress={handler} isDisabled>
101
+ Click
102
+ </Pill>
103
+ )
104
+ fireEvent.click(screen.getByRole('button'))
105
+ expect(handler).not.toHaveBeenCalled()
106
+ })
107
+
108
+ it('maps the legacy color prop onto a tone', () => {
109
+ render(
110
+ <Pill color="primary" leading="dot">
111
+ Legacy
112
+ </Pill>
113
+ )
114
+ expect(screen.getByText('Legacy')).toBeInTheDocument()
115
+ expect(screen.getByTestId('pill-dot')).toBeInTheDocument()
116
+ })
117
+
39
118
  it('applies rounded-full by default', () => {
40
119
  const { container } = render(<Pill>Test</Pill>)
41
120
  expect(container.firstChild).toBeInTheDocument()
@@ -65,5 +144,24 @@ describe('Pill', () => {
65
144
  const results = await axe(container)
66
145
  expect(results).toHaveNoViolations()
67
146
  })
147
+
148
+ it('has no accessibility violations with a leading dot', async () => {
149
+ const { container } = render(
150
+ <Pill tone="success" leading="dot">
151
+ Active
152
+ </Pill>
153
+ )
154
+ expect(await axe(container)).toHaveNoViolations()
155
+ })
156
+
157
+ it('has no accessibility violations when interactive', async () => {
158
+ const { container } = render(<Pill onPress={() => {}}>Filter</Pill>)
159
+ expect(await axe(container)).toHaveNoViolations()
160
+ })
161
+
162
+ it('exposes a button role when interactive', () => {
163
+ render(<Pill onPress={() => {}}>Filter</Pill>)
164
+ expect(screen.getByRole('button')).toBeInTheDocument()
165
+ })
68
166
  })
69
167
  })
@@ -2,102 +2,217 @@ import React from 'react'
2
2
  import { View, Text, Pressable, type ViewProps } from 'react-native'
3
3
  import { cn } from '../../../utils/cn'
4
4
 
5
- export type PillVariant = 'subtle' | 'outline'
6
- export type PillColor = 'default' | 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info'
7
- export type PillSize = 'xs' | 'sm' | 'md'
5
+ export type PillVariant = 'solid' | 'subtle' | 'outline'
6
+ /** Semantic tone. `brand-secondary` is the accent, not a second brand. */
7
+ export type PillTone =
8
+ | 'neutral'
9
+ | 'brand'
10
+ | 'brand-secondary'
11
+ | 'success'
12
+ | 'warning'
13
+ | 'error'
14
+ | 'info'
15
+ /** Legacy palette names, kept so existing `color=` call sites keep working. */
16
+ export type PillColor =
17
+ | 'default'
18
+ | 'primary'
19
+ | 'secondary'
20
+ | 'success'
21
+ | 'error'
22
+ | 'warning'
23
+ | 'info'
24
+ export type PillSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
8
25
 
9
26
  export interface PillProps extends ViewProps {
10
27
  /** Pill content */
11
- children: React.ReactNode
28
+ children?: React.ReactNode
12
29
  /** Visual variant */
13
30
  variant?: PillVariant
14
- /** Color scheme */
31
+ /** Semantic tone */
32
+ tone?: PillTone
33
+ /** @deprecated Use `tone` — `color` maps onto it and is kept for call-site compatibility. */
15
34
  color?: PillColor
16
35
  /** Size */
17
36
  size?: PillSize
18
37
  /** Fully rounded (default true) or slight radius */
19
38
  rounded?: boolean
20
- /** Element before the label (e.g., Indicator dot) */
39
+ /** Leading slot: `'dot'` renders a tone-matched dot, any node renders as-is. */
40
+ leading?: React.ReactNode | 'dot'
41
+ /**
42
+ * Colour the leading dot independently of the capsule. For the pattern where
43
+ * the label is neutral and only the dot carries status, so a row of chips
44
+ * stays quiet and the dots are the signal.
45
+ */
46
+ dotTone?: PillTone
47
+ /** @deprecated Use `leading`. */
21
48
  leftElement?: React.ReactNode
49
+ /** Trailing slot (dismiss affordance, counter) */
50
+ trailing?: React.ReactNode
22
51
  /** Press handler (makes pill interactive) */
23
52
  onPress?: () => void
24
- /** Additional className */
53
+ /** Non-interactive and dimmed */
54
+ isDisabled?: boolean
55
+ /** Additional className on the container */
25
56
  className?: string
57
+ /** Additional className on the label, for presets that own their typography */
58
+ textClassName?: string
26
59
  }
27
60
 
28
- const variantColorStyles: Record<PillVariant, Record<PillColor, string>> = {
61
+ const colorToTone: Record<PillColor, PillTone> = {
62
+ default: 'neutral',
63
+ primary: 'brand',
64
+ secondary: 'brand-secondary',
65
+ success: 'success',
66
+ error: 'error',
67
+ warning: 'warning',
68
+ info: 'info',
69
+ }
70
+
71
+ const toneStyles: Record<PillVariant, Record<PillTone, string>> = {
72
+ solid: {
73
+ neutral: 'bg-hairline-strong border-transparent text-text-inverse',
74
+ brand: 'bg-brand-primary border-transparent text-text-inverse',
75
+ 'brand-secondary': 'bg-brand-secondary border-transparent text-text-inverse',
76
+ success: 'bg-status-success border-transparent text-text-inverse',
77
+ warning: 'bg-status-warning border-transparent text-text-inverse',
78
+ error: 'bg-status-error border-transparent text-text-inverse',
79
+ info: 'bg-status-info border-transparent text-text-inverse',
80
+ },
29
81
  subtle: {
30
- default: 'bg-surface-raised border-hairline-subtle text-text-secondary',
31
- primary: 'bg-brand-primary/10 border-brand-primary/25 text-brand-primary',
32
- secondary: 'bg-brand-secondary/10 border-brand-secondary/25 text-brand-secondary',
33
- success: 'bg-status-success/10 border-status-success/25 text-status-success',
34
- error: 'bg-status-error/10 border-status-error/25 text-status-error',
35
- warning: 'bg-status-warning/10 border-status-warning/25 text-status-warning',
36
- info: 'bg-status-info/10 border-status-info/25 text-status-info',
82
+ // Alpha-white rather than a ramp step: a pill sits on whatever plane its
83
+ // host is, and an alpha fill composites by the same amount on all of them.
84
+ // A `surface-*` fill would vanish on the plane it names. Borderless like the
85
+ // six coloured tones — the fill carries the capsule, not a ring.
86
+ neutral: 'bg-hairline-subtle border-transparent text-text-secondary',
87
+ brand: 'bg-brand-primary-subtle border-transparent text-brand-primary',
88
+ 'brand-secondary': 'bg-brand-secondary-subtle border-transparent text-brand-secondary',
89
+ success: 'bg-status-success-subtle border-transparent text-status-success',
90
+ warning: 'bg-status-warning-subtle border-transparent text-status-warning',
91
+ error: 'bg-status-error-subtle border-transparent text-status-error',
92
+ info: 'bg-status-info-subtle border-transparent text-status-info',
37
93
  },
38
94
  outline: {
39
- default: 'border-hairline text-text-secondary',
40
- primary: 'border-brand-primary text-brand-primary',
41
- secondary: 'border-brand-secondary text-brand-secondary',
95
+ neutral: 'border-hairline text-text-secondary',
96
+ brand: 'border-brand-primary text-brand-primary',
97
+ 'brand-secondary': 'border-brand-secondary text-brand-secondary',
42
98
  success: 'border-status-success text-status-success',
43
- error: 'border-status-error text-status-error',
44
99
  warning: 'border-status-warning text-status-warning',
100
+ error: 'border-status-error text-status-error',
45
101
  info: 'border-status-info text-status-info',
46
102
  },
47
103
  }
48
104
 
105
+ const dotToneStyles: Record<PillTone, string> = {
106
+ neutral: 'bg-text-tertiary',
107
+ brand: 'bg-brand-primary',
108
+ 'brand-secondary': 'bg-brand-secondary',
109
+ success: 'bg-status-success',
110
+ warning: 'bg-status-warning',
111
+ error: 'bg-status-error',
112
+ info: 'bg-status-info',
113
+ }
114
+
115
+ // One text step per rung. `md` used to repeat `sm`'s 10px, which left no 12px
116
+ // capsule at all and pushed anything between the two down to 10px — that is how
117
+ // MuscleGroupChip lost 7px of height. `md` is the 12px rung now.
49
118
  const sizeStyles: Record<PillSize, { container: string; text: string }> = {
50
- xs: { container: 'px-1 py-px', text: 'text-[9px]' },
51
- sm: { container: 'px-2 py-0.5', text: 'text-[10px]' },
52
- md: { container: 'px-2 py-1', text: 'text-[10px]' },
119
+ xs: { container: 'px-1 py-px', text: 'text-3xs' },
120
+ sm: { container: 'px-2 py-0.5', text: 'text-2xs' },
121
+ md: { container: 'px-2.5 py-1', text: 'text-xs' },
122
+ lg: { container: 'px-3 py-1.5', text: 'text-sm' },
123
+ xl: { container: 'px-4 py-2', text: 'text-base' },
124
+ }
125
+
126
+ // The dot derives its testID from the pill's, so a preset's dot stays
127
+ // addressable under the preset's own name (`muscle-group-chip-dot`) instead of
128
+ // collapsing to a generic one — the visual parity layer targets it by name.
129
+ function PillDot({ tone, testID }: { tone: PillTone; testID?: string }) {
130
+ return (
131
+ <View
132
+ className={cn('w-1.5 h-1.5 rounded-full shrink-0', dotToneStyles[tone])}
133
+ accessibilityElementsHidden
134
+ testID={testID ? `${testID}-dot` : 'pill-dot'}
135
+ />
136
+ )
53
137
  }
54
138
 
55
- export function Pill({
56
- children,
57
- variant = 'subtle',
58
- color = 'default',
59
- size = 'sm',
60
- rounded = true,
61
- leftElement,
62
- onPress,
63
- className,
64
- ...props
65
- }: PillProps) {
66
- const containerClasses = cn(
139
+ function containerClasses(p: PillProps, tone: PillTone) {
140
+ return cn(
67
141
  'flex-row items-center gap-1 border self-start shrink-0',
68
- rounded ? 'rounded-full' : 'rounded',
69
- variantColorStyles[variant][color],
70
- sizeStyles[size].container,
71
- className
142
+ p.rounded === false ? 'rounded' : 'rounded-full',
143
+ toneStyles[p.variant ?? 'subtle'][tone],
144
+ sizeStyles[p.size ?? 'sm'].container,
145
+ p.isDisabled && 'opacity-50',
146
+ p.className
72
147
  )
148
+ }
73
149
 
150
+ function PillContent({ tone, ...p }: PillProps & { tone: PillTone }) {
74
151
  const textClasses = cn(
75
- 'font-heading font-semibold',
76
- sizeStyles[size].text,
77
- 'text-inherit'
152
+ 'font-heading font-semibold text-inherit',
153
+ sizeStyles[p.size ?? 'sm'].text,
154
+ p.textClassName
78
155
  )
79
-
80
- const content = (
156
+ const slot = p.leading ?? p.leftElement
157
+ return (
81
158
  <>
82
- {leftElement}
83
- {typeof children === 'string' ? (
84
- <Text className={textClasses}>{children}</Text>
159
+ {slot === 'dot' ? <PillDot tone={p.dotTone ?? tone} testID={p.testID} /> : slot}
160
+ {typeof p.children === 'string' ? (
161
+ <Text className={textClasses}>{p.children}</Text>
85
162
  ) : (
86
- children
163
+ p.children
87
164
  )}
165
+ {p.trailing}
88
166
  </>
89
167
  )
168
+ }
169
+
170
+ /**
171
+ * The single pill primitive: a capsule of tone-coloured label with optional
172
+ * leading and trailing slots. `Badge`, `Chip`, `StatusPill` and
173
+ * `MuscleGroupChip` are presets over it.
174
+ *
175
+ * @example
176
+ * <Pill tone="success" leading="dot">Active</Pill>
177
+ */
178
+ export function Pill(props: PillProps) {
179
+ const {
180
+ variant,
181
+ tone,
182
+ color,
183
+ size,
184
+ rounded,
185
+ leading,
186
+ dotTone,
187
+ leftElement,
188
+ trailing,
189
+ onPress,
190
+ isDisabled,
191
+ className,
192
+ textClassName,
193
+ children,
194
+ ...viewProps
195
+ } = props
196
+ const resolvedTone = tone ?? (color ? colorToTone[color] : 'neutral')
197
+ const classes = containerClasses(props, resolvedTone)
198
+ const content = <PillContent {...props} tone={resolvedTone} />
90
199
 
91
200
  if (onPress) {
92
201
  return (
93
- <Pressable onPress={onPress} className={containerClasses} {...props}>
202
+ <Pressable
203
+ onPress={onPress}
204
+ disabled={isDisabled}
205
+ accessibilityRole="button"
206
+ className={classes}
207
+ {...viewProps}
208
+ >
94
209
  {content}
95
210
  </Pressable>
96
211
  )
97
212
  }
98
213
 
99
214
  return (
100
- <View className={containerClasses} {...props}>
215
+ <View className={classes} {...viewProps}>
101
216
  {content}
102
217
  </View>
103
218
  )
@@ -1,2 +1,2 @@
1
1
  export { Pill } from './Pill'
2
- export type { PillProps, PillVariant, PillColor, PillSize } from './Pill'
2
+ export type { PillProps, PillVariant, PillTone, PillColor, PillSize } from './Pill'
@@ -124,15 +124,15 @@ export const WithCloseButton: Story = {
124
124
  </Button>
125
125
  </PopoverTrigger>
126
126
  <PopoverContent>
127
- <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
127
+ <View
128
+ style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}
129
+ >
128
130
  <Text className="text-text-primary text-sm font-semibold">Settings</Text>
129
131
  <PopoverCloseButton>
130
132
  <Text className="text-text-secondary text-lg">&#10005;</Text>
131
133
  </PopoverCloseButton>
132
134
  </View>
133
- <Text className="text-text-secondary text-sm mt-2">
134
- Configure your preferences here.
135
- </Text>
135
+ <Text className="text-text-secondary text-sm mt-2">Configure your preferences here.</Text>
136
136
  </PopoverContent>
137
137
  </Popover>
138
138
  ),
@@ -149,9 +149,7 @@ export const WithFormContent: Story = {
149
149
  <PopoverContent>
150
150
  <View style={{ gap: 12 }}>
151
151
  <Text className="text-text-primary text-sm font-semibold">Edit Display Name</Text>
152
- <Text className="text-text-secondary text-xs">
153
- Enter your new display name below.
154
- </Text>
152
+ <Text className="text-text-secondary text-xs">Enter your new display name below.</Text>
155
153
  <View className="bg-surface-input border border-hairline rounded-md px-3 py-2">
156
154
  <Text className="text-text-secondary text-sm">John Doe</Text>
157
155
  </View>
@@ -1,12 +1,7 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
2
  import { render, screen, fireEvent, act } from '@testing-library/react'
3
3
  import { axe } from 'jest-axe'
4
- import {
5
- Popover,
6
- PopoverTrigger,
7
- PopoverContent,
8
- PopoverCloseButton,
9
- } from './Popover'
4
+ import { Popover, PopoverTrigger, PopoverContent, PopoverCloseButton } from './Popover'
10
5
 
11
6
  describe('Popover', () => {
12
7
  it('renders trigger element', () => {
@@ -1,6 +1,8 @@
1
1
  import React, { useState, useRef, useCallback, createContext, useContext } from 'react'
2
2
  import { View, Pressable, Modal, type ViewProps } from 'react-native'
3
3
  import { cn } from '../../../utils/cn'
4
+ import { Surface } from '../surface'
5
+ import { TriggerSurface } from '../trigger'
4
6
 
5
7
  export type PopoverPlacement = 'top' | 'bottom' | 'left' | 'right'
6
8
 
@@ -68,12 +70,15 @@ export function Popover({
68
70
  const isOpen = controlledIsOpen ?? internalIsOpen
69
71
  const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
70
72
 
71
- const setIsOpen = useCallback((open: boolean) => {
72
- if (controlledIsOpen === undefined) {
73
- setInternalIsOpen(open)
74
- }
75
- onOpenChange?.(open)
76
- }, [controlledIsOpen, onOpenChange])
73
+ const setIsOpen = useCallback(
74
+ (open: boolean) => {
75
+ if (controlledIsOpen === undefined) {
76
+ setInternalIsOpen(open)
77
+ }
78
+ onOpenChange?.(open)
79
+ },
80
+ [controlledIsOpen, onOpenChange]
81
+ )
77
82
 
78
83
  const handleHoverIn = useCallback(() => {
79
84
  if (triggerMode !== 'hover') return
@@ -126,15 +131,14 @@ export function PopoverTrigger({ children, className }: PopoverTriggerProps) {
126
131
  : {}
127
132
 
128
133
  return (
129
- <Pressable
130
- onPress={() => setIsOpen(!isOpen)}
134
+ <TriggerSurface
135
+ handlers={{ onPress: () => setIsOpen(!isOpen), ...hoverProps }}
131
136
  accessibilityRole="button"
132
137
  accessibilityState={{ expanded: isOpen }}
133
138
  className={className}
134
- {...hoverProps}
135
139
  >
136
140
  {children}
137
- </Pressable>
141
+ </TriggerSurface>
138
142
  )
139
143
  }
140
144
 
@@ -184,12 +188,13 @@ export function PopoverContent({ children, className }: PopoverContentProps) {
184
188
  className="fixed inset-0 z-40"
185
189
  style={{ position: 'absolute' }}
186
190
  />
187
- {/* Content */}
188
- <View
191
+ {/* Content — floating: overlay plane + lift, no ring. */}
192
+ <Surface
193
+ elevation={4}
194
+ rounded={false}
189
195
  className={cn(
190
196
  'absolute z-50 min-w-[200px]',
191
- 'bg-surface-elevated rounded-lg shadow-lg border border-hairline',
192
- 'p-4',
197
+ 'rounded-lg p-4',
193
198
  placementStyles[placement],
194
199
  className
195
200
  )}
@@ -208,7 +213,7 @@ export function PopoverContent({ children, className }: PopoverContentProps) {
208
213
  />
209
214
  )}
210
215
  {children}
211
- </View>
216
+ </Surface>
212
217
  </>
213
218
  )
214
219
  }
@@ -1,2 +1,8 @@
1
1
  export { Popover, PopoverTrigger, PopoverContent, PopoverCloseButton } from './Popover'
2
- export type { PopoverProps, PopoverTriggerProps, PopoverContentProps, PopoverCloseButtonProps, PopoverPlacement } from './Popover'
2
+ export type {
3
+ PopoverProps,
4
+ PopoverTriggerProps,
5
+ PopoverContentProps,
6
+ PopoverCloseButtonProps,
7
+ PopoverPlacement,
8
+ } from './Popover'
@@ -5,7 +5,7 @@ import { Progress, CircularProgress, ProgressSteps } from './Progress'
5
5
  const meta: Meta<typeof Progress> = {
6
6
  title: 'Components/Molecules/Progress',
7
7
  component: Progress,
8
- tags: ['autodocs'],
8
+ tags: ['autodocs', 'status:stable', '!status:review'],
9
9
  argTypes: {
10
10
  value: {
11
11
  control: { type: 'range', min: 0, max: 100, step: 1 },
@@ -131,11 +131,7 @@ export const Steps: Story = {
131
131
  color="success"
132
132
  labels={['Step 1', 'Step 2', 'Step 3', 'Step 4']}
133
133
  />
134
- <ProgressSteps
135
- currentStep={0}
136
- totalSteps={5}
137
- size="lg"
138
- />
134
+ <ProgressSteps currentStep={0} totalSteps={5} size="lg" />
139
135
  </View>
140
136
  ),
141
137
  }
@@ -152,12 +148,7 @@ export const UsageExamples: Story = {
152
148
  {/* Storage quota */}
153
149
  <View className="p-4 bg-surface-elevated rounded-lg">
154
150
  <Text className="text-text-primary font-medium mb-3">Storage</Text>
155
- <Progress
156
- value={8.5}
157
- max={10}
158
- color="warning"
159
- label="8.5 GB of 10 GB used"
160
- />
151
+ <Progress value={8.5} max={10} color="warning" label="8.5 GB of 10 GB used" />
161
152
  </View>
162
153
 
163
154
  {/* Checkout steps */}
@@ -154,11 +154,7 @@ describe('Progress', () => {
154
154
 
155
155
  it('renders labels', () => {
156
156
  render(
157
- <ProgressSteps
158
- currentStep={1}
159
- totalSteps={3}
160
- labels={['Step 1', 'Step 2', 'Step 3']}
161
- />
157
+ <ProgressSteps currentStep={1} totalSteps={3} labels={['Step 1', 'Step 2', 'Step 3']} />
162
158
  )
163
159
  expect(screen.getByText('Step 1')).toBeInTheDocument()
164
160
  expect(screen.getByText('Step 2')).toBeInTheDocument()