@titan-design/react-ui 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +14 -0
  2. package/dist/bodymap.js +129 -76
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +129 -76
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +117 -26
  7. package/dist/index.d.ts +117 -26
  8. package/dist/index.js +677 -450
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +673 -451
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-_3Ajpiqu.d.ts → pages-CsjsUn0G.d.ts} +30 -13
  13. package/dist/{pages-CpW3OB3g.d.mts → pages-ZbT_pD_U.d.mts} +30 -13
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +676 -701
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +676 -701
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +31 -7
  21. package/dist/theme/index.d.ts +31 -7
  22. package/dist/theme/index.js +84 -20
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +83 -21
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +84 -20
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +84 -20
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +56 -7
  31. package/dist/theme/tokens.d.ts +56 -7
  32. package/dist/theme/tokens.js +72 -20
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +71 -21
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/render-testing-pattern.md +130 -0
  37. package/package.json +1 -1
  38. package/src/arch/Architecture.stories.tsx +3 -1
  39. package/src/arch/arch-graph.json +74 -72
  40. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +2 -1
  41. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +8 -3
  42. package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
  43. package/src/components/custom/Gauge/Gauge.stories.tsx +4 -1
  44. package/src/components/custom/Gauge/Gauge.tsx +3 -1
  45. package/src/components/custom/Scatter/Scatter.stories.tsx +10 -7
  46. package/src/components/custom/Scatter/Scatter.tsx +6 -4
  47. package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
  48. package/src/components/custom/Table/Table.stories.tsx +1 -1
  49. package/src/components/custom/Table/Table.tsx +2 -2
  50. package/src/components/custom/Treemap/Treemap.stories.tsx +13 -10
  51. package/src/components/custom/Treemap/Treemap.tsx +4 -1
  52. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +2 -1
  53. package/src/components/custom/Workout/BaseBadge.stories.tsx +11 -7
  54. package/src/components/custom/Workout/BaseBadge.tsx +3 -2
  55. package/src/components/custom/Workout/BodyMap.tsx +5 -3
  56. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +13 -3
  57. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +5 -3
  58. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +2 -1
  59. package/src/components/custom/Workout/CapacityBandChart.tsx +5 -3
  60. package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
  61. package/src/components/custom/Workout/DeviationBar.tsx +5 -2
  62. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +7 -6
  63. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +29 -16
  64. package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -8
  65. package/src/components/custom/Workout/ExerciseCard.test.tsx +39 -19
  66. package/src/components/custom/Workout/ExerciseCard.tsx +92 -167
  67. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +89 -6
  68. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
  69. package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
  70. package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
  71. package/src/components/custom/Workout/ExerciseHeading.tsx +192 -47
  72. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -5
  73. package/src/components/custom/Workout/InputBar.tsx +4 -2
  74. package/src/components/custom/Workout/IntensityBar.tsx +4 -1
  75. package/src/components/custom/Workout/MesoCard.tsx +2 -1
  76. package/src/components/custom/Workout/MesoStatusCard.tsx +11 -10
  77. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +8 -3
  78. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +12 -2
  79. package/src/components/custom/Workout/README.md +58 -12
  80. package/src/components/custom/Workout/ReadinessCheck.tsx +4 -2
  81. package/src/components/custom/Workout/RestTimer.stories.tsx +13 -2
  82. package/src/components/custom/Workout/RestTimer.tsx +4 -2
  83. package/src/components/custom/Workout/SegmentedBar.stories.tsx +16 -12
  84. package/src/components/custom/Workout/SegmentedBar.tsx +4 -3
  85. package/src/components/custom/Workout/SetStrip.stories.tsx +3 -2
  86. package/src/components/custom/Workout/SetsRepsLoad.tsx +23 -9
  87. package/src/components/custom/Workout/Sparkline.stories.tsx +13 -10
  88. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +2 -1
  89. package/src/components/custom/Workout/StrengthTrendChart.tsx +8 -6
  90. package/src/components/custom/Workout/TempoDisplay.stories.tsx +4 -3
  91. package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
  92. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +3 -2
  93. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +1 -1
  94. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +40 -0
  95. package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
  96. package/src/components/custom/Workout/VelocityStrip.tsx +23 -14
  97. package/src/components/custom/Workout/ZoneTrack.stories.tsx +3 -1
  98. package/src/components/custom/Workout/exerciseRowState.ts +29 -0
  99. package/src/components/custom/Workout/index.ts +12 -2
  100. package/src/components/custom/Workout/metricText.stories.tsx +9 -8
  101. package/src/components/custom/Workout/metricText.tsx +4 -2
  102. package/src/components/custom/Workout/setHeadingKit.tsx +12 -10
  103. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  104. package/src/components/custom/charts/README.md +5 -0
  105. package/src/components/custom/charts/SetBarChart.test.tsx +16 -0
  106. package/src/components/custom/charts/SetBarChart.tsx +14 -5
  107. package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
  108. package/src/components/custom/charts/flatBarGeometry.ts +32 -0
  109. package/src/components/custom/stepper/Stepper.tsx +8 -6
  110. package/src/components/ui/alert/Alert.stories.tsx +14 -4
  111. package/src/components/ui/alert/Alert.tsx +24 -3
  112. package/src/components/ui/button/Button.tsx +12 -12
  113. package/src/components/ui/checkbox/Checkbox.tsx +2 -2
  114. package/src/components/ui/data-row/DataRow.stories.tsx +6 -2
  115. package/src/components/ui/drawer/Drawer.tsx +4 -1
  116. package/src/components/ui/icon-box/IconBox.tsx +8 -6
  117. package/src/components/ui/indicator/Indicator.stories.tsx +4 -3
  118. package/src/components/ui/list-item/ListItem.stories.tsx +7 -4
  119. package/src/components/ui/modal/Modal.tsx +2 -0
  120. package/src/components/ui/pill/Pill.stories.tsx +53 -0
  121. package/src/components/ui/pill/Pill.tsx +10 -6
  122. package/src/components/ui/popover/Popover.stories.tsx +1 -1
  123. package/src/components/ui/progress/Progress.tsx +9 -6
  124. package/src/components/ui/radio/Radio.tsx +1 -1
  125. package/src/components/ui/section/Section.stories.tsx +10 -10
  126. package/src/components/ui/select/Select.tsx +2 -1
  127. package/src/components/ui/spinner/Spinner.tsx +3 -1
  128. package/src/components/ui/stack/Stack.stories.tsx +7 -7
  129. package/src/components/ui/surface/Surface.stories.tsx +75 -28
  130. package/src/components/ui/switch/Switch.tsx +1 -1
  131. package/src/components/ui/tabs/Tabs.tsx +4 -2
  132. package/src/components/ui/toast/Toast.tsx +6 -4
  133. package/src/components/ui/toolbar-button/ToolbarButton.tsx +13 -6
  134. package/src/stories/PresetShowcase.stories.tsx +1 -1
  135. package/src/test/no-raw-color.test.ts +39 -0
  136. package/src/theme/Alpha.stories.tsx +103 -0
  137. package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
  138. package/src/theme/config.ts +12 -0
  139. package/src/theme/global.css +15 -3
  140. package/src/theme/subtle-tone-weight.test.ts +163 -0
  141. package/src/theme/tailwind-var-opacity.test.ts +163 -0
  142. package/src/theme/tokens/primitives.ts +33 -0
  143. package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
  144. package/src/theme/tokens/semantic.ts +56 -20
  145. package/src/utils/colors.alpha-adoption.test.ts +408 -0
  146. package/tailwind.config.js +50 -9
