@titan-design/react-ui 0.12.0 → 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 +1320 -215
  7. package/dist/index.d.ts +1320 -215
  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 +202 -1042
  21. package/dist/theme/index.d.ts +202 -1042
  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 +972 -0
  31. package/dist/theme/tokens.d.ts +972 -0
  32. package/dist/theme/tokens.js +904 -0
  33. package/dist/theme/tokens.js.map +1 -0
  34. package/dist/theme/tokens.mjs +880 -0
  35. package/dist/theme/tokens.mjs.map +1 -0
  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 +7 -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 +12 -7
  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 +90 -27
  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 +24 -22
  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 +102 -0
  480. package/src/theme/tokens/primitives.test.ts +15 -7
  481. package/src/theme/tokens/primitives.ts +67 -35
  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
@@ -66,7 +66,9 @@ const PLANE_BY_STEP: Record<number, string> = Object.fromEntries(
66
66
  /** Steps whose value is byte-identical to what shipped in v0.10.0. */
67
67
  const ANCHOR_STEPS = new Set<number>(Object.values(SURFACE_PLANE_STEPS))
68
68
 
69
- const GREY_STEPS = Object.keys(greyRamp).map(Number).sort((a, b) => a - b)
69
+ const GREY_STEPS = Object.keys(greyRamp)
70
+ .map(Number)
71
+ .sort((a, b) => a - b)
70
72
 
71
73
  /**
72
74
  * The grey ramp, rendered as a numbered scale with its plane aliases.
@@ -92,7 +94,10 @@ function GreyRampRow() {
92
94
  borderColor: ANCHOR_STEPS.has(step) ? '#FF7900' : SWATCH_BORDER,
93
95
  }}
94
96
  />
95
- <Text className="text-text-primary" style={{ fontSize: 10, fontWeight: '600', marginTop: 3 }}>
97
+ <Text
98
+ className="text-text-primary"
99
+ style={{ fontSize: 10, fontWeight: '600', marginTop: 3 }}
100
+ >
96
101
  {step}
97
102
  {ANCHOR_STEPS.has(step) ? ' ★' : ''}
98
103
  </Text>
@@ -115,11 +120,10 @@ export const Ramps: StoryObj = {
115
120
  <View style={{ padding: 24 }}>
116
121
  <Text className="text-2xl font-bold text-text-primary mb-2">Ramps</Text>
117
122
  <SectionIntro>
118
- Every raw colour value titan ships, as ordered scales. Nothing here carries meaning — a
119
- ramp step becomes a border or a status only when a semantic token or a palette references
120
- it, which is what{' '}
121
- <Text className="font-semibold">Foundations/Color/Palettes</Text> documents. Everything on
122
- this page is current and safe to pick.
123
+ Every raw colour value titan ships, as ordered scales. Nothing here carries meaning — a ramp
124
+ step becomes a border or a status only when a semantic token or a palette references it,
125
+ which is what <Text className="font-semibold">Foundations/Color/Palettes</Text> documents.
126
+ Everything on this page is current and safe to pick.
123
127
  </SectionIntro>
124
128
 
125
129
  <Text className="text-text-tertiary text-xs mb-6">
@@ -127,21 +131,20 @@ export const Ramps: StoryObj = {
127
131
  <Text className="font-semibold">neutral</Text>,{' '}
128
132
  <Text className="font-semibold">redVivid</Text> or the support{' '}
129
133
  <Text className="font-semibold">blue</Text>/<Text className="font-semibold">red</Text>{' '}
130
- scales? All five were DELETED in TD-07.14, not shelved. The two grey scales folded into
131
- the warm ramp below — they were cold (neutral ran R−B down to −26) while the surfaces had
132
- been warm since v0.10.0, and nothing ever reconciled the two. The chromatics moved to the
133
- OKLCH ramps above. Spec and generator:
134
- coordination/design-explorations/foundations/warm-grey-ramp/.
134
+ scales? All five were DELETED in TD-07.14, not shelved. The two grey scales folded into the
135
+ warm ramp below — they were cold (neutral ran R−B down to −26) while the surfaces had been
136
+ warm since v0.10.0, and nothing ever reconciled the two. The chromatics moved to the OKLCH
137
+ ramps above. Spec and generator: sources/design/foundations/warm-grey-ramp/.
135
138
  </Text>
136
139
 
137
140
  <SectionTitle>Chromatic — OKLCH tonal ramps</SectionTitle>
138
141
  <Text className="text-text-secondary text-xs mb-4">
139
- Seven OKLCH-generated hue ramps, 11 perceptual steps each (50 → 950), built with
140
- hue-torsion and a chroma arc anchored through the brand/semantic hexes. These are the
141
- source of truth for chromatic hexes: the categorical, diverging, effort and fatigue
142
- palettes are all references into these steps rather than duplicated values, so re-tuning a
143
- ramp moves them together. Amber absorbs the former yellow (lemon → warm body) and cyan
144
- absorbs the former steel.
142
+ Seven OKLCH-generated hue ramps, 11 perceptual steps each (50 → 950), built with hue-torsion
143
+ and a chroma arc anchored through the brand/semantic hexes. These are the source of truth
144
+ for chromatic hexes: the categorical, diverging, effort and fatigue palettes are all
145
+ references into these steps rather than duplicated values, so re-tuning a ramp moves them
146
+ together. Amber absorbs the former yellow (lemon → warm body) and cyan absorbs the former
147
+ steel.
145
148
  </Text>
146
149
  {CHROMATIC_RAMPS.map(([key, name]) => (
147
150
  <ScaleRow key={key} name={name} scale={primitiveRamps[key]} />
@@ -157,16 +160,15 @@ export const Ramps: StoryObj = {
157
160
  set of names.
158
161
  </Text>
159
162
  <Text className="text-text-secondary text-xs mb-4">
160
- Two things about the numbering, both deliberate.{' '}
161
- <Text className="font-semibold">975</Text> sits past the end of the shared grid: the
162
- canonical L* ladder — the median of the seven chromatic ramps — stops at L*10.3, which is
163
- step 950, and the bezel is L*3.8. There was no number left for it.{' '}
164
- <Text className="font-semibold">850</Text> and <Text className="font-semibold">875</Text>{' '}
165
- are quarter-steps because overlay, raised and elevated sit only ~2.7 L* apart and all three
166
- collide on 900 at normal resolution. There is deliberately no{' '}
167
- <Text className="font-semibold">inset</Text>: at ΔE 1.10 from the frame it was an
168
- imperceptible duplicate, which is why <Text className="font-semibold">&lt;Surface
169
- pressed&gt;</Text> kept collapsing into it.
163
+ Two things about the numbering, both deliberate. <Text className="font-semibold">975</Text>{' '}
164
+ sits past the end of the shared grid: the canonical L* ladder — the median of the seven
165
+ chromatic ramps — stops at L*10.3, which is step 950, and the bezel is L*3.8. There was no
166
+ number left for it. <Text className="font-semibold">850</Text> and{' '}
167
+ <Text className="font-semibold">875</Text> are quarter-steps because overlay, raised and
168
+ elevated sit only ~2.7 L* apart and all three collide on 900 at normal resolution. There is
169
+ deliberately no <Text className="font-semibold">inset</Text>: at ΔE 1.10 from the frame it
170
+ was an imperceptible duplicate, which is why{' '}
171
+ <Text className="font-semibold">&lt;Surface pressed&gt;</Text> kept collapsing into it.
170
172
  </Text>
171
173
  <GreyRampRow />
172
174
  <Text className="text-text-tertiary text-xs mb-4">
@@ -258,8 +260,8 @@ export const Accessibility: StoryObj = {
258
260
  <SectionIntro>
259
261
  How the raw material behaves for viewers who cannot separate the hues, and under text.
260
262
  Dichromacy is simulated with the Machado-2009 matrices at severity 1.0. Converging strips
261
- across two swatches mean those two are the same colour to that viewer — which is a
262
- problem only where hue is carrying the meaning on its own.
263
+ across two swatches mean those two are the same colour to that viewer — which is a problem
264
+ only where hue is carrying the meaning on its own.
263
265
  </SectionIntro>
264
266
 
265
267
  <SectionTitle>Ramp mid-steps (500)</SectionTitle>
@@ -269,9 +271,7 @@ export const Accessibility: StoryObj = {
269
271
  below fans its picks across lightness instead of taking all seven at 500, and why the
270
272
  ordered scales in Palettes encode magnitude in lightness rather than hue.
271
273
  </Text>
272
- <View
273
- style={{ flexDirection: 'row', gap: 6, marginBottom: 6, paddingLeft: CVD_LABEL_WIDTH }}
274
- >
274
+ <View style={{ flexDirection: 'row', gap: 6, marginBottom: 6, paddingLeft: CVD_LABEL_WIDTH }}>
275
275
  {RAMP_MIDS.map(({ name, hex }) => (
276
276
  <View key={name} style={{ alignItems: 'center' }}>
277
277
  <View
@@ -297,8 +297,8 @@ export const Accessibility: StoryObj = {
297
297
  <SectionTitle>Categorical series — in-place text contrast</SectionTitle>
298
298
  <Text className="text-text-secondary text-xs mb-3">
299
299
  The palette that has to survive this test in production. All pairs hold ΔE ≥ 8 through the
300
- first {CATEGORICAL_CVD_SAFE_MAX}; the 7th is extended and wants a legend. The palette
301
- itself is documented in Palettes — this is the validation of it.
300
+ first {CATEGORICAL_CVD_SAFE_MAX}; the 7th is extended and wants a legend. The palette itself
301
+ is documented in Palettes — this is the validation of it.
302
302
  </Text>
303
303
  {(['default', 'dark'] as const).map((variant) => {
304
304
  const colors = categoricalPalette[variant]
@@ -3,12 +3,13 @@ import { View, Text } from 'react-native'
3
3
  import { greyRamp } from './tokens/primitives'
4
4
  import { getSemanticColors } from './tokens/semantic'
5
5
  import {
6
+ ELEVATION_PLANE,
7
+ FLOATING_ELEVATION_MIN,
6
8
  getElevationSurface,
7
9
  getElevationShadow,
8
- getBaseSurfaceColor,
9
- FLOATING_ELEVATION_MIN,
10
10
  type ElevationLevel,
11
11
  } from './elevation'
12
+ import { liftStyle, LIFT_RIM_ALPHA } from './lift'
12
13
  import { paperSheet, insetWell, barPaper } from './materials'
13
14
  import { SectionIntro, SectionTitle } from './color-story-kit'
14
15
 
@@ -17,15 +18,19 @@ const t = getSemanticColors('dark')
17
18
  /**
18
19
  * Foundations/Depth — how a surface says how far away it is.
19
20
  *
20
- * There are exactly three mechanisms, and which one applies is not a taste call:
21
+ * Four mechanisms, and which one applies is not a taste call:
21
22
  *
22
- * TONE how deep a plane sits — the grey ramp
23
- * HAIRLINE where one plane ends alpha, self-normalising
24
- * MATERIAL what a plane is made of paper, well
23
+ * TONE which plane a surface sits on — the grey ramp
24
+ * LIFT that it rests ON the plane below rim-light + ambient shadow
25
+ * HAIRLINE where one plane ends alpha, self-normalising
26
+ * MATERIAL what a plane is made of — paper, well
25
27
  *
26
- * Drop-shadow is deliberately not on that list for anything inline. This page
27
- * exists because the system previously had a fourth mechanism a neumorphic
28
- * dual-opposing shadow that could not work here and was removed in TD-07.16.
28
+ * Tone and lift travel together: every lifted plane wears both. This page
29
+ * previously taught that levels 0–3 separate by tone alone and that drop-shadow
30
+ * belongs to floating overlays only (TD-07.16). That was reversed on 2026-09-08:
31
+ * the upper ramp steps are ΔL* 2.5–3 apart, too tight for tone to carry the
32
+ * separation on its own, and a soft ambient shadow is not inert on a dark plane
33
+ * once a rim-light gives the edge something to fall from.
29
34
  */
30
35
  const meta: Meta = {
31
36
  title: 'Foundations/Depth',
@@ -35,7 +40,6 @@ export default meta
35
40
 
36
41
  const CONTENT_LEVELS: ElevationLevel[] = [0, 1, 2, 3]
37
42
  const FLOATING_LEVELS: ElevationLevel[] = [4, 5]
38
- const base = getBaseSurfaceColor('dark')
39
43
 
40
44
  function Swatch({
41
45
  label,
@@ -69,21 +73,21 @@ function Swatch({
69
73
  }
70
74
 
71
75
  export const Mechanisms: StoryObj = {
72
- name: '1. The three mechanisms',
76
+ name: '1. The four mechanisms',
73
77
  render: () => (
74
- <View style={{ padding: 24, backgroundColor: greyRamp[975] }}>
78
+ <View style={{ padding: 24, backgroundColor: greyRamp[925] }}>
75
79
  <Text className="text-2xl font-bold text-text-primary mb-2">Depth</Text>
76
80
  <SectionIntro>
77
- Three mechanisms, in the order you should reach for them. A surface that needs to read as
78
- deeper takes a TONE; one that needs an edge takes a HAIRLINE; one that needs to feel
79
- physical takes a MATERIAL. Drop-shadow is not on the list for anything that is not
80
- floating see section 3 for why.
81
+ Four mechanisms, in the order you should reach for them. A surface that sits higher takes a
82
+ TONE and, with it, a LIFT; one that only needs an edge takes a HAIRLINE; one that needs to
83
+ feel physical takes a MATERIAL. Tone is the only cue that works identically on web and
84
+ native, so it is never optional; the others sit on top of it.
81
85
  </SectionIntro>
82
86
 
83
87
  <SectionTitle>Tone — the grey ramp</SectionTitle>
84
88
  <Text className="text-text-secondary text-xs mb-3">
85
- The primary cue and the only one that works identically on web and native. Every plane is
86
- a ramp step.
89
+ Every plane is a ramp step. Nothing derives a colour between two steps: the elevation levels
90
+ in section 2 index this list, they do not lighten a base.
87
91
  </Text>
88
92
  <View style={{ flexDirection: 'row', gap: 10, marginBottom: 22 }}>
89
93
  {[975, 950, 925, 900, 875, 850].map((step) => (
@@ -96,12 +100,43 @@ export const Mechanisms: StoryObj = {
96
100
  ))}
97
101
  </View>
98
102
 
103
+ <SectionTitle>Lift — resting on the plane below</SectionTitle>
104
+ <Text className="text-text-secondary text-xs mb-3">
105
+ A crisp 1px top rim-light at {LIFT_RIM_ALPHA.dark} (one grade under paperSheet&apos;s 0.20
106
+ hero rim, chosen side by side in Lab/Depth) plus a soft ambient shadow that grows with the
107
+ planes crossed. Both are lit from above. This is what separates a card from the page when
108
+ the tone step alone is ΔL* 3. It is not a hairline ring: a ring is an edge, and stays the
109
+ divider&apos;s job.
110
+ </Text>
111
+ <View style={{ flexDirection: 'row', gap: 10, marginBottom: 22 }}>
112
+ <Swatch
113
+ label="tone only"
114
+ note="grey-875 on grey-925: the step is there, but the card is not resting on anything."
115
+ style={{ backgroundColor: greyRamp[875] }}
116
+ />
117
+ <Swatch
118
+ label="tone + rim"
119
+ note="the rim gives the top edge a lit lip."
120
+ style={{
121
+ backgroundColor: greyRamp[875],
122
+ boxShadow: `inset 0 1px 0 rgba(255,255,255,${LIFT_RIM_ALPHA.dark})`,
123
+ }}
124
+ />
125
+ <Swatch
126
+ label="tone + rim + shadow"
127
+ note="the default for any lifted plane: liftStyle(2)."
128
+ style={{
129
+ backgroundColor: greyRamp[875],
130
+ ...(liftStyle(2, 'dark') as Record<string, unknown>),
131
+ }}
132
+ />
133
+ </View>
134
+
99
135
  <SectionTitle>Hairline — where a plane ends</SectionTitle>
100
136
  <Text className="text-text-secondary text-xs mb-3">
101
- Alpha-white, so it composites toward white by the same amount on any background and holds
102
- a near-constant ΔL* wherever it lands. A solid border swings wildly across the ramp; this
103
- does not. It is the default separator, and it is what replaced the solid dark border
104
- tokens.
137
+ Alpha-white, so it composites toward white by the same amount on any background and holds a
138
+ near-constant ΔL* wherever it lands. It is the divider, the table rule, and the opt-in edge
139
+ of an `outline` card. It is not the default edge of a lifted card.
105
140
  </Text>
106
141
  <View style={{ flexDirection: 'row', gap: 10, marginBottom: 22 }}>
107
142
  {(['hairline-subtle', 'hairline-default', 'hairline-strong'] as const).map((token) => (
@@ -120,87 +155,118 @@ export const Mechanisms: StoryObj = {
120
155
 
121
156
  <SectionTitle>Material — what a plane is made of</SectionTitle>
122
157
  <Text className="text-text-secondary text-xs mb-3">
123
- Used sparingly. Paper is for HERO surfaces only and takes muted tones only vivid colour
124
- is ink ON paper, never the paper itself. There are no levels of paper; one treatment, or
125
- the texture stops meaning anything.
158
+ Used sparingly. Paper is for HERO surfaces only and takes muted tones only; vivid colour is
159
+ ink ON paper, never the paper itself. There are no levels of paper. The lift above is
160
+ paper&apos;s rim and contact shadow without the grain: the same light, one grade quieter.
126
161
  </Text>
127
162
  <View style={{ flexDirection: 'row', gap: 10, marginBottom: 8 }}>
128
163
  <Swatch
129
164
  label="paperSheet"
130
- note="tonal fill (ΔL* ≤ 3) + grain + dither + rim + contact shadow. Hero surfaces."
165
+ note="grain + rim + contact shadow. Hero surfaces."
131
166
  style={paperSheet(greyRamp[875]) as Record<string, unknown>}
132
167
  />
133
168
  <Swatch
134
169
  label="insetWell"
135
- note="inner top cut + a light line on the floor. The floor rim is the half that says 'below'."
170
+ note="inner top cut + a light line on the floor. Also the recess of every pressed surface."
136
171
  style={insetWell(greyRamp[950]) as Record<string, unknown>}
137
172
  />
138
173
  <Swatch
139
174
  label="barPaper"
140
175
  note="the same grain curve, tuned for a small saturated element."
141
- style={{ ...(barPaper(t['status-info']) as Record<string, unknown>), backgroundColor: t['status-info'] }}
176
+ style={{
177
+ ...(barPaper(t['status-info']) as Record<string, unknown>),
178
+ backgroundColor: t['status-info'],
179
+ }}
142
180
  />
143
181
  </View>
144
182
  <Text className="text-text-tertiary text-xs">
145
- Materials are web/RNW only — `backgroundImage` and multi-layer `boxShadow` are ignored on
146
- native. Every material keeps a solid `backgroundColor` underneath so a surface never
147
- depends on one, and a material must never be the only thing carrying meaning.
183
+ Lift and materials are web/RNW only — `backgroundImage` and multi-layer `boxShadow` are
184
+ ignored on native, which gets one shadow. Every treatment keeps a solid `backgroundColor`
185
+ underneath, so a surface never depends on one, and a treatment is never the only thing
186
+ carrying meaning.
148
187
  </Text>
149
188
  </View>
150
189
  ),
151
190
  }
152
191
 
153
192
  export const Elevation: StoryObj = {
154
- name: '2. Elevation — tone below, shadow only when floating',
193
+ name: '2. Elevation — planes on the ramp, lifted',
155
194
  render: () => (
156
- <View style={{ padding: 24, backgroundColor: greyRamp[975] }}>
195
+ <View style={{ padding: 24, backgroundColor: greyRamp[925] }}>
157
196
  <Text className="text-2xl font-bold text-text-primary mb-2">Elevation</Text>
158
197
  <SectionIntro>
159
- Levels 0–3 are separated by TONE ALONE. They cast no shadow, because a dark-on-dark drop
160
- shadow is inert it costs a composite layer and shows nothing. From{' '}
161
- <Text className="font-semibold">{FLOATING_ELEVATION_MIN}</Text> up the element is
162
- genuinely floating over a backdrop, where a large soft shadow describes separation rather
163
- than rank, and there the shadow returns.
198
+ A level is a ramp plane plus the lift that plane wears. Level 0 is the page. Levels 1–3
199
+ climb one plane each and lift; from {FLOATING_ELEVATION_MIN} up the element is floating over
200
+ a backdrop, so it stays on the overlay plane and the shadow does the separating. In a
201
+ component the numbers are RELATIVE to the enclosing Surface, clamped at overlay, so a card
202
+ inside a card cannot leave the ramp.
164
203
  </SectionIntro>
204
+ <Text className="text-text-secondary text-xs mb-4">
205
+ Lift sparingly, and only over something much larger. A card lifts off the page; a small card
206
+ lifted off a small card reads as clutter, not depth. Inside a lifted surface, organise with
207
+ an inset well (`CardInset`, `Surface pressed`), a tone-only `filled` tile, or a hairline
208
+ divider, and save a second lift for a surface that genuinely floats over the first.
209
+ </Text>
165
210
 
166
- <SectionTitle>Content levels — tone only</SectionTitle>
211
+ <SectionTitle>Content levels — tone + lift</SectionTitle>
167
212
  <View style={{ flexDirection: 'row', gap: 10, marginBottom: 22 }}>
168
213
  {CONTENT_LEVELS.map((lv) => (
169
214
  <Swatch
170
215
  key={lv}
171
216
  label={`elevation ${lv}`}
172
- note={`${getElevationSurface(base, lv, 'dark')} · no shadow`}
173
- style={{
174
- backgroundColor: getElevationSurface(base, lv, 'dark'),
175
- borderWidth: 1,
176
- borderColor: t['hairline-default'],
177
- ...(getElevationShadow(base, lv, 'dark') as Record<string, unknown>),
178
- }}
217
+ note={`${ELEVATION_PLANE[lv]} · ${getElevationSurface(lv, 'dark')}${lv > 0 ? ` · liftStyle(${lv})` : ' · the page'}`}
218
+ // Level 0 IS the page, so a solid swatch on the page would be an
219
+ // invisible rectangle. A dashed outline gives the row a fourth slot
220
+ // and says why it has nothing to draw.
221
+ style={
222
+ lv === 0
223
+ ? {
224
+ borderWidth: 1,
225
+ borderStyle: 'dashed',
226
+ borderColor: t['hairline-default'],
227
+ }
228
+ : {
229
+ backgroundColor: getElevationSurface(lv, 'dark'),
230
+ ...(getElevationShadow(lv, 'dark') as Record<string, unknown>),
231
+ }
232
+ }
179
233
  />
180
234
  ))}
181
235
  </View>
182
236
 
183
- <SectionTitle>Floating levels — shadow returns, always with a ring</SectionTitle>
237
+ <SectionTitle>Floating levels — overlay plane, larger shadow, no ring</SectionTitle>
184
238
  <View style={{ flexDirection: 'row', gap: 24, marginBottom: 8 }}>
185
239
  {FLOATING_LEVELS.map((lv) => (
186
240
  <Swatch
187
241
  key={lv}
188
242
  label={`elevation ${lv}`}
189
- note={`${getElevationSurface(base, lv, 'dark')} · casts a shadow`}
243
+ note={`${ELEVATION_PLANE[lv]} · ${getElevationSurface(lv, 'dark')} · three ambient layers`}
190
244
  style={{
191
- backgroundColor: getElevationSurface(base, lv, 'dark'),
192
- borderWidth: 1,
193
- borderColor: t['hairline-strong'],
194
- ...(getElevationShadow(base, lv, 'dark') as Record<string, unknown>),
245
+ backgroundColor: getElevationSurface(lv, 'dark'),
246
+ ...(getElevationShadow(lv, 'dark') as Record<string, unknown>),
195
247
  }}
196
248
  />
197
249
  ))}
198
250
  </View>
199
251
  <Text className="text-text-tertiary text-xs">
200
- A floating surface should carry a hairline ring as well as its shadow the shadow places
201
- it above the page, the ring gives it an edge that survives on a panel where the shadow is
202
- washed out by glare.
252
+ A floating surface carries the same rim as a card, not a hairline ring. The rim is the edge
253
+ that survives glare; a ring on top of it would be a second edge saying the same thing.
203
254
  </Text>
255
+
256
+ <SectionTitle>Recessed levels — one and two planes down</SectionTitle>
257
+ <View style={{ flexDirection: 'row', gap: 10, marginBottom: 8 }}>
258
+ {([-1, -2] as ElevationLevel[]).map((lv) => (
259
+ <Swatch
260
+ key={lv}
261
+ label={`elevation ${lv}`}
262
+ note={`${ELEVATION_PLANE[lv]} · ${getElevationSurface(lv, 'dark')} · insetWell recess`}
263
+ style={{
264
+ backgroundColor: getElevationSurface(lv, 'dark'),
265
+ ...(getElevationShadow(lv, 'dark') as Record<string, unknown>),
266
+ }}
267
+ />
268
+ ))}
269
+ </View>
204
270
  </View>
205
271
  ),
206
272
  }
@@ -208,19 +274,21 @@ export const Elevation: StoryObj = {
208
274
  export const WhyNotNeumorphism: StoryObj = {
209
275
  name: '3. Why not neumorphism',
210
276
  render: () => (
211
- <View style={{ padding: 24, backgroundColor: greyRamp[975] }}>
277
+ <View style={{ padding: 24, backgroundColor: greyRamp[925] }}>
212
278
  <Text className="text-2xl font-bold text-text-primary mb-2">Why not neumorphism</Text>
213
279
  <SectionIntro>
214
280
  The system used to ship a neumorphic builder — a dark shadow bottom-right paired with a
215
- light rim top-left. It was removed rather than retuned, and the reason is a lightness
216
- budget rather than a preference, so it is worth being able to see.
281
+ light rim top-left. It was removed rather than retuned, and the reason is a lightness budget
282
+ rather than a preference, so it is worth being able to see.
217
283
  </SectionIntro>
218
284
 
219
285
  <Text className="text-text-secondary text-xs mb-4">
220
286
  Neumorphism needs a MID-TONE ground (roughly 75–90% lightness) so the dark half has
221
- somewhere to fall and the light half has somewhere to rise. Our surfaces sit near 10%.
222
- Below them there is almost nothing left — so the dark half disappears and the treatment
223
- collapses to a one-sided rim, which reads as a smudge rather than as depth.
287
+ somewhere to fall and the light half has somewhere to rise. Our surfaces sit near 10%. Below
288
+ them there is almost nothing left — so the dark half disappears and the treatment collapses
289
+ to a one-sided rim, which reads as a smudge rather than as depth. The lift keeps the one
290
+ half that works (the top rim) and moves the shadow to where there IS room: straight down,
291
+ onto the plane below.
224
292
  </Text>
225
293
 
226
294
  <View style={{ flexDirection: 'row', gap: 24 }}>
@@ -294,9 +362,8 @@ export const WhyNotNeumorphism: StoryObj = {
294
362
  width: 110,
295
363
  height: 66,
296
364
  borderRadius: 10,
297
- backgroundColor: greyRamp[900],
298
- borderWidth: 1,
299
- borderColor: t['hairline-default'],
365
+ backgroundColor: greyRamp[875],
366
+ ...(liftStyle(2, 'dark') as Record<string, unknown>),
300
367
  }}
301
368
  />
302
369
  </View>
@@ -305,7 +372,7 @@ export const WhyNotNeumorphism: StoryObj = {
305
372
 
306
373
  <Text className="text-text-tertiary text-xs mt-4">
307
374
  Same treatment, same offsets, different ground. The middle panel is the one that shipped.
308
- The right panel is one tone step plus a hairline cheaper, and it survives glare.
375
+ The right panel is two ramp steps plus the lift: the rim from above, the shadow below.
309
376
  </Text>
310
377
  </View>
311
378
  ),
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
3
3
  import { View, Text, Pressable } from 'react-native'
4
4
  import { greyRamp } from './tokens/primitives'
5
5
  import { getSemanticColors } from './tokens/semantic'
6
- import { getBaseSurfaceColor, getElevationSurface, type ElevationLevel } from './elevation'
6
+ import { getElevationSurface, type ElevationLevel } from './elevation'
7
7
  import { grainForTone, paperSheet } from './materials'
8
8
 
9
9
  /**
@@ -19,8 +19,9 @@ import { grainForTone, paperSheet } from './materials'
19
19
  * north-star doc set its floors explicitly "to survive glare + a mediocre
20
20
  * panel", and none of them have been looked at on the panel. That matters more
21
21
  * than the numbers suggest: solid dark borders were DELETED, so if the hairlines
22
- * wash out there is no fallback, and elevation levels 1-3 are separated by tone
23
- * alone now that shadow is gated behind FLOATING_ELEVATION_MIN.
22
+ * wash out there is no fallback. Elevation levels now resolve to the ramp planes
23
+ * themselves and wear a lift (rim + shadow) on top; C2 measures the tone half of
24
+ * that pairing on its own, because the lift must never be the only cue.
24
25
  *
25
26
  * WHY FORCED-CHOICE. "Does this look OK?" is close to unfalsifiable at
26
27
  * threshold — you are asking someone to report the absence of a faint thing they
@@ -32,7 +33,7 @@ import { grainForTone, paperSheet } from './materials'
32
33
  * HOW TO RUN. On the wall panel, at demo brightness, at demo viewing distance
33
34
  * (~3 m), with the room lit the way it will be lit. Answer every panel BEFORE
34
35
  * touching Reveal. Record on the sheet:
35
- * `voltras-workspace/coordination/validation-runbooks/VW-99-depth-wall-calibration.md`
36
+ * `voltras-workspace/sources/runbooks/VW-99-depth-wall-calibration.md`
36
37
  */
37
38
  const meta: Meta = {
38
39
  title: 'Foundations/Depth Calibration',
@@ -45,7 +46,6 @@ const meta: Meta = {
45
46
  export default meta
46
47
 
47
48
  const t = getSemanticColors('dark')
48
- const base = getBaseSurfaceColor('dark')
49
49
 
50
50
  /**
51
51
  * The rig's ground sits BELOW the ramp, at true black.
@@ -105,7 +105,7 @@ function Panel({
105
105
  borderRadius: 8,
106
106
  borderWidth: 1,
107
107
  borderColor: t['hairline-strong'],
108
- backgroundColor: getElevationSurface(base, 2, 'dark'),
108
+ backgroundColor: getElevationSurface(2, 'dark'),
109
109
  }}
110
110
  >
111
111
  <Text className="text-text-primary" style={{ fontSize: 14, fontWeight: '600' }}>
@@ -233,7 +233,9 @@ export const Hairlines: StoryObj = {
233
233
 
234
234
  // ── C2 · tone steps ─────────────────────────────────────────────────────────
235
235
 
236
- const STAIRCASE: ElevationLevel[] = [0, 1, 2, 3, 4, 5]
236
+ // Levels 4 and 5 share the overlay plane by design (past the ramp the shadow
237
+ // separates), so the staircase stops at the last distinct tone.
238
+ const STAIRCASE: ElevationLevel[] = [-2, -1, 0, 1, 2, 3]
237
239
 
238
240
  /**
239
241
  * Adjacent-step pairs, with the lighter side pre-assigned to a fixed side and
@@ -244,13 +246,13 @@ const STEP_PAIRS: { left: ElevationLevel; right: ElevationLevel; label: string }
244
246
  { left: 1, right: 0, label: '1' },
245
247
  { left: 2, right: 3, label: '2' },
246
248
  { left: 2, right: 2, label: '3' },
247
- { left: 3, right: 4, label: '4' },
249
+ { left: -1, right: 0, label: '4' },
248
250
  { left: 1, right: 2, label: '5' },
249
- { left: 5, right: 4, label: '6' },
251
+ { left: -2, right: -1, label: '6' },
250
252
  ]
251
253
 
252
254
  export const ToneSteps: StoryObj = {
253
- name: 'C2 · Tone steps — levels 1-3 have nothing else',
255
+ name: 'C2 · Tone steps — the ramp with the lift stripped off',
254
256
  render: () => (
255
257
  <Panel
256
258
  title="C2 · Tone steps"
@@ -266,13 +268,13 @@ export const ToneSteps: StoryObj = {
266
268
  {STAIRCASE.map((lv) => (
267
269
  <View
268
270
  key={lv}
269
- style={{ flex: 1, backgroundColor: getElevationSurface(base, lv, 'dark') }}
271
+ style={{ flex: 1, backgroundColor: getElevationSurface(lv, 'dark') }}
270
272
  />
271
273
  ))}
272
274
  </View>
273
275
  <Text className="text-text-tertiary" style={{ fontSize: 13, marginTop: 8 }}>
274
276
  {revealed
275
- ? `6 bands / 5 edges — elevation 05, colorAdjustment 0 · .025 · .05 · .075 · .10 · .13`
277
+ ? `6 bands / 5 edges — elevation -23 = frame · background · base · elevated · raised · overlay`
276
278
  : 'Edges visible: ______'}
277
279
  </Text>
278
280
 
@@ -285,13 +287,13 @@ export const ToneSteps: StoryObj = {
285
287
  <View
286
288
  style={{
287
289
  flex: 1,
288
- backgroundColor: getElevationSurface(base, pair.left, 'dark'),
290
+ backgroundColor: getElevationSurface(pair.left, 'dark'),
289
291
  }}
290
292
  />
291
293
  <View
292
294
  style={{
293
295
  flex: 1,
294
- backgroundColor: getElevationSurface(base, pair.right, 'dark'),
296
+ backgroundColor: getElevationSurface(pair.right, 'dark'),
295
297
  }}
296
298
  />
297
299
  </View>
@@ -310,18 +312,18 @@ export const ToneSteps: StoryObj = {
310
312
  {revealed ? (
311
313
  <View style={{ marginTop: 14, maxWidth: 1000 }}>
312
314
  <KeyLine>
313
- {'Pair 1 is the one that matters: it is the 0.025 step from level 0 to level 1, the ' +
314
- 'smallest in the ladder and the one carrying the most UI. Pair 3 is the decoy.'}
315
+ {'Pair 2 is the one that matters: raised → overlay is the tightest step on the ' +
316
+ 'ramp (ΔL* 2.5) and the one a card inside a card lands on. Pair 3 is the decoy.'}
315
317
  </KeyLine>
316
318
  <KeyLine>
317
319
  {'PASS: all five edges counted on the staircase, pairs 1/2/4/5/6 called correctly, ' +
318
- 'pair 3 called SAME. Missing pair 1 means level 1 is not a level it is level 0 ' +
319
- 'with extra bookkeeping, and no shadow is coming to save it below level 4.'}
320
+ 'pair 3 called SAME. In production every lifted plane also wears a rim + shadow, ' +
321
+ 'but the tone must carry on its own native has no multi-layer shadow at all.'}
320
322
  </KeyLine>
321
323
  <KeyLine>
322
- {'Retune lever: widen `colorAdjustment` on elevation levels 1-3 in elevation.ts. ' +
323
- 'Level 5 is the ceiling the ladder has to fit under, so widening the bottom ' +
324
- 'compresses the top re-space the whole ladder rather than nudging one entry.'}
324
+ {'Retune lever: the ramp itself (tokens/primitives.ts, greyRamp 850–975). The ' +
325
+ 'elevation ladder no longer has values of its own to widen it reads the ramp ' +
326
+ 'so a missed step is a ramp finding, and re-spacing goes through the generator.'}
325
327
  </KeyLine>
326
328
  </View>
327
329
  ) : null}
@@ -578,7 +580,7 @@ const BAND_FIELDS: { key: string; answer: string; style: Record<string, unknown>
578
580
  style: {
579
581
  backgroundImage: `linear-gradient(180deg, ${BAND_STEPS.map(
580
582
  (hex, i) =>
581
- `${hex} ${(i / BAND_STEPS.length) * 100}%, ${hex} ${((i + 1) / BAND_STEPS.length) * 100}%`,
583
+ `${hex} ${(i / BAND_STEPS.length) * 100}%, ${hex} ${((i + 1) / BAND_STEPS.length) * 100}%`
582
584
  ).join(', ')})`,
583
585
  },
584
586
  },
@@ -14,7 +14,7 @@ describe('ThemeProvider', () => {
14
14
  render(
15
15
  <ThemeProvider>
16
16
  <Text>inside</Text>
17
- </ThemeProvider>,
17
+ </ThemeProvider>
18
18
  )
19
19
  expect(screen.getByText('inside')).toBeInTheDocument()
20
20
  })
@@ -23,7 +23,7 @@ describe('ThemeProvider', () => {
23
23
  render(
24
24
  <ThemeProvider mode="light">
25
25
  <Text>light</Text>
26
- </ThemeProvider>,
26
+ </ThemeProvider>
27
27
  )
28
28
  expect(screen.getByText('light')).toBeInTheDocument()
29
29
  })
@@ -32,7 +32,7 @@ describe('ThemeProvider', () => {
32
32
  render(
33
33
  <ThemeProvider mode="light">
34
34
  <ModeProbe />
35
- </ThemeProvider>,
35
+ </ThemeProvider>
36
36
  )
37
37
  expect(screen.getByText('light')).toBeInTheDocument()
38
38
  })
@@ -41,7 +41,7 @@ describe('ThemeProvider', () => {
41
41
  render(
42
42
  <ThemeProvider mode="system">
43
43
  <ModeProbe />
44
- </ThemeProvider>,
44
+ </ThemeProvider>
45
45
  )
46
46
  expect(screen.getByText('dark')).toBeInTheDocument()
47
47
  })