@titan-design/react-ui 0.11.0 → 0.12.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.
- package/dist/bodymap.js +173 -343
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +173 -343
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +24 -21
- package/dist/index.d.ts +24 -21
- package/dist/index.js +366 -561
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +360 -553
- package/dist/index.mjs.map +1 -1
- package/dist/pages.js +242 -407
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +242 -407
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +409 -415
- package/dist/theme/index.d.ts +409 -415
- package/dist/theme/index.js +234 -408
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +227 -399
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +141 -140
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +141 -140
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +9 -9
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +3 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +3 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +1 -1
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +2 -2
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +4 -4
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +3 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -12
- package/src/components/custom/Sidebar/Sidebar.tsx +4 -4
- package/src/components/custom/Table/Table.tsx +2 -2
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +3 -3
- package/src/components/custom/Workout/BaseBadge.test.tsx +6 -1
- package/src/components/custom/Workout/BaseBadge.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.tsx +2 -2
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +2 -2
- package/src/components/custom/Workout/DeviationBar.test.tsx +2 -1
- package/src/components/custom/Workout/DeviationBar.tsx +3 -2
- package/src/components/custom/Workout/ExerciseCard.test.tsx +3 -3
- package/src/components/custom/Workout/ExerciseCard.tsx +2 -2
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -4
- package/src/components/custom/Workout/InputBar.tsx +2 -2
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.tsx +3 -3
- package/src/components/custom/Workout/MesoCard.tsx +3 -1
- package/src/components/custom/Workout/MuscleGroupChip.tsx +4 -3
- package/src/components/custom/Workout/PrHistoryModal.tsx +2 -2
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +1 -1
- package/src/components/custom/Workout/ReadinessCheck.tsx +2 -2
- package/src/components/custom/Workout/RestTimer.tsx +2 -2
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +3 -3
- package/src/components/custom/Workout/SessionHeader.stories.tsx +2 -2
- package/src/components/custom/Workout/SessionHeader.tsx +1 -1
- package/src/components/custom/Workout/SessionRail.stories.tsx +2 -2
- package/src/components/custom/Workout/SessionRail.tsx +13 -5
- package/src/components/custom/Workout/SetBar.tsx +3 -3
- package/src/components/custom/Workout/SetRow.test.tsx +6 -3
- package/src/components/custom/Workout/StatusDot.tsx +5 -4
- package/src/components/custom/Workout/StrengthTrendChart.tsx +1 -1
- package/src/components/custom/Workout/TrainingStatusPage.tsx +2 -2
- package/src/components/custom/Workout/VelocityStrip.test.tsx +5 -2
- package/src/components/custom/Workout/VelocityStrip.tsx +4 -4
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +3 -3
- package/src/components/custom/Workout/WeightBadge.test.tsx +6 -1
- package/src/components/custom/Workout/WeightBadge.tsx +2 -1
- package/src/components/custom/Workout/WorkoutCard.tsx +3 -1
- package/src/components/custom/Workout/WorkoutPill.tsx +5 -2
- package/src/components/custom/Workout/ZoneTrack.tsx +4 -4
- package/src/components/custom/Workout/setHeadingKit.tsx +14 -8
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/SetBarChart.tsx +3 -3
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/custom/stepper/Stepper.tsx +3 -3
- package/src/components/shell/DeviceMenu.tsx +2 -2
- package/src/components/shell/SideNav.tsx +1 -1
- package/src/components/shell/TopBar.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -2
- package/src/components/ui/badge/Badge.tsx +2 -2
- package/src/components/ui/card/Card.tsx +3 -3
- package/src/components/ui/checkbox/Checkbox.tsx +1 -1
- package/src/components/ui/collapse/Collapse.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.tsx +3 -3
- package/src/components/ui/form-field/FormField.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.tsx +1 -1
- package/src/components/ui/menu/Menu.tsx +2 -2
- package/src/components/ui/pill/Pill.tsx +2 -2
- package/src/components/ui/popover/Popover.stories.tsx +2 -2
- package/src/components/ui/popover/Popover.tsx +1 -1
- package/src/components/ui/progress/Progress.tsx +3 -3
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.tsx +4 -4
- package/src/components/ui/select/Select.tsx +3 -3
- package/src/components/ui/surface/Surface.stories.tsx +3 -3
- package/src/components/ui/surface/Surface.test.tsx +32 -19
- package/src/components/ui/surface/Surface.tsx +3 -3
- package/src/components/ui/surface/SurfaceContext.ts +19 -29
- package/src/components/ui/surface/surface.contract.test.ts +57 -44
- package/src/components/ui/switch/Switch.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +2 -2
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +5 -5
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +36 -22
- package/src/stories/PresetShowcase.stories.tsx +21 -21
- package/src/theme/ColorPalettes.stories.tsx +41 -38
- package/src/theme/ColorPrimitives.stories.tsx +89 -41
- package/src/theme/Depth.stories.tsx +312 -0
- package/src/theme/DepthCalibration.stories.tsx +655 -0
- package/src/theme/ThemeProvider.tsx +1 -1
- package/src/theme/barrel.ts +7 -14
- package/src/theme/color-stories.coverage.test.ts +15 -20
- package/src/theme/color-story-kit.tsx +1 -1
- package/src/theme/color-utils.ts +197 -0
- package/src/theme/config.ts +0 -8
- package/src/theme/depth-calibration.test.tsx +97 -0
- package/src/theme/elevation.test.ts +62 -1
- package/src/theme/elevation.ts +34 -8
- package/src/theme/global.css +47 -53
- package/src/theme/grey-ramp.test.ts +166 -0
- package/src/theme/materials.test.ts +106 -0
- package/src/theme/materials.ts +211 -0
- package/src/theme/presets/audiobook.ts +4 -4
- package/src/theme/presets/types.ts +3 -3
- package/src/theme/semantic-contrast.test.ts +83 -0
- package/src/theme/tokens/primitives.ts +62 -115
- package/src/theme/tokens/semantic.ts +130 -82
- package/src/utils/colors.ts +1 -1
- package/tailwind.config.js +2 -5
- package/src/theme/bar-paper.ts +0 -59
- package/src/theme/shadows.stories.tsx +0 -523
- package/src/theme/shadows.ts +0 -439
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
10
10
|
import { View, Text } from 'react-native'
|
|
11
|
-
import { primitiveColors } from '../../../theme/tokens/primitives'
|
|
11
|
+
import { primitiveColors, greyRamp } from '../../../theme/tokens/primitives'
|
|
12
12
|
import { alpha } from '../../../utils/colors'
|
|
13
13
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
14
14
|
import {
|
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
FONT_MONO,
|
|
24
24
|
} from './fatigue-tokens'
|
|
25
25
|
|
|
26
|
-
const PAGE_BG =
|
|
27
|
-
const PANEL_BG =
|
|
26
|
+
const PAGE_BG = greyRamp[975]
|
|
27
|
+
const PANEL_BG = greyRamp[950]
|
|
28
28
|
const t = getSemanticColors('dark')
|
|
29
29
|
|
|
30
30
|
const meta: Meta = {
|
|
@@ -161,7 +161,7 @@ const PHASE_AXIS: SwatchDef[] = [
|
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
name: 'Idle',
|
|
164
|
-
token: 'PHASE_AXIS_COLOR.idle ·
|
|
164
|
+
token: 'PHASE_AXIS_COLOR.idle · grey[300]',
|
|
165
165
|
value: PHASE_AXIS_COLOR.idle,
|
|
166
166
|
note: 'Pauses / holds on the phase band.',
|
|
167
167
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { View, Text } from 'react-native'
|
|
3
3
|
import { VelocityHero } from './VelocityHero'
|
|
4
|
-
import {
|
|
4
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
5
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
6
|
import { MOCK_MEAN_VELOCITIES } from './fatigue-mock'
|
|
7
7
|
|
|
8
|
-
const PAGE_BG =
|
|
9
|
-
const PANEL_BG =
|
|
8
|
+
const PAGE_BG = greyRamp[975]
|
|
9
|
+
const PANEL_BG = greyRamp[950]
|
|
10
10
|
const t = getSemanticColors('dark')
|
|
11
11
|
|
|
12
12
|
const meta: Meta<typeof VelocityHero> = {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { View, Text } from 'react-native'
|
|
3
3
|
import { VerdictHero } from './VerdictHero'
|
|
4
|
-
import {
|
|
4
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
5
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
6
|
import { FATIGUE_STATES, WARMING_UP_MODEL } from './fatigue-mock'
|
|
7
7
|
|
|
8
|
-
const PAGE_BG =
|
|
8
|
+
const PAGE_BG = greyRamp[975]
|
|
9
9
|
const t = getSemanticColors('dark')
|
|
10
10
|
|
|
11
11
|
const meta: Meta<typeof VerdictHero> = {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* the family inherits the surface-ramp refresh when it lands.
|
|
6
6
|
*/
|
|
7
7
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
8
|
-
import {
|
|
8
|
+
import { primitiveRamps, greyRamp } from '../../../theme/tokens/primitives'
|
|
9
9
|
import type { DimensionTone, FatigueVerdictState, SamplePhase } from './fatigue-model'
|
|
10
10
|
|
|
11
11
|
const t = getSemanticColors('dark')
|
|
@@ -34,9 +34,9 @@ export const STATE_LABEL: Record<FatigueVerdictState, string> = {
|
|
|
34
34
|
* language: eccentric magenta, concentric cyan, hold and idle in the grey family.
|
|
35
35
|
*
|
|
36
36
|
* The idle grey has to read as BAND MATERIAL (a pause the lifter held), not as a
|
|
37
|
-
* hole in the strip: at
|
|
37
|
+
* hole in the strip: at grey[300] it sat inside the surface-ramp's own value
|
|
38
38
|
* range (`surface-raised` #31302F) so a short pause between ECC and CON read as a
|
|
39
|
-
* gap.
|
|
39
|
+
* gap. grey[200] clears every content plane while staying quieter than the two
|
|
40
40
|
* saturated phase tones, so the band reads contiguous and the phases still lead.
|
|
41
41
|
*
|
|
42
42
|
* `hold` stays GREY rather than taking a hue of its own. It was briefly amber, which
|
|
@@ -48,17 +48,17 @@ export const STATE_LABEL: Record<FatigueVerdictState, string> = {
|
|
|
48
48
|
*
|
|
49
49
|
* Hold and idle are separated by VALUE rather than hue, plus the `HOLD` label and the
|
|
50
50
|
* fact that a hold PACES (it has a prescribed duration) while idle never fills:
|
|
51
|
-
* unfilled hold
|
|
52
|
-
* idle
|
|
53
|
-
* filled hold
|
|
51
|
+
* unfilled hold grey[300] — darker than idle
|
|
52
|
+
* idle grey[200]
|
|
53
|
+
* filled hold grey[100] — brighter than idle
|
|
54
54
|
* so a hold reads apart from dead time at any fill level, including the narrow runs
|
|
55
55
|
* where the label drops out.
|
|
56
56
|
*/
|
|
57
57
|
export const PHASE_AXIS_COLOR: Record<SamplePhase, string> = {
|
|
58
58
|
eccentric: primitiveRamps.magenta[800],
|
|
59
59
|
concentric: primitiveRamps.cyan[800],
|
|
60
|
-
hold:
|
|
61
|
-
idle:
|
|
60
|
+
hold: greyRamp[700],
|
|
61
|
+
idle: greyRamp[800],
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -72,8 +72,8 @@ export const PHASE_AXIS_COLOR: Record<SamplePhase, string> = {
|
|
|
72
72
|
export const PHASE_AXIS_BASE_COLOR: Record<SamplePhase, string> = {
|
|
73
73
|
eccentric: primitiveRamps.magenta[950],
|
|
74
74
|
concentric: primitiveRamps.cyan[950],
|
|
75
|
-
hold:
|
|
76
|
-
idle:
|
|
75
|
+
hold: greyRamp[900],
|
|
76
|
+
idle: greyRamp[800],
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
@@ -114,7 +114,7 @@ export const PACING_TONE_MIN_CONTRAST = 4.5
|
|
|
114
114
|
// SHADES OF RED when there's an issue. No greens, no ambers — those languages belong
|
|
115
115
|
// to the verdict tones / velocity-loss bands, not here.
|
|
116
116
|
/** On-track / at-or-above-working. */
|
|
117
|
-
export const SILVER =
|
|
117
|
+
export const SILVER = greyRamp[200]
|
|
118
118
|
export const RED_LIGHT = primitiveRamps.red[400]
|
|
119
119
|
export const RED_MID = primitiveRamps.red[600]
|
|
120
120
|
export const RED_DEEP = primitiveRamps.red[800]
|
|
@@ -124,7 +124,7 @@ export const GRIND_THRESHOLD = 0.35
|
|
|
124
124
|
/** A quiet grey the controlled line dims toward as it drifts (never a colour): a dimmed
|
|
125
125
|
* cool grey in the SAME neutral family as SILVER, so a fully-drifted line reads dim-silver
|
|
126
126
|
* rather than sinking toward black. Shared with the ROM chart. */
|
|
127
|
-
export const DRIFT_GREY =
|
|
127
|
+
export const DRIFT_GREY = greyRamp[700]
|
|
128
128
|
|
|
129
129
|
export function clamp01(v: number): number {
|
|
130
130
|
return Math.min(1, Math.max(0, v))
|
|
@@ -64,7 +64,7 @@ export function Sidebar({
|
|
|
64
64
|
<View
|
|
65
65
|
style={{ width: currentWidth }}
|
|
66
66
|
className={cn(
|
|
67
|
-
'h-full flex-col bg-surface-base border-r border-
|
|
67
|
+
'h-full flex-col bg-surface-base border-r border-hairline transition-all duration-200',
|
|
68
68
|
className
|
|
69
69
|
)}
|
|
70
70
|
{...props}
|
|
@@ -85,7 +85,7 @@ export interface SidebarHeaderProps {
|
|
|
85
85
|
*/
|
|
86
86
|
export function SidebarHeader({ children, className }: SidebarHeaderProps) {
|
|
87
87
|
return (
|
|
88
|
-
<View className={cn('px-4 py-4 border-b border-
|
|
88
|
+
<View className={cn('px-4 py-4 border-b border-hairline-subtle', className)}>
|
|
89
89
|
{children}
|
|
90
90
|
</View>
|
|
91
91
|
)
|
|
@@ -120,7 +120,7 @@ export interface SidebarFooterProps {
|
|
|
120
120
|
*/
|
|
121
121
|
export function SidebarFooter({ children, className }: SidebarFooterProps) {
|
|
122
122
|
return (
|
|
123
|
-
<View className={cn('px-2 py-4 border-t border-
|
|
123
|
+
<View className={cn('px-2 py-4 border-t border-hairline-subtle', className)}>
|
|
124
124
|
{children}
|
|
125
125
|
</View>
|
|
126
126
|
)
|
|
@@ -252,6 +252,6 @@ export interface SidebarDividerProps {
|
|
|
252
252
|
*/
|
|
253
253
|
export function SidebarDivider({ className }: SidebarDividerProps) {
|
|
254
254
|
return (
|
|
255
|
-
<View className={cn('h-px mx-4 my-2 bg-
|
|
255
|
+
<View className={cn('h-px mx-4 my-2 bg-hairline-subtle', className)} />
|
|
256
256
|
)
|
|
257
257
|
}
|
|
@@ -590,7 +590,7 @@ export function TableSelectAllCell({ className }: TableSelectAllCellProps) {
|
|
|
590
590
|
? 'bg-brand-primary border-brand-primary'
|
|
591
591
|
: someSelected
|
|
592
592
|
? 'bg-brand-primary-subtle border-brand-primary'
|
|
593
|
-
: 'border-
|
|
593
|
+
: 'border-hairline-strong bg-transparent'
|
|
594
594
|
)}
|
|
595
595
|
>
|
|
596
596
|
{(allSelected || someSelected) && (
|
|
@@ -629,7 +629,7 @@ export function TableSelectCell({ rowId, className }: TableSelectCellProps) {
|
|
|
629
629
|
'w-5 h-5 rounded border-2 items-center justify-center',
|
|
630
630
|
isSelected
|
|
631
631
|
? 'bg-brand-primary border-brand-primary'
|
|
632
|
-
: 'border-
|
|
632
|
+
: 'border-hairline-strong bg-transparent web:hover:border-brand-primary'
|
|
633
633
|
)}
|
|
634
634
|
>
|
|
635
635
|
{isSelected && (
|
|
@@ -273,7 +273,7 @@ function WorkoutHeader({
|
|
|
273
273
|
</View>
|
|
274
274
|
</View>
|
|
275
275
|
<View
|
|
276
|
-
className="bg-
|
|
276
|
+
className="bg-hairline"
|
|
277
277
|
style={{ height: 4, borderRadius: 2 }}
|
|
278
278
|
testID="active-workout-page-progress-track"
|
|
279
279
|
>
|
|
@@ -305,7 +305,7 @@ function ExerciseList({ groups, focusedId, onToggle }: ExerciseListProps) {
|
|
|
305
305
|
return (
|
|
306
306
|
<View
|
|
307
307
|
key={group.exercise.id}
|
|
308
|
-
className="bg-surface-elevated border-
|
|
308
|
+
className="bg-surface-elevated border-hairline"
|
|
309
309
|
style={{
|
|
310
310
|
borderWidth: 1,
|
|
311
311
|
borderRadius: 12,
|
|
@@ -399,7 +399,7 @@ export function ActiveWorkoutPage({
|
|
|
399
399
|
|
|
400
400
|
{(showRest || showInput) && (
|
|
401
401
|
<View
|
|
402
|
-
style={{ borderTopWidth: 1, borderTopColor: resolveColor('
|
|
402
|
+
style={{ borderTopWidth: 1, borderTopColor: resolveColor('hairline-default') }}
|
|
403
403
|
testID="active-workout-page-bottom-bar"
|
|
404
404
|
>
|
|
405
405
|
{showRest && rest != null ? (
|
|
@@ -3,6 +3,11 @@ import { render, screen, fireEvent } from '@testing-library/react'
|
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { Text, View } from 'react-native'
|
|
5
5
|
import { BaseBadge } from './BaseBadge'
|
|
6
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
7
|
+
|
|
8
|
+
// Read from the token, not pinned: these used to be hand-copied hexes and
|
|
9
|
+
// silently desynced when borders became alpha hairlines (TD-07.16).
|
|
10
|
+
const T = getSemanticColors('dark')
|
|
6
11
|
|
|
7
12
|
describe('BaseBadge', () => {
|
|
8
13
|
it('renders children', () => {
|
|
@@ -12,7 +17,7 @@ describe('BaseBadge', () => {
|
|
|
12
17
|
|
|
13
18
|
it('defaults to the plain variant border color', () => {
|
|
14
19
|
render(<BaseBadge testID="bb"><Text>x</Text></BaseBadge>)
|
|
15
|
-
expect(screen.getByTestId('bb')).toHaveStyle({ borderTopColor: '
|
|
20
|
+
expect(screen.getByTestId('bb')).toHaveStyle({ borderTopColor: T['hairline-default'] })
|
|
16
21
|
})
|
|
17
22
|
|
|
18
23
|
it('applies the pr variant border color', () => {
|
|
@@ -30,7 +30,7 @@ export const baseBadgeSizeConfig: Record<
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const variantColors: Record<BaseBadgeVariant, { backgroundColor: string; borderColor: string }> = {
|
|
33
|
-
plain: { backgroundColor: t['surface-raised'], borderColor: '
|
|
33
|
+
plain: { backgroundColor: t['surface-raised'], borderColor: t['hairline-default'] },
|
|
34
34
|
pr: {
|
|
35
35
|
backgroundColor: 'rgba(255, 121, 0, 0.12)',
|
|
36
36
|
borderColor: 'rgba(255, 121, 0, 0.3)',
|
|
@@ -278,7 +278,7 @@ function ViewToggle({ view, onViewChange }: ViewToggleProps) {
|
|
|
278
278
|
borderRadius: 9999,
|
|
279
279
|
backgroundColor: active ? 'rgba(255,121,0,0.16)' : 'transparent',
|
|
280
280
|
borderWidth: 1,
|
|
281
|
-
borderColor: active ? BRAND_PRIMARY : resolveColor('
|
|
281
|
+
borderColor: active ? BRAND_PRIMARY : resolveColor('hairline-strong'),
|
|
282
282
|
}}
|
|
283
283
|
testID={`body-map-toggle-${side}`}
|
|
284
284
|
>
|
|
@@ -324,7 +324,7 @@ function MuscleButton({ entry, highlighted, onMusclePress }: MuscleButtonProps)
|
|
|
324
324
|
paddingVertical: 3,
|
|
325
325
|
borderRadius: 9999,
|
|
326
326
|
borderWidth: 1,
|
|
327
|
-
borderColor: highlighted ? BRAND_PRIMARY : resolveColor('
|
|
327
|
+
borderColor: highlighted ? BRAND_PRIMARY : resolveColor('hairline-default'),
|
|
328
328
|
}}
|
|
329
329
|
testID={`body-map-muscle-${entry.key}`}
|
|
330
330
|
>
|
|
@@ -220,7 +220,7 @@ export function BodyMapDetailPanel({
|
|
|
220
220
|
testID="body-map-detail-panel-handle"
|
|
221
221
|
>
|
|
222
222
|
<View
|
|
223
|
-
className="bg-
|
|
223
|
+
className="bg-hairline-strong"
|
|
224
224
|
style={{ width: 40, height: 4, borderRadius: 2 }}
|
|
225
225
|
accessibilityElementsHidden
|
|
226
226
|
/>
|
|
@@ -380,7 +380,7 @@ export function BodyMapDetailPanel({
|
|
|
380
380
|
{contributingExercises!.map((exercise, index) => (
|
|
381
381
|
<View
|
|
382
382
|
key={`${exercise.name}-${index}`}
|
|
383
|
-
className="flex-row items-center justify-between bg-surface-raised border-
|
|
383
|
+
className="flex-row items-center justify-between bg-surface-raised border-hairline"
|
|
384
384
|
style={{
|
|
385
385
|
gap: 10,
|
|
386
386
|
paddingVertical: 8,
|
|
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'
|
|
|
2
2
|
import { render, screen } from '@testing-library/react'
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { DeviationBar } from './DeviationBar'
|
|
5
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
6
|
|
|
6
7
|
describe('DeviationBar', () => {
|
|
7
8
|
it('renders the bar and dot', () => {
|
|
@@ -62,7 +63,7 @@ describe('DeviationBar', () => {
|
|
|
62
63
|
const { getByTestId } = render(<DeviationBar deviation={0} />)
|
|
63
64
|
const dot = getByTestId('deviation-dot')
|
|
64
65
|
expect(dot).toHaveStyle({ width: '8px', height: '8px' })
|
|
65
|
-
expect(dot).toHaveStyle({ borderTopWidth: '1.5px', borderTopColor:
|
|
66
|
+
expect(dot).toHaveStyle({ borderTopWidth: '1.5px', borderTopColor: greyRamp[50] })
|
|
66
67
|
expect(dot).toHaveStyle({ boxShadow: '0 0 4px rgba(0,0,0,0.5)' })
|
|
67
68
|
})
|
|
68
69
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { View, type ViewProps, type ViewStyle } from 'react-native'
|
|
3
3
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
4
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
4
5
|
|
|
5
6
|
const t = getSemanticColors('dark')
|
|
6
7
|
|
|
@@ -13,7 +14,7 @@ export interface DeviationBarProps extends ViewProps {
|
|
|
13
14
|
function getDotColor(deviation: number): string {
|
|
14
15
|
const abs = Math.abs(deviation)
|
|
15
16
|
if (deviation < -0.3) return t['status-success']
|
|
16
|
-
if (abs <= 0.3) return
|
|
17
|
+
if (abs <= 0.3) return greyRamp[500]
|
|
17
18
|
if (abs <= 0.7) return t['status-warning']
|
|
18
19
|
return t['status-error']
|
|
19
20
|
}
|
|
@@ -68,7 +69,7 @@ export function DeviationBar({
|
|
|
68
69
|
height: dotSize,
|
|
69
70
|
borderRadius: 9999,
|
|
70
71
|
borderWidth: 1.5,
|
|
71
|
-
borderColor:
|
|
72
|
+
borderColor: greyRamp[50],
|
|
72
73
|
boxShadow: '0 0 4px rgba(0,0,0,0.5)',
|
|
73
74
|
backgroundColor: getDotColor(clamped),
|
|
74
75
|
left: Math.max(0, Math.min(dotPosition - dotSize / 2, resolvedWidth - dotSize)),
|
|
@@ -3,6 +3,7 @@ import { render, screen, fireEvent, within } from '@testing-library/react'
|
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { ExerciseCard } from './ExerciseCard'
|
|
5
5
|
import type { SetRowProps } from './SetRow'
|
|
6
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
7
|
|
|
7
8
|
const baseCollapsedProps = {
|
|
8
9
|
name: 'Bench Press',
|
|
@@ -86,9 +87,8 @@ describe('ExerciseCard', () => {
|
|
|
86
87
|
sets: unifiedSets,
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
//
|
|
90
|
-
const T_ACTIVE = '
|
|
91
|
-
const T_MUTED = '#9CA3AF'
|
|
90
|
+
// Read from the tokens, not pinned (see SetRow.test.tsx).
|
|
91
|
+
const { 'text-primary': T_ACTIVE, 'text-secondary': T_MUTED } = getSemanticColors('dark')
|
|
92
92
|
|
|
93
93
|
it('renders exercise name via the real ExerciseCardHeading header', () => {
|
|
94
94
|
render(<ExerciseCard {...expandedProps} />)
|
|
@@ -220,7 +220,7 @@ function CollapsedCard({
|
|
|
220
220
|
|
|
221
221
|
const DARK = getSemanticColors('dark')
|
|
222
222
|
/** The header↔body seam. */
|
|
223
|
-
const BODY_DIVIDER = DARK['
|
|
223
|
+
const BODY_DIVIDER = DARK['hairline-subtle']
|
|
224
224
|
|
|
225
225
|
/** Project the set rows onto the heading strip's per-set state (done / active / todo). */
|
|
226
226
|
function deriveHeaderSetStates(sets: SetRowProps[]): SetStripSet[] {
|
|
@@ -262,7 +262,7 @@ function ExpandedCard({
|
|
|
262
262
|
|
|
263
263
|
return (
|
|
264
264
|
<View
|
|
265
|
-
className="bg-surface-elevated border-
|
|
265
|
+
className="bg-surface-elevated border-hairline"
|
|
266
266
|
style={{
|
|
267
267
|
borderWidth: 1,
|
|
268
268
|
...borderRadius,
|
|
@@ -193,7 +193,7 @@ function TabBar({ active, onSelect }: TabBarProps) {
|
|
|
193
193
|
className="flex-row"
|
|
194
194
|
style={{
|
|
195
195
|
borderBottomWidth: 1,
|
|
196
|
-
borderBottomColor: resolveColor('
|
|
196
|
+
borderBottomColor: resolveColor('hairline-default'),
|
|
197
197
|
}}
|
|
198
198
|
accessibilityRole={'tablist' as ViewProps['accessibilityRole']}
|
|
199
199
|
testID="exercise-detail-page-tabs"
|
|
@@ -247,7 +247,7 @@ function StatStrip({ stats, unit }: { stats: ExerciseDetailStats; unit: 'lbs' |
|
|
|
247
247
|
return (
|
|
248
248
|
<View
|
|
249
249
|
key={key}
|
|
250
|
-
className="bg-surface-elevated border-
|
|
250
|
+
className="bg-surface-elevated border-hairline"
|
|
251
251
|
style={{
|
|
252
252
|
flex: 1,
|
|
253
253
|
padding: 12,
|
|
@@ -297,7 +297,7 @@ function SectionCard({
|
|
|
297
297
|
}) {
|
|
298
298
|
return (
|
|
299
299
|
<View
|
|
300
|
-
className="bg-surface-elevated border-
|
|
300
|
+
className="bg-surface-elevated border-hairline"
|
|
301
301
|
style={{
|
|
302
302
|
borderWidth: 1,
|
|
303
303
|
borderRadius: 12,
|
|
@@ -421,7 +421,7 @@ function VbtSummaryRow({ summary }: { summary: VbtSummary }) {
|
|
|
421
421
|
{cells.map((cell) => (
|
|
422
422
|
<View
|
|
423
423
|
key={cell.label}
|
|
424
|
-
className="bg-surface-elevated border-
|
|
424
|
+
className="bg-surface-elevated border-hairline"
|
|
425
425
|
style={{
|
|
426
426
|
flex: 1,
|
|
427
427
|
padding: 12,
|
|
@@ -19,7 +19,7 @@ export interface InputBarProps {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
|
|
22
|
-
const INPUT_CLASSNAME = 'bg-surface-raised border-
|
|
22
|
+
const INPUT_CLASSNAME = 'bg-surface-raised border-hairline-strong text-text-primary'
|
|
23
23
|
|
|
24
24
|
const inputStyle = {
|
|
25
25
|
fontSize: 14,
|
|
@@ -55,7 +55,7 @@ export function InputBar({
|
|
|
55
55
|
style={{
|
|
56
56
|
width: '100%',
|
|
57
57
|
borderTopWidth: 1,
|
|
58
|
-
borderTopColor: resolveColor('
|
|
58
|
+
borderTopColor: resolveColor('hairline-default'),
|
|
59
59
|
paddingTop: 10,
|
|
60
60
|
paddingHorizontal: 16,
|
|
61
61
|
paddingBottom: 12,
|
|
@@ -82,7 +82,7 @@ const meta: Meta<typeof LiveAuraFrame> = {
|
|
|
82
82
|
docs: {
|
|
83
83
|
description: {
|
|
84
84
|
component:
|
|
85
|
-
'Full-surface color-flood frame tied to a coaching category. Wraps content on a
|
|
85
|
+
'Full-surface color-flood frame tied to a coaching category. Wraps content on a grey ' +
|
|
86
86
|
'base and layers a radial wash — amber at threshold (VL20), red at stop (VL28+, pulsing) — ' +
|
|
87
87
|
'derived from the semantic warning / error tokens via `alpha()`. Children pass through. ' +
|
|
88
88
|
'Rendered here at live-screen size so the flood reads (it is meant to tint a whole panel, ' +
|
|
@@ -19,7 +19,7 @@ describe('LiveAuraFrame', () => {
|
|
|
19
19
|
expect(screen.getByText('Cable Row')).toBeInTheDocument()
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
it('sits on the
|
|
22
|
+
it('sits on the grey base surface', () => {
|
|
23
23
|
render(<LiveAuraFrame category="productive" />)
|
|
24
24
|
expect(screen.getByTestId('live-aura-frame')).toHaveStyle({
|
|
25
25
|
backgroundColor: t['background-base'],
|
|
@@ -45,7 +45,7 @@ export function liveAuraColor(category: LiveAuraCategory): string | null {
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Full-surface color-flood frame tied to a coaching category. Wraps `children`
|
|
48
|
-
* on a
|
|
48
|
+
* on a grey base and layers a radial wash (amber at threshold, red at stop)
|
|
49
49
|
* as a decorative, pointer-transparent overlay. The wash uses a solid
|
|
50
50
|
* `backgroundColor` fallback (native) plus a web-only `backgroundImage` radial
|
|
51
51
|
* gradient; both derive from the same semantic token via `alpha()`.
|
|
@@ -62,7 +62,7 @@ export function LiveAuraFrame({
|
|
|
62
62
|
const tint = color ? alpha(color, floodOpacity[category]) : 'transparent'
|
|
63
63
|
|
|
64
64
|
// Match R2's canonical aura: a top-down radial wash that FADES TO TRANSPARENT
|
|
65
|
-
// by ~62%, so the
|
|
65
|
+
// by ~62%, so the grey base shows through the lower panel — NOT a flat
|
|
66
66
|
// full-surface tint. On web (the target: MCP dashboard + Storybook) render the
|
|
67
67
|
// gradient only; native has no gradient support, so fall back to a solid tint.
|
|
68
68
|
const floodStyle = {
|
|
@@ -84,7 +84,7 @@ export function LiveAuraFrame({
|
|
|
84
84
|
borderRadius: 10,
|
|
85
85
|
backgroundColor: t['background-base'],
|
|
86
86
|
borderWidth: 1,
|
|
87
|
-
borderColor: t['
|
|
87
|
+
borderColor: t['hairline-default'],
|
|
88
88
|
},
|
|
89
89
|
style,
|
|
90
90
|
]}
|
|
@@ -10,10 +10,12 @@ import {
|
|
|
10
10
|
MESO_ACCENT_GRADIENT_LIGHT,
|
|
11
11
|
} from '../../../theme/extracted-colors-dataviz'
|
|
12
12
|
|
|
13
|
+
const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
|
|
14
|
+
|
|
13
15
|
const BRAND_PRIMARY = getSemanticColors('dark')['brand-primary']
|
|
14
16
|
const BRAND_PRIMARY_DARK = MESO_ACCENT_GRADIENT_DARK
|
|
15
17
|
const BRAND_PRIMARY_LIGHT = MESO_ACCENT_GRADIENT_LIGHT
|
|
16
|
-
const BORDER_DEFAULT =
|
|
18
|
+
const BORDER_DEFAULT = HAIRLINE_DEFAULT
|
|
17
19
|
|
|
18
20
|
/** Gradient stops for the 3px top accent: dark -> primary -> light. */
|
|
19
21
|
const ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY, BRAND_PRIMARY_LIGHT]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { View, Text, Pressable, type ViewProps } from 'react-native'
|
|
3
3
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
4
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
4
5
|
|
|
5
6
|
const t = getSemanticColors('dark')
|
|
6
7
|
|
|
@@ -15,7 +16,7 @@ export interface MuscleGroupChipProps extends ViewProps {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const dotColorMap: Record<VolumeStatus, string> = {
|
|
18
|
-
untrained:
|
|
19
|
+
untrained: greyRamp[900],
|
|
19
20
|
behind: t['brand-secondary'],
|
|
20
21
|
ontrack: t['status-success'],
|
|
21
22
|
target: t['brand-primary'],
|
|
@@ -31,13 +32,13 @@ export function MuscleGroupChip({
|
|
|
31
32
|
}: MuscleGroupChipProps) {
|
|
32
33
|
const dotColor = volumeStatus
|
|
33
34
|
? dotColorMap[volumeStatus]
|
|
34
|
-
:
|
|
35
|
+
: greyRamp[900]
|
|
35
36
|
|
|
36
37
|
const statusLabel = volumeStatus ?? 'no status'
|
|
37
38
|
|
|
38
39
|
const content = (
|
|
39
40
|
<View
|
|
40
|
-
className={['bg-surface-raised border-
|
|
41
|
+
className={['bg-surface-raised border-hairline', className].filter(Boolean).join(' ')}
|
|
41
42
|
style={{
|
|
42
43
|
flexDirection: 'row',
|
|
43
44
|
alignItems: 'center',
|
|
@@ -10,7 +10,7 @@ const RECENT_BORDER = 'rgba(249,180,21,0.3)'
|
|
|
10
10
|
const RECENT_GLOW = '0 0 12px rgba(249,180,21,0.06)'
|
|
11
11
|
// Native-safe fallback for the conditional row border (recent uses a computed
|
|
12
12
|
// rgba, so className can't express both branches).
|
|
13
|
-
const BORDER_DEFAULT = resolveColor('
|
|
13
|
+
const BORDER_DEFAULT = resolveColor('hairline-default')
|
|
14
14
|
|
|
15
15
|
export type PrRecordType = 'e1rm' | 'weight' | 'reps' | 'volume' | 'velocity'
|
|
16
16
|
|
|
@@ -172,7 +172,7 @@ export function PrHistoryModal({
|
|
|
172
172
|
>
|
|
173
173
|
<View
|
|
174
174
|
accessibilityElementsHidden
|
|
175
|
-
className="bg-
|
|
175
|
+
className="bg-hairline"
|
|
176
176
|
style={{
|
|
177
177
|
alignSelf: 'center',
|
|
178
178
|
width: 40,
|
|
@@ -116,7 +116,7 @@ function EmojiSlider({ factor }: EmojiSliderProps) {
|
|
|
116
116
|
accessibilityLabel={`${factor.label} level ${level} of 5`}
|
|
117
117
|
aria-checked={selected}
|
|
118
118
|
testID="readiness-check-emoji"
|
|
119
|
-
className={selected ? undefined : 'border-
|
|
119
|
+
className={selected ? undefined : 'border-hairline bg-surface-raised'}
|
|
120
120
|
style={({ pressed }) => ({
|
|
121
121
|
flex: 1,
|
|
122
122
|
alignItems: 'center',
|
|
@@ -172,7 +172,7 @@ function WarmUpCard({ validation }: { validation: WarmUpValidation }) {
|
|
|
172
172
|
const badge = WARMUP_BADGE[validation.status]
|
|
173
173
|
return (
|
|
174
174
|
<View
|
|
175
|
-
className="border-
|
|
175
|
+
className="border-hairline bg-surface-raised"
|
|
176
176
|
style={{
|
|
177
177
|
marginTop: 16,
|
|
178
178
|
padding: 12,
|
|
@@ -188,7 +188,7 @@ export function RestTimer({
|
|
|
188
188
|
style={{
|
|
189
189
|
width: '100%',
|
|
190
190
|
borderTopWidth: 1,
|
|
191
|
-
borderTopColor: resolveColor('
|
|
191
|
+
borderTopColor: resolveColor('hairline-default'),
|
|
192
192
|
paddingVertical: 12,
|
|
193
193
|
paddingHorizontal: 16,
|
|
194
194
|
}}
|
|
@@ -253,7 +253,7 @@ export function RestTimer({
|
|
|
253
253
|
|
|
254
254
|
{/* Progress bar */}
|
|
255
255
|
<View
|
|
256
|
-
className="bg-
|
|
256
|
+
className="bg-hairline"
|
|
257
257
|
style={{
|
|
258
258
|
height: 3,
|
|
259
259
|
borderRadius: 2,
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
11
11
|
import { Fragment } from 'react'
|
|
12
|
-
import {
|
|
12
|
+
import { primitiveRamps as ramp, greyRamp } from '../../../theme/tokens/primitives'
|
|
13
13
|
import { GREY, RAIL_W, SET_GAP, T_PRIMARY, T_SECONDARY, T_TERTIARY, INSET, velColor, reps, Page, sectionTitle } from './setHeadingKit'
|
|
14
14
|
|
|
15
15
|
// ---- variable-zone (floor→max) upcoming color — real ramp candidates (no invented hex)
|
|
16
|
-
const VAR_GREY =
|
|
16
|
+
const VAR_GREY = greyRamp[800] // #3C3C3C — lighter grey (the paler-grey reference)
|
|
17
17
|
const VAR_BLUE_800 = ramp.blue[800] // #14407E — clearly blue, a touch brighter
|
|
18
18
|
const VAR_BLUE_900 = ramp.blue[900] // #112C5A — dark muted navy, recessive
|
|
19
19
|
const VAR_CYAN_900 = ramp.cyan[900] // #0B3149 — teal alt, sidesteps info-blue adjacency
|
|
@@ -115,7 +115,7 @@ function Sheet() {
|
|
|
115
115
|
{/* 1. variable rep-range — real ramp candidates for the variable zone */}
|
|
116
116
|
<Col label="1 · variable rep-range — LOCKED: cyan 900">
|
|
117
117
|
{([
|
|
118
|
-
['grey ·
|
|
118
|
+
['grey · grey 200', VAR_GREY],
|
|
119
119
|
['blue 800 · #14407E', VAR_BLUE_800],
|
|
120
120
|
['blue 900 · #112C5A', VAR_BLUE_900],
|
|
121
121
|
['cyan 900 · #0B3149', VAR_CYAN_900],
|
|
@@ -9,7 +9,7 @@ import { SessionHeader, type SessionHeaderPlanEntry } from './SessionHeader'
|
|
|
9
9
|
* bar (fill ∝ sets done, coloured against the elapsed-vs-budget pace with a live marker),
|
|
10
10
|
* a mono `n/total sets` label and a ⏱ elapsed readout. Upcoming, the tiles become
|
|
11
11
|
* Date/Time/Until, the bar is the empty plan shape, and the readout shows the planned
|
|
12
|
-
* duration. Rendered on the raised
|
|
12
|
+
* duration. Rendered on the raised grey plane.
|
|
13
13
|
*/
|
|
14
14
|
const meta: Meta<typeof SessionHeader> = {
|
|
15
15
|
title: 'Shell/SessionRail/SessionHeader',
|
|
@@ -20,7 +20,7 @@ const meta: Meta<typeof SessionHeader> = {
|
|
|
20
20
|
description: {
|
|
21
21
|
component:
|
|
22
22
|
'**Molecule** (shell S3). The session-rail heading — title, stat tiles, chunked ' +
|
|
23
|
-
'pace bar, sets label + ⏱ readout, on the raised
|
|
23
|
+
'pace bar, sets label + ⏱ readout, on the raised grey plane. Composes ' +
|
|
24
24
|
'[MetricTiles](?path=/docs/workout-metrictiles--docs) / ' +
|
|
25
25
|
'[ScheduleTiles](?path=/docs/workout-scheduletiles--docs) + ' +
|
|
26
26
|
'[SegmentedProgressBar](?path=/docs/workout-segmentedprogressbar--docs) + ' +
|
|
@@ -51,7 +51,7 @@ export interface SessionHeaderProps extends ViewProps {
|
|
|
51
51
|
* done, coloured green/amber against the elapsed-vs-budget pace, with a live marker),
|
|
52
52
|
* a mono `n/total sets` label and a ⏱ elapsed readout. Upcoming (`next` set), the tiles
|
|
53
53
|
* become Date/Time/Until, the bar is the empty plan shape, and the readout shows the
|
|
54
|
-
* planned duration. Rendered on the raised
|
|
54
|
+
* planned duration. Rendered on the raised grey plane; presentational. Composes
|
|
55
55
|
* {@link MetricTiles} / {@link ScheduleTiles} + {@link SegmentedProgressBar} +
|
|
56
56
|
* {@link TimerReadout}.
|
|
57
57
|
*/
|