@titan-design/react-ui 0.3.0 → 0.4.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 (52) hide show
  1. package/dist/bodymap.js +441 -97
  2. package/dist/bodymap.js.map +1 -1
  3. package/dist/bodymap.mjs +49 -62
  4. package/dist/bodymap.mjs.map +1 -1
  5. package/dist/{chunk-TOD2WQBG.mjs → chunk-2SISKTWM.mjs} +22 -43
  6. package/dist/chunk-2SISKTWM.mjs.map +1 -0
  7. package/dist/{chunk-P7TSQUN6.mjs → chunk-SNVKL5WZ.mjs} +42 -5
  8. package/dist/chunk-SNVKL5WZ.mjs.map +1 -0
  9. package/dist/index.d.mts +42 -2
  10. package/dist/index.d.ts +42 -2
  11. package/dist/index.js +431 -328
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +397 -314
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/theme/index.d.mts +28 -2
  16. package/dist/theme/index.d.ts +28 -2
  17. package/dist/theme/index.js +37 -0
  18. package/dist/theme/index.js.map +1 -1
  19. package/dist/theme/index.mjs +1 -1
  20. package/package.json +1 -1
  21. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +10 -15
  22. package/src/components/custom/Workout/BodyMap.tsx +7 -8
  23. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +25 -28
  24. package/src/components/custom/Workout/CapacityBandChart.tsx +2 -3
  25. package/src/components/custom/Workout/ExerciseCard.tsx +12 -27
  26. package/src/components/custom/Workout/ExerciseDetailPage.tsx +21 -27
  27. package/src/components/custom/Workout/InputBar.tsx +10 -11
  28. package/src/components/custom/Workout/MesoCard.tsx +2 -6
  29. package/src/components/custom/Workout/MesoStatusCard.tsx +17 -15
  30. package/src/components/custom/Workout/MuscleGroupChip.tsx +2 -4
  31. package/src/components/custom/Workout/PlaceholderStrip.tsx +2 -3
  32. package/src/components/custom/Workout/PrHistoryModal.tsx +12 -14
  33. package/src/components/custom/Workout/ProgramPlanningPage.tsx +8 -12
  34. package/src/components/custom/Workout/ReadinessCheck.tsx +13 -11
  35. package/src/components/custom/Workout/RestTimer.tsx +8 -7
  36. package/src/components/custom/Workout/SetRow.tsx +10 -7
  37. package/src/components/custom/Workout/StrengthTrendChart.tsx +15 -17
  38. package/src/components/custom/Workout/TempoBar.stories.tsx +95 -0
  39. package/src/components/custom/Workout/TempoBar.test.tsx +103 -0
  40. package/src/components/custom/Workout/TempoBar.tsx +215 -0
  41. package/src/components/custom/Workout/TrainingStatusPage.tsx +8 -17
  42. package/src/components/custom/Workout/VelocityStrip.tsx +7 -5
  43. package/src/components/custom/Workout/WeekRow.tsx +2 -1
  44. package/src/components/custom/Workout/WorkoutCard.tsx +3 -8
  45. package/src/components/custom/Workout/index.ts +9 -0
  46. package/src/test/nativewind-stub.ts +13 -0
  47. package/src/theme/ThemeProvider.test.tsx +24 -0
  48. package/src/theme/ThemeProvider.tsx +50 -0
  49. package/src/theme/index.ts +2 -0
  50. package/src/theme/resolve-color.ts +19 -0
  51. package/dist/chunk-P7TSQUN6.mjs.map +0 -1
  52. package/dist/chunk-TOD2WQBG.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- export { applyThemePreset, audiobookPreset, componentElevationRanges, createFlatShadow, createPressedHoverShadow, createPressedShadow, createRaisedHoverShadow, createRaisedShadow, darken, defaultPreset, elevationSystem, getBaseSurfaceColor, getElevationConfig, getElevationShadow, getElevationSurface, getGlowShadow, getHoverColors, getValidatedElevation, hexToRgb, hsvToRgb, isCssPropertyManifest, isDark, lighten, neumorphicShadows, rgbToHex, rgbToHsv, shadowColors, validateCssPropertyManifest } from '../chunk-P7TSQUN6.mjs';