package/dist/bodymap.mjs CHANGED
@@ -12,8 +12,29 @@ function cn(...inputs) {
12
12
 
13
13
  // src/theme/tokens/primitives.ts
14
14
  var primitiveColors = {
15
- white: "#FFFFFF"};
15
+ white: "#FFFFFF",
16
+ black: "#000000"};
16
17
  var alertRedVivid = "#FF4757";
18
+ var resultPaletteColors = {
19
+ improve: "#4caf50",
20
+ improveDark: "#248a24",
21
+ degrade: "#ef5350",
22
+ degradeDark: "#b30000",
23
+ inconclusive: "#9E9A97"
24
+ };
25
+ var semanticPins = {
26
+ // Cool navy-black; darker and more blue-shifted than any warm greyRamp step.
27
+ textPrimaryLight: "#121828",
28
+ // Cool blue-grey; outside greyRamp's warm (R>=G>=B) scale.
29
+ textSecondaryLight: "#65748B",
30
+ // Pure neutral grey (R=G=B); no warm greyRamp step lands on it exactly.
31
+ backgroundBaseLight: "#EBEBEB",
32
+ // Near-neutral grey, kept one step off backgroundBaseLight by design.
33
+ dividerLight: "#E8E9EB",
34
+ // Periwinkle/indigo; more purple than any primitiveRamps.blue step. Shared by
35
+ // dark-mode text-link, border-focus and border-input-focus.
36
+ focusIndigoDark: "#828DF8"
37
+ };
17
38
  var greyRamp = {
18
39
  50: "#F9F6F3",
19
40
  // L*97.0 W6
@@ -47,6 +68,7 @@ var greyRamp = {
47
68
  var primitiveRamps = {
48
69
  red: {
49
70
  50: "#FFF4F4",
71
+ 400: "#F77175",
50
72
  500: "#E05254",
51
73
  600: "#D14343",
52
74
  // pin
@@ -151,6 +173,12 @@ var semanticColorsLight = {
151
173
  // Text on brand backgrounds (on-*)
152
174
  "on-brand-primary": primitiveColors.white,
153
175
  "on-brand-secondary": primitiveColors.white,
176
+ // Text ON a `-subtle` fill. Light mode keeps today's pairing (the base token) so
177
+ // nothing moves here: light has the same disease mirrored — a light rung on a
178
+ // near-white ramp[50] fill — but correcting it belongs with the light-mode pass
179
+ // in AW-121, not this one.
180
+ "on-brand-primary-subtle": primitiveRamps.orange[400],
181
+ "on-brand-secondary-subtle": primitiveRamps.cyan[600],
154
182
  // Status colors (status-*)
155
183
  "status-success": primitiveRamps.green[300],
156
184
  "status-success-light": primitiveRamps.green[200],
@@ -186,21 +214,26 @@ var semanticColorsLight = {
186
214
  "status-info-subtle": primitiveRamps.blue[50],
187
215
  "status-info-muted": "rgba(33, 150, 243, 0.30)",
188
216
  "status-info-strong": "rgba(33, 150, 243, 0.50)",
217
+ // Text ON a `-subtle` fill — see the on-brand-*-subtle note above.
218
+ "on-status-success-subtle": primitiveRamps.green[300],
219
+ "on-status-error-subtle": primitiveRamps.red[600],
220
+ "on-status-warning-subtle": primitiveRamps.amber[300],
221
+ "on-status-info-subtle": primitiveRamps.blue[500],
189
222
  // Text on status backgrounds (on-status-*)
190
223
  "on-status-success": primitiveColors.white,
191
224
  "on-status-error": primitiveColors.white,
192
225
  "on-status-warning": primitiveColors.white,
193
226
  "on-status-info": primitiveColors.white,
194
227
  // Result/outcome indicators (result-*)
195
- "result-improve": "#4caf50",
228
+ "result-improve": resultPaletteColors.improve,
196
229
  // Green - positive outcome
197
230
  "result-improve-light": "rgba(76, 175, 80, 0.12)",
198
- "result-improve-dark": "#248a24",
199
- "result-degrade": "#ef5350",
231
+ "result-improve-dark": resultPaletteColors.improveDark,
232
+ "result-degrade": resultPaletteColors.degrade,
200
233
  // Red - negative outcome
201
234
  "result-degrade-light": "rgba(239, 83, 80, 0.12)",
202
- "result-degrade-dark": "#b30000",
203
- "result-inconclusive": "#9E9A97",
235
+ "result-degrade-dark": resultPaletteColors.degradeDark,
236
+ "result-inconclusive": resultPaletteColors.inconclusive,
204
237
  // Gray - no clear result
205
238
  "result-inconclusive-light": "rgba(158, 154, 151, 0.12)",
206
239
  "result-neutral": greyRamp[600],
@@ -232,8 +265,8 @@ var semanticColorsLight = {
232
265
  "data-10": discreteRainbow[22],
233
266
  // Dark Orange
234
267
  // Text colors (text-*)
235
- "text-primary": "#121828",
236
- "text-secondary": "#65748B",
268
+ "text-primary": semanticPins.textPrimaryLight,
269
+ "text-secondary": semanticPins.textSecondaryLight,
237
270
  "text-tertiary": greyRamp[400],
238
271
  "text-disabled": "rgba(55, 65, 81, 0.48)",
239
272
  "text-inverse": primitiveColors.white,
@@ -249,7 +282,7 @@ var semanticColorsLight = {
249
282
  "surface-input": greyRamp[50],
250
283
  // Input field background (filled variant)
251
284
  // Background colors (background-*)
252
- "background-base": "#EBEBEB",
285
+ "background-base": semanticPins.backgroundBaseLight,
253
286
  "background-default": primitiveColors.white,
254
287
  "background-subtle": greyRamp[50],
255
288
  // Frame/bezel chrome — top bar + side nav shell, one step below
@@ -280,7 +313,7 @@ var semanticColorsLight = {
280
313
  "interactive-disabled": "rgba(55, 65, 81, 0.12)",
281
314
  "interactive-disabled-text": "rgba(55, 65, 81, 0.26)",
282
315
  // Divider
283
- divider: "#E8E9EB",
316
+ divider: semanticPins.dividerLight,
284
317
  // Avatar default
285
318
  "avatar-background": greyRamp[600],
286
319
  "avatar-text": primitiveColors.white
@@ -298,7 +331,7 @@ var semanticColorsDark = {
298
331
  "brand-secondary": primitiveRamps.cyan[600],
299
332
  "brand-secondary-light": primitiveRamps.cyan[500],
300
333
  "brand-secondary-dark": primitiveRamps.cyan[700],
301
- "brand-secondary-subtle": "rgba(48, 123, 155, 0.12)",
334
+ "brand-secondary-subtle": "rgba(34, 211, 238, 0.12)",
302
335
  "brand-secondary-muted": "rgba(48, 123, 155, 0.30)",
303
336
  "brand-secondary-strong": "rgba(48, 123, 155, 0.50)",
304
337
  "brand-secondary-hover": primitiveRamps.cyan[500],
@@ -306,6 +339,14 @@ var semanticColorsDark = {
306
339
  // Text on brand backgrounds
307
340
  "on-brand-primary": primitiveColors.white,
308
341
  "on-brand-secondary": primitiveColors.white,
342
+ // Text ON a `-subtle` fill. Its own role: `brand-primary` and friends are tuned to
343
+ // carry a white label as a solid fill, which makes the two deepest of them
344
+ // (cyan[600], red[600]) too dark to read as text on a dark plane. Levelling the
345
+ // family at rung 300 puts every tone within OKLCH L 0.769-0.813 instead of
346
+ // 0.550-0.813. Brand is deliberately left at its own orange[400] (operator, AW-133)
347
+ // so the Voltras tone never drifts; it is the one tone under AA on a raised card.
348
+ "on-brand-primary-subtle": primitiveRamps.orange[400],
349
+ "on-brand-secondary-subtle": primitiveRamps.cyan[300],
309
350
  // Status colors
310
351
  "status-success": primitiveRamps.green[300],
311
352
  "status-success-light": primitiveRamps.green[200],
@@ -319,7 +360,10 @@ var semanticColorsDark = {
319
360
  "status-error": primitiveRamps.red[600],
320
361
  "status-error-light": primitiveRamps.red[500],
321
362
  "status-error-dark": primitiveRamps.red[700],
322
- "status-error-subtle": "rgba(209, 67, 67, 0.12)",
363
+ // Alpha 0.08, not the family's 0.12: error's label is red[400], a rung darker than
364
+ // its siblings so it reads RED rather than pink (rung 300 has only 0.121 chroma).
365
+ // Thinning the fill buys back the contrast that extra darkness costs. See AW-133.
366
+ "status-error-subtle": "rgba(247, 113, 117, 0.08)",
323
367
  "status-error-muted": "rgba(209, 67, 67, 0.30)",
324
368
  "status-error-strong": "rgba(209, 67, 67, 0.50)",
325
369
  "status-error-vivid": alertRedVivid,
@@ -337,7 +381,7 @@ var semanticColorsDark = {
337
381
  "status-info": primitiveRamps.blue[500],
338
382
  "status-info-light": primitiveRamps.blue[300],
339
383
  "status-info-dark": primitiveRamps.blue[600],
340
- "status-info-subtle": "rgba(33, 150, 243, 0.12)",
384
+ "status-info-subtle": "rgba(120, 194, 255, 0.12)",
341
385
  "status-info-muted": "rgba(33, 150, 243, 0.30)",
342
386
  "status-info-strong": "rgba(33, 150, 243, 0.50)",
343
387
  // Text on status backgrounds
@@ -345,14 +389,22 @@ var semanticColorsDark = {
345
389
  "on-status-error": primitiveColors.white,
346
390
  "on-status-warning": primitiveColors.white,
347
391
  "on-status-info": primitiveColors.white,
392
+ // Text ON a `-subtle` fill — see the on-brand-*-subtle note above. Error is the
393
+ // second deliberate exception to the rung-300 rule (operator, AW-133): red[300]
394
+ // levelled perfectly but read PINK, because a red that light can only hold 0.121
395
+ // chroma. red[400] carries 0.165 and reads red; its fill is thinned to compensate.
396
+ "on-status-success-subtle": primitiveRamps.green[300],
397
+ "on-status-error-subtle": primitiveRamps.red[400],
398
+ "on-status-warning-subtle": primitiveRamps.amber[300],
399
+ "on-status-info-subtle": primitiveRamps.blue[300],
348
400
  // Result/outcome indicators (result-*)
349
- "result-improve": "#4caf50",
401
+ "result-improve": resultPaletteColors.improve,
350
402
  "result-improve-light": "rgba(76, 175, 80, 0.16)",
351
- "result-improve-dark": "#248a24",
352
- "result-degrade": "#ef5350",
403
+ "result-improve-dark": resultPaletteColors.improveDark,
404
+ "result-degrade": resultPaletteColors.degrade,
353
405
  "result-degrade-light": "rgba(239, 83, 80, 0.16)",
354
- "result-degrade-dark": "#b30000",
355
- "result-inconclusive": "#9E9A97",
406
+ "result-degrade-dark": resultPaletteColors.degradeDark,
407
+ "result-inconclusive": resultPaletteColors.inconclusive,
356
408
  "result-inconclusive-light": "rgba(158, 154, 151, 0.16)",
357
409
  "result-neutral": greyRamp[400],
358
410
  // Text on result backgrounds
@@ -383,7 +435,7 @@ var semanticColorsDark = {
383
435
  "text-tertiary": greyRamp[500],
384
436
  "text-disabled": "rgba(255, 255, 255, 0.38)",
385
437
  "text-inverse": greyRamp[950],
386
- "text-link": "#828DF8",
438
+ "text-link": semanticPins.focusIndigoDark,
387
439
  "text-link-hover": primitiveRamps.blue[400],
388
440
  // Surface colors - dark backgrounds — warm-tapered DERIVED ramp (TD-surface-tokens,
389
441
  // S-1, re-spaced S-3). Shipped verbatim from `deriveSurfaceRamp()` — see
@@ -433,10 +485,10 @@ var semanticColorsDark = {
433
485
  // plane, so it keeps its job without collision.
434
486
  "border-prominent": greyRamp[800],
435
487
  // high-visibility divider
436
- "border-focus": "#828DF8",
488
+ "border-focus": semanticPins.focusIndigoDark,
437
489
  "border-input": greyRamp[700],
438
490
  "border-input-hover": greyRamp[600],
439
- "border-input-focus": "#828DF8",
491
+ "border-input-focus": semanticPins.focusIndigoDark,
440
492
  "border-input-error": primitiveRamps.red[500],
441
493
  // Alpha hairline separators — the primary separation cue (§4/S-2). Self-
442
494
  // normalizing: composites toward white by ~the same amount on ANY plane, so
@@ -511,6 +563,38 @@ function hexToRgb(hex) {
511
563
  return { r, g, b };
512
564
  }
513
565
 
566
+ // src/utils/colors.ts
567
+ function alpha(color, opacity) {
568
+ const clampedOpacity = Math.max(0, Math.min(1, opacity));
569
+ if (color.startsWith("#")) {
570
+ const hex = color.slice(1);
571
+ let r, g, b;
572
+ if (hex.length === 3) {
573
+ r = parseInt(hex[0] + hex[0], 16);
574
+ g = parseInt(hex[1] + hex[1], 16);
575
+ b = parseInt(hex[2] + hex[2], 16);
576
+ } else if (hex.length === 6) {
577
+ r = parseInt(hex.slice(0, 2), 16);
578
+ g = parseInt(hex.slice(2, 4), 16);
579
+ b = parseInt(hex.slice(4, 6), 16);
580
+ } else {
581
+ return color;
582
+ }
583
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
584
+ }
585
+ const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
586
+ if (rgbMatch) {
587
+ const [, r, g, b] = rgbMatch;
588
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
589
+ }
590
+ const rgbaMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/);
591
+ if (rgbaMatch) {
592
+ const [, r, g, b] = rgbaMatch;
593
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
594
+ }
595
+ return color;
596
+ }
597
+
514
598
  // src/theme/materials.ts
515
599
  var c = getSemanticColors("dark");
516
600
  function insetWell(tone = c["surface-input"]) {
@@ -861,8 +945,8 @@ var jsxs = wrapJsx(jsxs$1);
861
945
  // src/components/custom/Workout/BodyMap.tsx
862
946
  var Body = BodyHighlighter.default ?? BodyHighlighter;
863
947
  var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
864
- var OUTLINE_FILL = "rgba(255,255,255,0.08)";
865
- var OUTLINE_BORDER = "rgba(255,255,255,0.12)";
948
+ var OUTLINE_FILL = alpha(primitiveColors.white, 0.08);
949
+ var OUTLINE_BORDER = alpha(primitiveColors.white, 0.12);
866
950
  var BODY_SCALE = 0.8;
867
951
  function buildSlugParts(data) {
868
952
  const bySlug = /* @__PURE__ */ new Map();
@@ -1038,7 +1122,7 @@ function ViewToggle({ view, onViewChange }) {
1038
1122
  paddingHorizontal: 12,
1039
1123
  paddingVertical: 4,
1040
1124
  borderRadius: 9999,
1041
- backgroundColor: active ? "rgba(255,121,0,0.16)" : "transparent",
1125
+ backgroundColor: active ? alpha(BRAND_PRIMARY, 0.16) : "transparent",
1042
1126
  borderWidth: 1,
1043
1127
  borderColor: active ? BRAND_PRIMARY : resolveColor("hairline-strong")
1044
1128
  },
@@ -1216,13 +1300,16 @@ var toneStyles = {
1216
1300
  // host is, and an alpha fill composites by the same amount on all of them.
1217
1301
  // A `surface-*` fill would vanish on the plane it names. Borderless like the
1218
1302
  // six coloured tones — the fill carries the capsule, not a ring.
1303
+ // Label comes from `on-*-subtle`, not the base tone token: the base is tuned to
1304
+ // carry a white label as a solid fill, which left error and accent unreadable
1305
+ // here (AW-133). The on-subtle tokens level the family at one weight.
1219
1306
  neutral: "bg-hairline-subtle border-transparent text-text-secondary",
1220
- brand: "bg-brand-primary-subtle border-transparent text-brand-primary",
1221
- "brand-secondary": "bg-brand-secondary-subtle border-transparent text-brand-secondary",
1222
- success: "bg-status-success-subtle border-transparent text-status-success",
1223
- warning: "bg-status-warning-subtle border-transparent text-status-warning",
1224
- error: "bg-status-error-subtle border-transparent text-status-error",
1225
- info: "bg-status-info-subtle border-transparent text-status-info"
1307
+ brand: "bg-brand-primary-subtle border-transparent text-on-brand-primary-subtle",
1308
+ "brand-secondary": "bg-brand-secondary-subtle border-transparent text-on-brand-secondary-subtle",
1309
+ success: "bg-status-success-subtle border-transparent text-on-status-success-subtle",
1310
+ warning: "bg-status-warning-subtle border-transparent text-on-status-warning-subtle",
1311
+ error: "bg-status-error-subtle border-transparent text-on-status-error-subtle",
1312
+ info: "bg-status-info-subtle border-transparent text-on-status-info-subtle"
1226
1313
  },
1227
1314
  outline: {
1228
1315
  neutral: "border-hairline text-text-secondary",
@@ -1707,7 +1794,7 @@ function BodyMapDetailPanel({
1707
1794
  left: 0,
1708
1795
  right: 0,
1709
1796
  bottom: 0,
1710
- backgroundColor: "#000000",
1797
+ backgroundColor: primitiveColors.black,
1711
1798
  opacity: backdrop
1712
1799
  },
1713
1800
  testID: "body-map-detail-panel-backdrop-anim",
@@ -2048,9 +2135,9 @@ function BodyMapDetailPanel({
2048
2135
  paddingVertical: 10,
2049
2136
  borderRadius: 8,
2050
2137
  alignItems: "center",
2051
- backgroundColor: "rgba(255,121,0,0.12)",
2138
+ backgroundColor: alpha(BRAND_PRIMARY2, 0.12),
2052
2139
  borderWidth: 1,
2053
- borderColor: "rgba(255,121,0,0.3)"
2140
+ borderColor: alpha(BRAND_PRIMARY2, 0.3)
2054
2141
  },
2055
2142
  testID: "body-map-detail-panel-view-exercises",
2056
2143
  children: /* @__PURE__ */ jsx(
@@ -2249,40 +2336,6 @@ function SkeletonBody({ hasHeader, hasFooter, contentLines }) {
2249
2336
  hasFooter && /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(View, { className: "h-9 w-24 bg-interactive-disabled rounded" }) })
2250
2337
  ] });
2251
2338
  }
2252
-
2253
- // src/utils/colors.ts
2254
- function alpha(color, opacity) {
2255
- const clampedOpacity = Math.max(0, Math.min(1, opacity));
2256
- if (color.startsWith("#")) {
2257
- const hex = color.slice(1);
2258
- let r, g, b;
2259
- if (hex.length === 3) {
2260
- r = parseInt(hex[0] + hex[0], 16);
2261
- g = parseInt(hex[1] + hex[1], 16);
2262
- b = parseInt(hex[2] + hex[2], 16);
2263
- } else if (hex.length === 6) {
2264
- r = parseInt(hex.slice(0, 2), 16);
2265
- g = parseInt(hex.slice(2, 4), 16);
2266
- b = parseInt(hex.slice(4, 6), 16);
2267
- } else {
2268
- return color;
2269
- }
2270
- return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
2271
- }
2272
- const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
2273
- if (rgbMatch) {
2274
- const [, r, g, b] = rgbMatch;
2275
- return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
2276
- }
2277
- const rgbaMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/);
2278
- if (rgbaMatch) {
2279
- const [, r, g, b] = rgbaMatch;
2280
- return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
2281
- }
2282
- return color;
2283
- }
2284
-
2285
- // src/components/custom/Workout/StatusDot.tsx
2286
2339
  var solidFillToken = {
2287
2340
  success: "status-success",
2288
2341
  warning: "status-warning",
@@ -2433,11 +2486,11 @@ var WARNING = t3["status-warning"];
2433
2486
  var ERROR = t3["status-error"];
2434
2487
  var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY3, BRAND_PRIMARY_LIGHT];
2435
2488
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
2436
- var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(249,180,21,0.25) 50%, rgba(209,67,67,0.25) 100%)";
2489
+ var GAUGE_GRADIENT = `linear-gradient(90deg, ${alpha(SUCCESS, 0.25)} 0%, ${alpha(WARNING, 0.25)} 50%, ${alpha(ERROR, 0.25)} 100%)`;
2437
2490
  var STATUS_VARIANTS = {
2438
- success: { bg: "rgba(46,213,115,0.15)", border: "rgba(46,213,115,0.3)", text: SUCCESS },
2439
- warning: { bg: "rgba(249,180,21,0.15)", border: "rgba(249,180,21,0.3)", text: WARNING },
2440
- error: { bg: "rgba(209,67,67,0.15)", border: "rgba(209,67,67,0.25)", text: ERROR }
2491
+ success: { bg: alpha(SUCCESS, 0.15), border: alpha(SUCCESS, 0.3), text: SUCCESS },
2492
+ warning: { bg: alpha(WARNING, 0.15), border: alpha(WARNING, 0.3), text: WARNING },
2493
+ error: { bg: alpha(ERROR, 0.15), border: alpha(ERROR, 0.25), text: ERROR }
2441
2494
  };
2442
2495
  function clamp012(value) {
2443
2496
  if (!Number.isFinite(value)) return 0;
@@ -2590,7 +2643,7 @@ function Gauge({ gauge }) {
2590
2643
  bottom: 0,
2591
2644
  width: 1,
2592
2645
  marginLeft: -0.5,
2593
- backgroundColor: "rgba(255,255,255,0.3)"
2646
+ backgroundColor: alpha(primitiveColors.white, 0.3)
2594
2647
  },
2595
2648
  testID: "meso-status-card-gauge-center"
2596
2649
  }
@@ -2699,9 +2752,9 @@ function MesoStatusCard({
2699
2752
  View,
2700
2753
  {
2701
2754
  style: {
2702
- backgroundColor: "rgba(249,180,21,0.06)",
2755
+ backgroundColor: alpha(WARNING, 0.06),
2703
2756
  borderWidth: 1,
2704
- borderColor: "rgba(249,180,21,0.15)",
2757
+ borderColor: alpha(WARNING, 0.15),
2705
2758
  borderRadius: 8,
2706
2759
  paddingVertical: 10,
2707
2760
  paddingHorizontal: 12
@@ -2727,9 +2780,9 @@ function MesoStatusCard({
2727
2780
  className: "flex-row items-center",
2728
2781
  style: {
2729
2782
  gap: 8,
2730
- backgroundColor: "rgba(46,213,115,0.06)",
2783
+ backgroundColor: alpha(SUCCESS, 0.06),
2731
2784
  borderWidth: 1,
2732
- borderColor: "rgba(46,213,115,0.2)",
2785
+ borderColor: alpha(SUCCESS, 0.2),
2733
2786
  borderRadius: 8,
2734
2787
  paddingVertical: 10,
2735
2788
  paddingHorizontal: 12