1
+ export { ThemeProvider, applyThemePreset, audiobookPreset, componentElevationRanges, createFlatShadow, createPressedHoverShadow, createPressedShadow, createRaisedHoverShadow, createRaisedShadow, darken, defaultPreset, elevationSystem, getBaseSurfaceColor, getElevationConfig, getElevationShadow, getElevationSurface, getGlowShadow, getHoverColors, getValidatedElevation, hexToRgb, hsvToRgb, isCssPropertyManifest, isDark, lighten, neumorphicShadows, resolveColor, rgbToHex, rgbToHsv, shadowColors, validateCssPropertyManifest } from '../chunk-SNVKL5WZ.mjs';
2
2
  export { buttonSizes, darkThemeCSSVars, discreteRainbow, getDiscreteRainbowColor, getSemanticColors, getThemeCSSVars, gluestackConfig, lightThemeCSSVars, primitiveBorderRadius, primitiveBreakpoints, primitiveColors, primitiveShadows, primitiveSpacing, primitiveTypography, primitiveZIndex, semanticColorsDark, semanticColorsLight, semanticTypography } from '../chunk-7XPB4NAO.mjs';
3
3
  //# sourceMappingURL=index.mjs.map
4
4
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@titan-design/react-ui",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Cross-platform design system built on Gluestack UI",
5
5
  "author": "Henry Jewkes",
6
6
  "license": "MIT",
@@ -6,14 +6,9 @@ import { InputBar } from './InputBar'
6
6
  import { RestTimer } from './RestTimer'
7
7
  import { SupersetWrapper } from './SupersetWrapper'
8
8
  import { type SetRowProps } from './SetRow'
9
+ import { resolveColor } from '../../../theme/resolve-color'
9
10
 
10
- const SURFACE_ELEVATED = 'var(--color-surface-elevated)'
11
- const BORDER_DEFAULT = 'var(--color-border-default)'
12
11
  const BRAND_PRIMARY = '#FF7900'
13
- const TEXT_PRIMARY = 'var(--color-text-primary)'
14
- const TEXT_SECONDARY = 'var(--color-text-secondary)'
15
- const TEXT_TERTIARY = 'var(--color-text-tertiary)'
16
- const PAGE_BG = 'var(--color-background-base)'
17
12
 
18
13
  /** Where an exercise sits in the during-workout flow. */
19
14
  export type ActiveExerciseStatus = 'completed' | 'active' | 'upcoming'
@@ -232,11 +227,11 @@ function WorkoutHeader({
232
227
  <View style={{ flexShrink: 1 }}>
233
228
  <Text
234
229
  accessibilityRole="header"
230
+ className="text-text-primary"
235
231
  style={{
236
232
  fontSize: 20,
237
233
  fontFamily: '"Space Grotesk", sans-serif',
238
234
  fontWeight: '700',
239
- color: TEXT_PRIMARY,
240
235
  }}
241
236
  testID="active-workout-page-title"
242
237
  >
@@ -244,11 +239,11 @@ function WorkoutHeader({
244
239
  </Text>
245
240
  {subtitle != null && (
246
241
  <Text
242
+ className="text-text-secondary"
247
243
  style={{
248
244
  marginTop: 2,
249
245
  fontSize: 12,
250
246
  fontFamily: 'Inter, sans-serif',
251
- color: TEXT_SECONDARY,
252
247
  }}
253
248
  testID="active-workout-page-subtitle"
254
249
  >
@@ -269,13 +264,13 @@ function WorkoutHeader({
269
264
  {`${progress.completedExercises}/${progress.totalExercises}`}
270
265
  </Text>
271
266
  <Text
267
+ className="text-text-tertiary"
272
268
  style={{
273
269
  fontSize: 10,
274
270
  fontFamily: 'Inter, sans-serif',
275
271
  fontWeight: '600',
276
272
  letterSpacing: 0.4,
277
273
  textTransform: 'uppercase',
278
- color: TEXT_TERTIARY,
279
274
  }}
280
275
  >
281
276
  {`${progress.completedSets}/${progress.totalSets} sets`}
@@ -283,7 +278,8 @@ function WorkoutHeader({
283
278
  </View>
284
279
  </View>
285
280
  <View
286
- style={{ height: 4, backgroundColor: BORDER_DEFAULT, borderRadius: 2 }}
281
+ className="bg-border"
282
+ style={{ height: 4, borderRadius: 2 }}
287
283
  testID="active-workout-page-progress-track"
288
284
  >
289
285
  <View
@@ -314,10 +310,9 @@ function ExerciseList({ groups, focusedId, onToggle }: ExerciseListProps) {
314
310
  return (
315
311
  <View
316
312
  key={group.exercise.id}
313
+ className="bg-surface-elevated border-border"
317
314
  style={{
318
- backgroundColor: SURFACE_ELEVATED,
319
315
  borderWidth: 1,
320
- borderColor: BORDER_DEFAULT,
321
316
  borderRadius: 12,
322
317
  }}
323
318
  >
@@ -395,8 +390,8 @@ export function ActiveWorkoutPage({
395
390
 
396
391
  return (
397
392
  <View
398
- className={className}
399
- style={{ position: 'relative', width: 390, backgroundColor: PAGE_BG }}
393
+ className={['bg-background-base', className].filter(Boolean).join(' ')}
394
+ style={{ position: 'relative', width: 390 }}
400
395
  accessibilityRole={'main' as ViewProps['accessibilityRole']}
401
396
  aria-label={title}
402
397
  testID="active-workout-page"
@@ -409,7 +404,7 @@ export function ActiveWorkoutPage({
409
404
 
410
405
  {(showRest || showInput) && (
411
406
  <View
412
- style={{ borderTopWidth: 1, borderTopColor: BORDER_DEFAULT }}
407
+ style={{ borderTopWidth: 1, borderTopColor: resolveColor('border-default') }}
413
408
  testID="active-workout-page-bottom-bar"
414
409
  >
415
410
  {showRest && rest != null ? (
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react'
3
3
  import { View, Text, Pressable, Animated, Easing, type ViewProps } from 'react-native'
4
4
  import BodyHighlighter, { type ExtendedBodyPart, type Slug } from 'react-native-body-highlighter'
5
5
  import { cn } from '../../../utils/cn'
6
+ import { resolveColor } from '../../../theme/resolve-color'
6
7
  import {
7
8
  MuscleGroup,
8
9
  SimpleMuscleGroup,
@@ -27,8 +28,6 @@ const Body = ((BodyHighlighter as unknown as { default?: typeof BodyHighlighter
27
28
  const OUTLINE_FILL = 'rgba(255,255,255,0.08)'
28
29
  const OUTLINE_BORDER = 'rgba(255,255,255,0.12)'
29
30
  const BODY_SCALE = 0.8 // 200x400 intrinsic -> ~160x320 px
30
- const TEXT_PRIMARY = 'var(--color-text-primary)'
31
- const TEXT_SECONDARY = 'var(--color-text-secondary)'
32
31
 
33
32
  export interface BodyMapData {
34
33
  muscleGroup: MuscleGroup
@@ -217,10 +216,10 @@ export function BodyMap({
217
216
 
218
217
  {legend.length === 0 ? (
219
218
  <Text
219
+ className="text-text-secondary"
220
220
  style={{
221
221
  marginTop: 8,
222
222
  fontSize: 12,
223
- color: TEXT_SECONDARY,
224
223
  textAlign: 'center',
225
224
  fontFamily: 'Inter, sans-serif',
226
225
  }}
@@ -276,7 +275,7 @@ function ViewToggle({ view, onViewChange }: ViewToggleProps) {
276
275
  borderRadius: 9999,
277
276
  backgroundColor: active ? 'rgba(255,121,0,0.16)' : 'transparent',
278
277
  borderWidth: 1,
279
- borderColor: active ? '#FF7900' : 'var(--color-border-strong)',
278
+ borderColor: active ? '#FF7900' : resolveColor('border-strong'),
280
279
  }}
281
280
  testID={`body-map-toggle-${side}`}
282
281
  >
@@ -285,7 +284,7 @@ function ViewToggle({ view, onViewChange }: ViewToggleProps) {
285
284
  fontSize: 12,
286
285
  fontFamily: 'Inter, sans-serif',
287
286
  fontWeight: active ? '700' : '500',
288
- color: active ? '#FF7900' : TEXT_SECONDARY,
287
+ color: active ? '#FF7900' : resolveColor('text-secondary'),
289
288
  textTransform: 'capitalize',
290
289
  }}
291
290
  >
@@ -313,6 +312,7 @@ function MuscleButton({ entry, highlighted, onMusclePress }: MuscleButtonProps)
313
312
  accessibilityRole="button"
314
313
  accessibilityLabel={label}
315
314
  aria-pressed={highlighted}
315
+ className="bg-surface-raised"
316
316
  style={{
317
317
  flexDirection: 'row',
318
318
  alignItems: 'center',
@@ -321,8 +321,7 @@ function MuscleButton({ entry, highlighted, onMusclePress }: MuscleButtonProps)
321
321
  paddingVertical: 3,
322
322
  borderRadius: 9999,
323
323
  borderWidth: 1,
324
- borderColor: highlighted ? '#FF7900' : 'var(--color-border-default)',
325
- backgroundColor: 'var(--color-surface-raised)',
324
+ borderColor: highlighted ? '#FF7900' : resolveColor('border-default'),
326
325
  }}
327
326
  testID={`body-map-muscle-${entry.key}`}
328
327
  >
@@ -332,11 +331,11 @@ function MuscleButton({ entry, highlighted, onMusclePress }: MuscleButtonProps)
332
331
  testID={`body-map-muscle-dot-${entry.key}`}
333
332
  />
334
333
  <Text
334
+ className="text-text-primary"
335
335
  style={{
336
336
  fontSize: 11,
337
337
  fontFamily: 'Inter, sans-serif',
338
338
  fontWeight: '500',
339
- color: TEXT_PRIMARY,
340
339
  }}
341
340
  accessibilityElementsHidden
342
341
  >
@@ -19,14 +19,7 @@ import {
19
19
  type VolumeStatus,
20
20
  } from './muscleTaxonomy'
21
21
 
22
- const SURFACE_ELEVATED = 'var(--color-surface-elevated)'
23
- const SURFACE_RAISED = 'var(--color-surface-raised)'
24
- const BORDER_STRONG = 'var(--color-border-strong)'
25
- const BORDER_DEFAULT = 'var(--color-border-default)'
26
22
  const BRAND_PRIMARY = '#FF7900'
27
- const TEXT_PRIMARY = 'var(--color-text-primary)'
28
- const TEXT_SECONDARY = 'var(--color-text-secondary)'
29
- const TEXT_TERTIARY = 'var(--color-text-tertiary)'
30
23
 
31
24
  /** Volume track gradient: status-info (blue) -> result-improve (teal) -> status-error (red). */
32
25
  const VOLUME_GRADIENT = 'linear-gradient(90deg, #2196F3 0%, #14B8A6 50%, #D14343 100%)'
@@ -205,8 +198,8 @@ export function BodyMapDetailPanel({
205
198
  accessibilityRole={'dialog' as ViewProps['accessibilityRole']}
206
199
  accessibilityLabel={dialogLabel}
207
200
  aria-label={dialogLabel}
201
+ className="bg-surface-elevated"
208
202
  style={{
209
- backgroundColor: SURFACE_ELEVATED,
210
203
  borderTopLeftRadius: 16,
211
204
  borderTopRightRadius: 16,
212
205
  maxHeight: '88%',
@@ -224,7 +217,8 @@ export function BodyMapDetailPanel({
224
217
  testID="body-map-detail-panel-handle"
225
218
  >
226
219
  <View
227
- style={{ width: 40, height: 4, borderRadius: 2, backgroundColor: BORDER_STRONG }}
220
+ className="bg-border-strong"
221
+ style={{ width: 40, height: 4, borderRadius: 2 }}
228
222
  accessibilityElementsHidden
229
223
  />
230
224
  </Pressable>
@@ -242,11 +236,11 @@ export function BodyMapDetailPanel({
242
236
  <View className="flex-row items-center" style={{ gap: 8, flexShrink: 1 }}>
243
237
  <Text
244
238
  accessibilityRole="header"
239
+ className="text-text-primary"
245
240
  style={{
246
241
  fontSize: 16,
247
242
  fontFamily: '"Space Grotesk", sans-serif',
248
243
  fontWeight: '700',
249
- color: TEXT_PRIMARY,
250
244
  }}
251
245
  testID="body-map-detail-panel-title"
252
246
  >
@@ -269,13 +263,14 @@ export function BodyMapDetailPanel({
269
263
  style={{ padding: 4, margin: -4 }}
270
264
  testID="body-map-detail-panel-close"
271
265
  >
272
- <Text style={{ fontSize: 22, lineHeight: 22, color: TEXT_SECONDARY }}>{'×'}</Text>
266
+ <Text className="text-text-secondary" style={{ fontSize: 22, lineHeight: 22 }}>{'×'}</Text>
273
267
  </Pressable>
274
268
  </View>
275
269
 
276
270
  {lastTrained != null && (
277
271
  <Text
278
- style={{ fontSize: 12, fontFamily: 'Inter, sans-serif', color: TEXT_SECONDARY }}
272
+ className="text-text-secondary"
273
+ style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
279
274
  testID="body-map-detail-panel-last-trained"
280
275
  >
281
276
  {`Last trained ${lastTrained}`}
@@ -288,10 +283,10 @@ export function BodyMapDetailPanel({
288
283
  style={{ marginBottom: 6 }}
289
284
  accessibilityElementsHidden
290
285
  >
291
- <Text style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600', color: TEXT_TERTIARY }}>
286
+ <Text className="text-text-tertiary" style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}>
292
287
  {`MEV ${landmarks.mev}`}
293
288
  </Text>
294
- <Text style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600', color: TEXT_TERTIARY }}>
289
+ <Text className="text-text-tertiary" style={{ fontSize: 10, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}>
295
290
  {`MRV ${landmarks.mrv}`}
296
291
  </Text>
297
292
  </View>
@@ -310,6 +305,7 @@ export function BodyMapDetailPanel({
310
305
  accessibilityElementsHidden
311
306
  />
312
307
  <View
308
+ className="border-text-primary bg-surface-elevated"
313
309
  style={{
314
310
  position: 'absolute',
315
311
  left: `${markerLeft}%`,
@@ -318,8 +314,6 @@ export function BodyMapDetailPanel({
318
314
  height: 14,
319
315
  borderRadius: 7,
320
316
  borderWidth: 2,
321
- borderColor: TEXT_PRIMARY,
322
- backgroundColor: SURFACE_ELEVATED,
323
317
  boxShadow: '0 0 4px rgba(0,0,0,0.5)',
324
318
  }}
325
319
  accessibilityElementsHidden
@@ -330,18 +324,19 @@ export function BodyMapDetailPanel({
330
324
 
331
325
  <View className="flex-row items-baseline" style={{ marginTop: 14, gap: 4 }}>
332
326
  <Text
327
+ className="text-text-primary"
333
328
  style={{
334
329
  fontSize: 24,
335
330
  fontFamily: '"Space Grotesk", sans-serif',
336
331
  fontWeight: '700',
337
- color: TEXT_PRIMARY,
338
332
  }}
339
333
  testID="body-map-detail-panel-set-count"
340
334
  >
341
335
  {weeklySets}
342
336
  </Text>
343
337
  <Text
344
- style={{ fontSize: 12, fontFamily: 'Inter, sans-serif', color: TEXT_SECONDARY }}
338
+ className="text-text-secondary"
339
+ style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
345
340
  testID="body-map-detail-panel-mrv-suffix"
346
341
  >
347
342
  {`/ ${landmarks.mrv} MRV`}
@@ -351,11 +346,11 @@ export function BodyMapDetailPanel({
351
346
  {weeklyHistory != null && weeklyHistory.length > 0 && (
352
347
  <View style={{ marginTop: 14 }} testID="body-map-detail-panel-sparkline">
353
348
  <Text
349
+ className="text-text-tertiary"
354
350
  style={{
355
351
  fontSize: 10,
356
352
  fontFamily: 'Inter, sans-serif',
357
353
  fontWeight: '600',
358
- color: TEXT_TERTIARY,
359
354
  marginBottom: 6,
360
355
  }}
361
356
  accessibilityElementsHidden
@@ -369,11 +364,11 @@ export function BodyMapDetailPanel({
369
364
  {hasContributing && (
370
365
  <View style={{ marginTop: 16 }} testID="body-map-detail-panel-contributing">
371
366
  <Text
367
+ className="text-text-tertiary"
372
368
  style={{
373
369
  fontSize: 10,
374
370
  fontFamily: 'Inter, sans-serif',
375
371
  fontWeight: '600',
376
- color: TEXT_TERTIARY,
377
372
  marginBottom: 8,
378
373
  }}
379
374
  >
@@ -382,27 +377,27 @@ export function BodyMapDetailPanel({
382
377
  {contributingExercises!.map((exercise, index) => (
383
378
  <View
384
379
  key={`${exercise.name}-${index}`}
385
- className="flex-row items-center justify-between"
380
+ className="flex-row items-center justify-between bg-surface-raised border-border"
386
381
  style={{
387
382
  gap: 10,
388
383
  paddingVertical: 8,
389
384
  paddingHorizontal: 12,
390
385
  marginBottom: 6,
391
386
  borderRadius: 8,
392
- backgroundColor: SURFACE_RAISED,
393
387
  borderWidth: 1,
394
- borderColor: BORDER_DEFAULT,
395
388
  }}
396
389
  testID={`body-map-detail-panel-contributing-${index}`}
397
390
  >
398
391
  <Text
399
- style={{ flex: 1, fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: '600', color: TEXT_PRIMARY }}
392
+ className="text-text-primary"
393
+ style={{ flex: 1, fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}
400
394
  testID={`body-map-detail-panel-contributing-${index}-name`}
401
395
  >
402
396
  {exercise.name}
403
397
  </Text>
404
398
  <Text
405
- style={{ fontSize: 12, fontFamily: 'Inter, sans-serif', color: TEXT_SECONDARY }}
399
+ className="text-text-secondary"
400
+ style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
406
401
  testID={`body-map-detail-panel-contributing-${index}-detail`}
407
402
  >
408
403
  {`${exercise.sets} sets · ${Math.round(exercise.contributionWeight * 100)}%`}
@@ -415,11 +410,11 @@ export function BodyMapDetailPanel({
415
410
  {hasUpcoming && (
416
411
  <View style={{ marginTop: 16 }} testID="body-map-detail-panel-upcoming">
417
412
  <Text
413
+ className="text-text-tertiary"
418
414
  style={{
419
415
  fontSize: 10,
420
416
  fontFamily: 'Inter, sans-serif',
421
417
  fontWeight: '600',
422
- color: TEXT_TERTIARY,
423
418
  marginBottom: 8,
424
419
  }}
425
420
  >
@@ -433,13 +428,15 @@ export function BodyMapDetailPanel({
433
428
  testID={`body-map-detail-panel-upcoming-${index}`}
434
429
  >
435
430
  <Text
436
- style={{ flex: 1, fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: '600', color: TEXT_PRIMARY }}
431
+ className="text-text-primary"
432
+ style={{ flex: 1, fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: '600' }}
437
433
  testID={`body-map-detail-panel-upcoming-${index}-name`}
438
434
  >
439
435
  {exercise.name}
440
436
  </Text>
441
437
  <Text
442
- style={{ fontSize: 12, fontFamily: 'Inter, sans-serif', color: TEXT_TERTIARY }}
438
+ className="text-text-tertiary"
439
+ style={{ fontSize: 12, fontFamily: 'Inter, sans-serif' }}
443
440
  testID={`body-map-detail-panel-upcoming-${index}-detail`}
444
441
  >
445
442
  {`${exercise.workoutName} · ${exercise.sets} sets`}
@@ -8,7 +8,6 @@ const STATUS_INFO = '#2196F3'
8
8
  /** Dot outline: near-white ring so load dots read on the band fill and any
9
9
  * surface (matches the MesoStatusCard gauge-marker convention). */
10
10
  const DOT_BORDER = '#F3F4F6'
11
- const TEXT_TERTIARY = 'var(--color-text-tertiary)'
12
11
  const BAND_FILL = 'rgba(20,184,166,0.1)'
13
12
  const BAND_EDGE = 'rgba(20,184,166,0.45)'
14
13
  const PROJECTION_FILL = 'rgba(20,184,166,0.05)'
@@ -398,6 +397,7 @@ export function CapacityBandChart({
398
397
 
399
398
  {/* Y axis conceptual label (no numbers). */}
400
399
  <Text
400
+ className="text-text-tertiary"
401
401
  style={{
402
402
  position: 'absolute',
403
403
  left: PADDING_LEFT / 2 - 14,
@@ -406,7 +406,6 @@ export function CapacityBandChart({
406
406
  textAlign: 'center',
407
407
  fontSize: 9,
408
408
  fontFamily: 'Inter, sans-serif',
409
- color: TEXT_TERTIARY,
410
409
  transform: [{ rotate: '-90deg' }],
411
410
  }}
412
411
  accessibilityElementsHidden
@@ -422,6 +421,7 @@ export function CapacityBandChart({
422
421
  return (
423
422
  <Text
424
423
  key={`x-${point.date}`}
424
+ className="text-text-tertiary"
425
425
  style={{
426
426
  position: 'absolute',
427
427
  left: toX(point.date) - 14,
@@ -430,7 +430,6 @@ export function CapacityBandChart({
430
430
  textAlign: 'center',
431
431
  fontSize: 10,
432
432
  fontFamily: 'Inter, sans-serif',
433
- color: TEXT_TERTIARY,
434
433
  }}
435
434
  accessibilityElementsHidden
436
435
  importantForAccessibility="no-hide-descendants"
@@ -8,6 +8,7 @@ import { PrBadge } from './PrBadge'
8
8
  import { TempoDisplay } from './TempoDisplay'
9
9
  import { SetRow, type SetRowProps } from './SetRow'
10
10
  import { roundWeight } from '../../../utils/workout-format'
11
+ import { resolveColor } from '../../../theme/resolve-color'
11
12
 
12
13
  export type ExerciseCardState = 'collapsed' | 'expanded' | 'upcoming'
13
14
 
@@ -34,21 +35,6 @@ export interface ExerciseCardProps {
34
35
  supersetColor?: string
35
36
  }
36
37
 
37
- const COLORS = {
38
- // Theme-aware tokens: text foregrounds and this card's own surfaces/borders
39
- // must flip together so text stays readable in both light and dark. The
40
- // expanded/pressed surfaces are self-owned here (not from a themed Card), so
41
- // hardcoding dark values would leave dark-on-dark once the text flips.
42
- // react-native-web passes the CSS var through to the inline style.
43
- textPrimary: 'var(--color-text-primary)',
44
- textSecondary: 'var(--color-text-secondary)',
45
- textTertiary: 'var(--color-text-tertiary)',
46
- brandPrimary: '#FF7900',
47
- surfaceRaised: 'var(--color-surface-raised)',
48
- surfaceElevated: 'var(--color-surface-elevated)',
49
- borderDefault: 'var(--color-border-default)',
50
- }
51
-
52
38
  /** Column headers; the weight column reflects the card's unit (LBS / KG). */
53
39
  function buildColumnHeaders(unit: 'lbs' | 'kg'): string[] {
54
40
  return ['SET', 'PREV', 'REPS', unit.toUpperCase(), 'RPE']
@@ -139,11 +125,11 @@ function CollapsedCard({
139
125
  testID="exercise-card"
140
126
  >
141
127
  <View
128
+ className={pressed ? 'bg-surface-raised' : undefined}
142
129
  style={{
143
130
  padding: 12,
144
131
  paddingHorizontal: 14,
145
132
  cursor: 'pointer',
146
- backgroundColor: pressed ? COLORS.surfaceRaised : 'transparent',
147
133
  ...borderRadius,
148
134
  ...supersetMargin,
149
135
  }}
@@ -153,11 +139,11 @@ function CollapsedCard({
153
139
  testID="exercise-card-header"
154
140
  >
155
141
  <Text
142
+ className="text-text-primary"
156
143
  style={{
157
144
  fontSize: 14,
158
145
  fontFamily: '"Space Grotesk", sans-serif',
159
146
  fontWeight: '700',
160
- color: COLORS.textPrimary,
161
147
  }}
162
148
  testID="exercise-card-name"
163
149
  >
@@ -166,10 +152,10 @@ function CollapsedCard({
166
152
  <View className="flex-1" />
167
153
  {summary && (
168
154
  <Text
155
+ className="text-text-secondary"
169
156
  style={{
170
157
  fontSize: 12,
171
158
  fontFamily: 'Inter, sans-serif',
172
- color: COLORS.textSecondary,
173
159
  marginRight: 8,
174
160
  }}
175
161
  testID="exercise-card-summary"
@@ -239,10 +225,9 @@ function ExpandedCard({
239
225
 
240
226
  return (
241
227
  <View
228
+ className="bg-surface-elevated border-border"
242
229
  style={{
243
- backgroundColor: COLORS.surfaceElevated,
244
230
  borderWidth: 1,
245
- borderColor: COLORS.borderDefault,
246
231
  ...borderRadius,
247
232
  ...supersetMargin,
248
233
  }}
@@ -262,15 +247,15 @@ function ExpandedCard({
262
247
  paddingHorizontal: 14,
263
248
  paddingBottom: 10,
264
249
  borderBottomWidth: 1,
265
- borderBottomColor: COLORS.borderDefault,
250
+ borderBottomColor: resolveColor('border-default'),
266
251
  }}
267
252
  >
268
253
  <Text
254
+ className="text-text-primary"
269
255
  style={{
270
256
  fontSize: 14,
271
257
  fontFamily: '"Space Grotesk", sans-serif',
272
258
  fontWeight: '700',
273
- color: COLORS.textPrimary,
274
259
  }}
275
260
  testID="exercise-card-name"
276
261
  >
@@ -279,10 +264,10 @@ function ExpandedCard({
279
264
  <View className="flex-1" />
280
265
  {summary && (
281
266
  <Text
267
+ className="text-text-secondary"
282
268
  style={{
283
269
  fontSize: 12,
284
270
  fontFamily: 'Inter, sans-serif',
285
- color: COLORS.textSecondary,
286
271
  marginRight: 8,
287
272
  }}
288
273
  testID="exercise-card-summary"
@@ -339,13 +324,13 @@ function ExpandedCard({
339
324
  {...(isFlex ? { className: 'flex-1' } : {})}
340
325
  >
341
326
  <Text
327
+ className="text-text-tertiary"
342
328
  style={{
343
329
  fontSize: 10,
344
330
  fontWeight: '600',
345
331
  fontFamily: 'Inter, sans-serif',
346
332
  textTransform: 'uppercase',
347
333
  letterSpacing: 0.8,
348
- color: COLORS.textTertiary,
349
334
  }}
350
335
  >
351
336
  {header}
@@ -389,11 +374,11 @@ function UpcomingCard({
389
374
  >
390
375
  <View className="flex-row items-center">
391
376
  <Text
377
+ className="text-text-primary"
392
378
  style={{
393
379
  fontSize: 14,
394
380
  fontFamily: '"Space Grotesk", sans-serif',
395
381
  fontWeight: '700',
396
- color: COLORS.textPrimary,
397
382
  }}
398
383
  testID="exercise-card-name"
399
384
  >
@@ -401,10 +386,10 @@ function UpcomingCard({
401
386
  </Text>
402
387
  {prescription && (
403
388
  <Text
389
+ className="text-text-secondary"
404
390
  style={{
405
391
  fontSize: 12,
406
392
  fontFamily: 'Inter, sans-serif',
407
- color: COLORS.textSecondary,
408
393
  marginLeft: 8,
409
394
  }}
410
395
  testID="exercise-card-prescription"
@@ -415,10 +400,10 @@ function UpcomingCard({
415
400
  <View className="flex-1" />
416
401
  {previousBest && (
417
402
  <Text
403
+ className="text-text-tertiary"
418
404
  style={{
419
405
  fontSize: 11,
420
406
  fontFamily: 'Inter, sans-serif',
421
- color: COLORS.textTertiary,
422
407
  }}
423
408
  testID="exercise-card-previous-best"
424
409
  >