@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
|
@@ -10,7 +10,7 @@ const t = getSemanticColors('dark')
|
|
|
10
10
|
* `SessionRail` (shell organism) — the live-workout exercise list: a flat raised
|
|
11
11
|
* `SessionHeader` glance (title, stat tiles, chunked pace bar) over a sunk, inset list
|
|
12
12
|
* of ExerciseCard rail headings. The header plan (chunk widths ∝ sets) is derived from
|
|
13
|
-
* `exercises`. Surfaces bind to the
|
|
13
|
+
* `exercises`. Surfaces bind to the grey ramp; the list depth is a subtle neumorphic
|
|
14
14
|
* inset. Driven entirely by props.
|
|
15
15
|
*/
|
|
16
16
|
const meta: Meta<typeof SessionRail> = {
|
|
@@ -37,7 +37,7 @@ const meta: Meta<typeof SessionRail> = {
|
|
|
37
37
|
description: {
|
|
38
38
|
component:
|
|
39
39
|
'**Organism** (shell S3). The live-workout exercise list: raised header glance over ' +
|
|
40
|
-
'a sunk inset list, subtle neumorphic depth (
|
|
40
|
+
'a sunk inset list, subtle neumorphic depth (grey ramp + `neumorphicShadows`). ' +
|
|
41
41
|
'Composes ' +
|
|
42
42
|
'[SessionHeader](?path=/docs/shell-sessionrail-sessionheader--docs) + ' +
|
|
43
43
|
'[ExerciseCardHeading](?path=/docs/workout-exercisecardheading--docs) × N. ' +
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { View, type ViewProps } from 'react-native'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
4
|
+
import { insetWell } from '../../../theme/materials'
|
|
5
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
6
|
import { Surface } from '../../ui/surface'
|
|
6
7
|
import { ExerciseCardHeading } from './ExerciseCardHeading'
|
|
7
8
|
import { ExerciseCard } from './ExerciseCard'
|
|
@@ -17,7 +18,9 @@ import type { ExerciseIndicatorKind } from './ExerciseIndicator'
|
|
|
17
18
|
// recessed via its inner shadow yet paler than the header, so the transparent
|
|
18
19
|
// exercise headings on it never blend into the header plane. Surface owns both backgrounds,
|
|
19
20
|
// so the rail no longer hand-sets them.
|
|
20
|
-
const
|
|
21
|
+
const SEMANTIC = getSemanticColors('dark')
|
|
22
|
+
/** Row divider — one step up from the well it sits in so the line reads. */
|
|
23
|
+
const DIVIDER = greyRamp[875]
|
|
21
24
|
|
|
22
25
|
const DEFAULT_WIDTH = 246
|
|
23
26
|
|
|
@@ -124,9 +127,14 @@ export function SessionRail({
|
|
|
124
127
|
next={next}
|
|
125
128
|
/>
|
|
126
129
|
|
|
130
|
+
{/* The list is a WELL cut into the rail: one plane down from the rail's
|
|
131
|
+
`elevated`, recessed by the shared inset material rather than by a
|
|
132
|
+
neumorphic pressed shadow (which had no room to read at this
|
|
133
|
+
lightness). `level` follows the well's tone so on-surface text
|
|
134
|
+
resolves against what is actually painted. */}
|
|
127
135
|
<Surface
|
|
128
|
-
level="
|
|
129
|
-
style={[{ flex: 1 },
|
|
136
|
+
level="base"
|
|
137
|
+
style={[{ flex: 1 }, insetWell(SEMANTIC['surface-base'])]}
|
|
130
138
|
testID="session-rail-list"
|
|
131
139
|
>
|
|
132
140
|
{exercises.map((ex, i) => (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
|
-
import {
|
|
2
|
+
import { primitiveRamps, greyRamp } from '../../../theme/tokens/primitives'
|
|
3
3
|
import { SegmentedBar, type SegmentedBarSegment } from './SegmentedBar'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -13,8 +13,8 @@ export const SET_STRIP_ZONES = {
|
|
|
13
13
|
fastest: primitiveRamps.green[300],
|
|
14
14
|
} as const
|
|
15
15
|
|
|
16
|
-
/** Grey fill for planned-but-unperformed reps / upcoming sets (
|
|
17
|
-
const TODO_COLOR =
|
|
16
|
+
/** Grey fill for planned-but-unperformed reps / upcoming sets (grey placeholder). */
|
|
17
|
+
const TODO_COLOR = greyRamp[900]
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* The learned "variable / unknown / opportunity" semantic (cyan-900, a real ramp
|
|
@@ -3,10 +3,13 @@ import { render, screen } from '@testing-library/react'
|
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { View } from 'react-native'
|
|
5
5
|
import { SetRow, type SetRowProps } from './SetRow'
|
|
6
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
7
|
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// Text treatments, read from the tokens rather than pinned as hexes. These
|
|
9
|
+
// used to be hand-copied literals and silently desynced when the greys moved
|
|
10
|
+
// to the warm ramp (TD-07.14). The assertion still bites — it checks the
|
|
11
|
+
// component reaches for the right ROLE — it just no longer restates the value.
|
|
12
|
+
const { 'text-primary': T_ACTIVE, 'text-secondary': T_MUTED } = getSemanticColors('dark')
|
|
10
13
|
|
|
11
14
|
const doneRow: SetRowProps = {
|
|
12
15
|
state: 'done',
|
|
@@ -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, 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
|
|
|
@@ -28,7 +29,7 @@ const solidVariantColors: Record<string, string> = {
|
|
|
28
29
|
success: t['status-success'],
|
|
29
30
|
warning: t['status-warning'],
|
|
30
31
|
error: t['status-error'],
|
|
31
|
-
neutral:
|
|
32
|
+
neutral: greyRamp[500],
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const ringVariantStyles: Record<string, Record<string, unknown>> = {
|
|
@@ -71,10 +72,10 @@ const iconColors: Record<StatusDotVariant, string> = {
|
|
|
71
72
|
success: '#0A5C52',
|
|
72
73
|
warning: '#6B4000',
|
|
73
74
|
error: '#5C1A1A',
|
|
74
|
-
neutral:
|
|
75
|
+
neutral: greyRamp[200],
|
|
75
76
|
'on-track': t['status-success'],
|
|
76
77
|
deviation: t['status-warning'],
|
|
77
|
-
future:
|
|
78
|
+
future: greyRamp[500],
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
const sizeDimensions = {
|
|
@@ -146,7 +147,7 @@ export function StatusDot({
|
|
|
146
147
|
>
|
|
147
148
|
<View accessibilityElementsHidden>{dot}</View>
|
|
148
149
|
<Text
|
|
149
|
-
style={{ fontSize: 12, lineHeight: 16, color:
|
|
150
|
+
style={{ fontSize: 12, lineHeight: 16, color: greyRamp[400] }}
|
|
150
151
|
accessibilityElementsHidden
|
|
151
152
|
>
|
|
152
153
|
{label}
|
|
@@ -477,7 +477,7 @@ export function StrengthTrendChart({
|
|
|
477
477
|
<View
|
|
478
478
|
testID="strength-trend-chart-tooltip"
|
|
479
479
|
accessibilityElementsHidden
|
|
480
|
-
className="bg-surface-elevated border-
|
|
480
|
+
className="bg-surface-elevated border-hairline-strong"
|
|
481
481
|
style={{
|
|
482
482
|
position: 'absolute',
|
|
483
483
|
left: Math.max(
|
|
@@ -114,7 +114,7 @@ function SummaryCards({ summary }: { summary: TrainingStatusSummary }) {
|
|
|
114
114
|
return (
|
|
115
115
|
<View
|
|
116
116
|
key={key}
|
|
117
|
-
className="bg-surface-elevated border-
|
|
117
|
+
className="bg-surface-elevated border-hairline"
|
|
118
118
|
style={{
|
|
119
119
|
flexGrow: 1,
|
|
120
120
|
flexBasis: '46%',
|
|
@@ -265,7 +265,7 @@ export function TrainingStatusPage({
|
|
|
265
265
|
<SummaryCards summary={summary} />
|
|
266
266
|
|
|
267
267
|
<View
|
|
268
|
-
className="bg-surface-elevated border-
|
|
268
|
+
className="bg-surface-elevated border-hairline"
|
|
269
269
|
style={{
|
|
270
270
|
borderWidth: 1,
|
|
271
271
|
borderRadius: 12,
|
|
@@ -414,8 +414,11 @@ describe('VelocityStrip all-zero velocities (NaN guard)', () => {
|
|
|
414
414
|
|
|
415
415
|
// Literal-hex slot colors (must match the component's tokens exactly).
|
|
416
416
|
// TODO_SOLID = the LANDED surface-relative solid to-do tone (base/dark plane blended toward the
|
|
417
|
-
// on-surface neutral) — replaced the old fixed
|
|
418
|
-
|
|
417
|
+
// on-surface neutral) — replaced the old fixed grey #2C2C2C. Solid fill, not a dashed outline.
|
|
418
|
+
// Pinned rather than recomputed on purpose: deriving it here would just mirror
|
|
419
|
+
// SetBarChart's mixHex and stop catching a change to the blend itself.
|
|
420
|
+
// = mixHex(grey-925, text-tertiary, 0.55); moved with the warm ramp (TD-07.14).
|
|
421
|
+
const TODO_SOLID = '#5B5957'
|
|
419
422
|
const VARIABLE_CYAN = '#0B3149'
|
|
420
423
|
const CONTINUE_OUTLINE = '#22465F'
|
|
421
424
|
describe('VelocityStrip set-type modes (compact)', () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useEffect, useState } from 'react'
|
|
3
3
|
import { View, Text, Pressable, Animated, type ViewProps, type ViewStyle } from 'react-native'
|
|
4
4
|
import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
5
|
-
import {
|
|
5
|
+
import { sequentialEffort, greyRamp } from '../../../theme/tokens/primitives'
|
|
6
6
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
7
7
|
import { alpha } from '../../../utils/colors'
|
|
8
8
|
import { formatVelocity } from '../../../utils/workout-format'
|
|
@@ -456,14 +456,14 @@ const EXPANDED_HEIGHT = 60
|
|
|
456
456
|
/** Default `compact` (flat resting strip) height (px) — a THIN radius-2 pill row, the resting glance. */
|
|
457
457
|
const COMPACT_HEIGHT = 8
|
|
458
458
|
|
|
459
|
-
/** The dashed running-best reference line color (the lightest
|
|
460
|
-
const HERO_REFERENCE_COLOR =
|
|
459
|
+
/** The dashed running-best reference line color (the lightest grey step). */
|
|
460
|
+
const HERO_REFERENCE_COLOR = greyRamp[600]
|
|
461
461
|
|
|
462
462
|
/**
|
|
463
463
|
* A dashed running-best reference line spanning the plot width, absolutely positioned a
|
|
464
464
|
* pixel `offset` from the given `anchor` edge. Shared by the single `hero` chart (anchored
|
|
465
465
|
* to the baseline, `bottom`) and BOTH wings of the diverging dual chart (anchored to the
|
|
466
|
-
* centre axis, `top`) so the three sites render one line treatment (
|
|
466
|
+
* centre axis, `top`) so the three sites render one line treatment (grey-0, 1px dashed).
|
|
467
467
|
* Presentation-only + non-interactive — the numeric best lives in each chart's container
|
|
468
468
|
* accessibility label, so the line itself is hidden from the a11y tree.
|
|
469
469
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Font mapping: font-heading=Space Grotesk, font-body=Nunito Sans (UI), font-sans=Inter (body)
|
|
2
2
|
import { View, Text, type ViewProps } from 'react-native'
|
|
3
3
|
import { WORKOUT_TOKENS } from '../../../theme/workout-tokens'
|
|
4
|
-
import {
|
|
4
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
5
|
import { ZoneTrack } from './ZoneTrack'
|
|
6
6
|
import { DataRow } from '../../ui/data-row/DataRow'
|
|
7
7
|
import type { VolumeLandmarks } from './muscleTaxonomy'
|
|
@@ -11,9 +11,9 @@ import type { VolumeLandmarks } from './muscleTaxonomy'
|
|
|
11
11
|
// BodyMap / MesoProgressBar so a muscle reads identically across the app.
|
|
12
12
|
const HEAT = WORKOUT_TOKENS.heatmap
|
|
13
13
|
|
|
14
|
-
// The muted, un-reached track colour — the same
|
|
14
|
+
// The muted, un-reached track colour — the same grey step ZoneTrack defaults
|
|
15
15
|
// to, so the bar sits on the shared gauge-track surface.
|
|
16
|
-
const NEUTRAL_TRACK =
|
|
16
|
+
const NEUTRAL_TRACK = greyRamp[800]
|
|
17
17
|
const MONO = 'monospace'
|
|
18
18
|
|
|
19
19
|
export type VolumeZone = 'under' | 'maintenance' | 'productive' | 'approaching' | 'over'
|
|
@@ -2,6 +2,11 @@ import { describe, it, expect, vi } from 'vitest'
|
|
|
2
2
|
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
3
|
import { axe } from 'jest-axe'
|
|
4
4
|
import { WeightBadge } from './WeightBadge'
|
|
5
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
6
|
+
|
|
7
|
+
// Read from the token, not pinned: these used to be hand-copied hexes and
|
|
8
|
+
// silently desynced when borders became alpha hairlines (TD-07.16).
|
|
9
|
+
const T = getSemanticColors('dark')
|
|
5
10
|
|
|
6
11
|
describe('WeightBadge', () => {
|
|
7
12
|
it('renders value and default unit', () => {
|
|
@@ -71,7 +76,7 @@ describe('WeightBadge', () => {
|
|
|
71
76
|
it('uses default border color when not a PR', () => {
|
|
72
77
|
render(<WeightBadge value={225} />)
|
|
73
78
|
expect(screen.getByTestId('weight-badge')).toHaveStyle({
|
|
74
|
-
borderTopColor: '
|
|
79
|
+
borderTopColor: T['hairline-default'],
|
|
75
80
|
})
|
|
76
81
|
})
|
|
77
82
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type BaseBadgeProps,
|
|
9
9
|
} from './BaseBadge'
|
|
10
10
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
11
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
11
12
|
|
|
12
13
|
export type WeightBadgeSize = BaseBadgeSize
|
|
13
14
|
|
|
@@ -43,7 +44,7 @@ export function WeightBadge({
|
|
|
43
44
|
...props
|
|
44
45
|
}: WeightBadgeProps) {
|
|
45
46
|
const config = baseBadgeSizeConfig[size]
|
|
46
|
-
const textColor = isPr ? getSemanticColors('dark')['brand-primary'] :
|
|
47
|
+
const textColor = isPr ? getSemanticColors('dark')['brand-primary'] : greyRamp[400]
|
|
47
48
|
// Dumbbell reads clearly as a weight/strength metric and is unit-agnostic
|
|
48
49
|
// across estimated-1RM and N-rep-max contexts (vs. TrendingUp which implies
|
|
49
50
|
// change/trend rather than a load value).
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
} from './MuscleGroupChip'
|
|
9
9
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
10
10
|
|
|
11
|
+
const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
|
|
12
|
+
|
|
11
13
|
const t = getSemanticColors('dark')
|
|
12
14
|
|
|
13
15
|
export type WorkoutStatus = 'completed' | 'today' | 'upcoming'
|
|
@@ -48,7 +50,7 @@ export interface WorkoutCardProps extends ViewProps {
|
|
|
48
50
|
const COLORS = {
|
|
49
51
|
statusSuccess: t['status-success'],
|
|
50
52
|
brandPrimary: t['brand-primary'],
|
|
51
|
-
borderDefault:
|
|
53
|
+
borderDefault: HAIRLINE_DEFAULT,
|
|
52
54
|
brandPrimarySubtle: 'rgba(255,121,0,0.06)',
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -3,6 +3,9 @@ import { useEffect, useState } from 'react'
|
|
|
3
3
|
import { View, Text, Pressable, Animated, Easing, type ViewProps } from 'react-native'
|
|
4
4
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
5
|
import { WORKOUT_PILL_DELOAD } from '../../../theme/extracted-colors-dataviz'
|
|
6
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
7
|
+
|
|
8
|
+
const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
|
|
6
9
|
|
|
7
10
|
const t = getSemanticColors('dark')
|
|
8
11
|
|
|
@@ -35,7 +38,7 @@ const statusBorderStyles: Record<WorkoutPillStatus, Record<string, unknown>> = {
|
|
|
35
38
|
completed: { borderWidth: 1, borderColor: 'rgba(46,213,115,0.3)' },
|
|
36
39
|
current: { borderWidth: 1, borderColor: 'rgba(255,121,0,0.3)' },
|
|
37
40
|
next: { borderWidth: 1, borderColor: 'rgba(255,121,0,0.4)' },
|
|
38
|
-
upcoming: { borderWidth: 1, borderColor:
|
|
41
|
+
upcoming: { borderWidth: 1, borderColor: HAIRLINE_DEFAULT },
|
|
39
42
|
missed: { borderWidth: 1, borderColor: 'rgba(209,67,67,0.25)' },
|
|
40
43
|
deload: { borderWidth: 1, borderColor: 'rgba(186,41,150,0.3)' },
|
|
41
44
|
}
|
|
@@ -44,7 +47,7 @@ const textColors: Record<WorkoutPillStatus, string> = {
|
|
|
44
47
|
completed: t['status-success'],
|
|
45
48
|
current: t['brand-primary'],
|
|
46
49
|
next: t['brand-primary'],
|
|
47
|
-
upcoming:
|
|
50
|
+
upcoming: greyRamp[500],
|
|
48
51
|
missed: 'rgba(209,67,67,0.7)',
|
|
49
52
|
deload: WORKOUT_PILL_DELOAD,
|
|
50
53
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useState } from 'react'
|
|
3
3
|
import { View, Text, type ViewProps, type DimensionValue, type LayoutChangeEvent } from 'react-native'
|
|
4
4
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
|
-
import { primitiveColors } from '../../../theme/tokens/primitives'
|
|
5
|
+
import { primitiveColors, greyRamp } from '../../../theme/tokens/primitives'
|
|
6
6
|
import { alpha } from '../../../utils/colors'
|
|
7
7
|
import { Tooltip } from '../../ui/tooltip/Tooltip'
|
|
8
8
|
|
|
@@ -13,8 +13,8 @@ function glowShadow(color: string): string {
|
|
|
13
13
|
return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/** Muted, un-reached track colour — a
|
|
17
|
-
const DEFAULT_TRACK_COLOR =
|
|
16
|
+
/** Muted, un-reached track colour — a grey step, matches the IntensityBar track family. */
|
|
17
|
+
const DEFAULT_TRACK_COLOR = greyRamp[800]
|
|
18
18
|
/** Default needle / fill-marker colour. */
|
|
19
19
|
const DEFAULT_MARKER_COLOR = primitiveColors.white
|
|
20
20
|
/** Tick mark + tick label colour. */
|
|
@@ -139,7 +139,7 @@ export interface ZoneTrackProps extends ViewProps {
|
|
|
139
139
|
trackHeight?: number
|
|
140
140
|
/** How far a needle marker overhangs the track top + bottom, in px. Overrides the `size` default (6 default / 9 wall). */
|
|
141
141
|
needleOverhang?: number
|
|
142
|
-
/** Muted colour of the track behind / beyond the fill. Default a
|
|
142
|
+
/** Muted colour of the track behind / beyond the fill. Default a grey step. */
|
|
143
143
|
trackColor?: string
|
|
144
144
|
className?: string
|
|
145
145
|
}
|
|
@@ -5,19 +5,25 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { ReactNode } from 'react'
|
|
7
7
|
import { TempoDisplay } from './TempoDisplay'
|
|
8
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
9
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
8
10
|
|
|
9
11
|
// ---- geometry
|
|
10
12
|
export const RAIL_W = 232
|
|
11
13
|
export const SET_GAP = 5
|
|
12
14
|
|
|
13
|
-
// ---- surface + text tokens (dark theme
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
20
|
-
export const
|
|
15
|
+
// ---- surface + text tokens (dark theme)
|
|
16
|
+
// These were hand-copied hexes labelled with their grey/neutral steps, which
|
|
17
|
+
// is exactly how they silently stopped matching the ramps they named. They read
|
|
18
|
+
// from the tokens now, so a retune reaches them.
|
|
19
|
+
const T = getSemanticColors('dark')
|
|
20
|
+
export const GREY = greyRamp[900] // upcoming/placeholder
|
|
21
|
+
export const BORDER_SUBTLE = greyRamp[950] // row dividers
|
|
22
|
+
export const T_PRIMARY = T['text-primary']
|
|
23
|
+
export const T_SECONDARY = T['text-secondary']
|
|
24
|
+
export const T_TERTIARY = T['text-tertiary']
|
|
25
|
+
export const RAISED = greyRamp[925] // nav + heading raised plane
|
|
26
|
+
export const INSET = greyRamp[975] // sunk exercise-list column
|
|
21
27
|
// depth lives on the sunk list: inner shadow from the heading (top) + nav (left). Raised plane is flat.
|
|
22
28
|
export const INSET_SHADOW =
|
|
23
29
|
'inset 0 9px 12px -6px rgba(0,0,0,0.85), inset 7px 0 10px -6px rgba(0,0,0,0.7), inset 0 -1px 0 rgba(255,255,255,0.02)'
|
|
@@ -20,13 +20,13 @@ import type { ReactNode } from 'react'
|
|
|
20
20
|
import type { Decorator } from '@storybook/react-vite'
|
|
21
21
|
import { View, Text } from 'react-native'
|
|
22
22
|
import { VelocityStrip, DualVelocityStrip, type VelocitySet } from './VelocityStrip'
|
|
23
|
-
import {
|
|
23
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
24
24
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
25
25
|
|
|
26
26
|
const t = getSemanticColors('dark')
|
|
27
27
|
|
|
28
28
|
/** The real dark surface-background plane — stories sit on the token, not an ad-hoc grey. */
|
|
29
|
-
export const SURFACE_BG =
|
|
29
|
+
export const SURFACE_BG = greyRamp[950]
|
|
30
30
|
|
|
31
31
|
export const VIEW_LABEL = t['text-secondary']
|
|
32
32
|
export const VIEW_NOTE = t['text-tertiary']
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
type LayoutChangeEvent,
|
|
21
21
|
} from 'react-native'
|
|
22
22
|
import { primitiveRamps } from '../../../theme/tokens/primitives'
|
|
23
|
-
import { barPaper } from '../../../theme/
|
|
23
|
+
import { barPaper } from '../../../theme/materials'
|
|
24
24
|
import { useOnSurfaceColor, useSurface, surfaceBackground } from '../../ui/surface/SurfaceContext'
|
|
25
25
|
import { useLiveRepGrowth } from './live-rep-growth'
|
|
26
26
|
|
|
@@ -313,7 +313,7 @@ export function SetBarChart({
|
|
|
313
313
|
const flipStyle = flip ? ({ transform: [{ scaleY: -1 as number }] } as const) : null
|
|
314
314
|
|
|
315
315
|
// Planned/to-do reps + the baseline draw in a SURFACE-relative neutral (on-surface tertiary)
|
|
316
|
-
// so they stay legible on every plane instead of a fixed
|
|
316
|
+
// so they stay legible on every plane instead of a fixed grey.
|
|
317
317
|
const placeholderColor = useOnSurfaceColor('tertiary')
|
|
318
318
|
// The expanded strip's solid to-do tone: the surface plane blended toward the neutral (the same
|
|
319
319
|
// relative model), so a `todoVariant="solid"` section holds ~constant contrast on every plane.
|
|
@@ -590,7 +590,7 @@ export interface SideRailSection {
|
|
|
590
590
|
/**
|
|
591
591
|
* The ONE shared gutter/side-rail lockup used by BOTH the single hero and the diverging dual, so a
|
|
592
592
|
* single chart is pixel-identical to the dual's upper half — going dual only adds a lower section.
|
|
593
|
-
* A fixed-width gutter (34px hero / 18px rail) with the `border-
|
|
593
|
+
* A fixed-width gutter (34px hero / 18px rail) with the `border-hairline` right hairline (the vertical
|
|
594
594
|
* axis) and one rotated, height-responsive, initials-collapsing label per section (each `sectionExtent`
|
|
595
595
|
* tall). The single passes ONE section; the dual passes two (up + down wings). Rendered null when no
|
|
596
596
|
* section carries a label. The rotate lives on a wrapper sized to `sectionExtent` so `numberOfLines`
|
|
@@ -137,7 +137,7 @@ export const VerticalWithContent: Story = {
|
|
|
137
137
|
<Text className="text-text-secondary text-sm">
|
|
138
138
|
Fill in your profile details to continue.
|
|
139
139
|
</Text>
|
|
140
|
-
<View className="bg-surface-input border border-
|
|
140
|
+
<View className="bg-surface-input border border-hairline rounded-md px-3 py-2">
|
|
141
141
|
<Text className="text-text-secondary text-sm">Display Name</Text>
|
|
142
142
|
</View>
|
|
143
143
|
</View>
|
|
@@ -91,7 +91,7 @@ function StepConnector({ index }: StepConnectorProps) {
|
|
|
91
91
|
<View
|
|
92
92
|
className={cn(
|
|
93
93
|
'flex-1 h-0.5 mx-2',
|
|
94
|
-
isCompleted ? 'bg-brand-primary' : 'bg-
|
|
94
|
+
isCompleted ? 'bg-brand-primary' : 'bg-hairline'
|
|
95
95
|
)}
|
|
96
96
|
/>
|
|
97
97
|
)
|
|
@@ -101,7 +101,7 @@ function StepConnector({ index }: StepConnectorProps) {
|
|
|
101
101
|
<View
|
|
102
102
|
className={cn(
|
|
103
103
|
'w-0.5 h-8 ml-4 my-1',
|
|
104
|
-
isCompleted ? 'bg-brand-primary' : 'bg-
|
|
104
|
+
isCompleted ? 'bg-brand-primary' : 'bg-hairline'
|
|
105
105
|
)}
|
|
106
106
|
/>
|
|
107
107
|
)
|
|
@@ -171,7 +171,7 @@ export function StepIndicator({
|
|
|
171
171
|
const statusStyles: Record<StepStatus, string> = {
|
|
172
172
|
completed: 'bg-brand-primary border-brand-primary',
|
|
173
173
|
active: 'bg-brand-primary border-brand-primary',
|
|
174
|
-
upcoming: 'bg-surface-base border-
|
|
174
|
+
upcoming: 'bg-surface-base border-hairline-strong',
|
|
175
175
|
error: 'bg-status-error border-status-error',
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -43,7 +43,7 @@ export function DeviceMenu({
|
|
|
43
43
|
<PopoverTrigger>
|
|
44
44
|
<DeviceIndicator status={status} />
|
|
45
45
|
</PopoverTrigger>
|
|
46
|
-
<PopoverContent className="right-0 left-auto mt-[10px] w-[326px] bg-surface-elevated border border-
|
|
46
|
+
<PopoverContent className="right-0 left-auto mt-[10px] w-[326px] bg-surface-elevated border border-hairline-strong rounded-[10px] p-[7px]">
|
|
47
47
|
<Typography
|
|
48
48
|
variant="monoLabel"
|
|
49
49
|
color="tertiary"
|
|
@@ -52,7 +52,7 @@ export function DeviceMenu({
|
|
|
52
52
|
Devices — {boundCount} bound · {availCount} available
|
|
53
53
|
</Typography>
|
|
54
54
|
{devices.map((device, i) => (
|
|
55
|
-
<View key={device.id} className={cn(i > 0 && 'border-t border-
|
|
55
|
+
<View key={device.id} className={cn(i > 0 && 'border-t border-hairline')}>
|
|
56
56
|
<DeviceRow
|
|
57
57
|
device={device}
|
|
58
58
|
onPress={onSelectDevice ? () => onSelectDevice(device) : undefined}
|
|
@@ -55,7 +55,7 @@ export function SideNav({
|
|
|
55
55
|
<View
|
|
56
56
|
accessibilityRole="tablist"
|
|
57
57
|
className={cn(
|
|
58
|
-
'w-[60px] items-center gap-[6px] border-r border-
|
|
58
|
+
'w-[60px] items-center gap-[6px] border-r border-hairline bg-background-base py-3',
|
|
59
59
|
className
|
|
60
60
|
)}
|
|
61
61
|
>
|
|
@@ -58,7 +58,7 @@ export function TopBar({
|
|
|
58
58
|
// shared chrome gradient (web); solid bg-surface-elevated is the native fallback
|
|
59
59
|
style={surfaceGradient.chrome() as object}
|
|
60
60
|
className={cn(
|
|
61
|
-
'h-[46px] flex-row items-center gap-[14px] px-4 bg-surface-elevated border-b border-
|
|
61
|
+
'h-[46px] flex-row items-center gap-[14px] px-4 bg-surface-elevated border-b border-hairline',
|
|
62
62
|
className
|
|
63
63
|
)}
|
|
64
64
|
>
|
|
@@ -287,7 +287,7 @@ export const FormExample: Story = {
|
|
|
287
287
|
isRequired
|
|
288
288
|
/>
|
|
289
289
|
|
|
290
|
-
<View className="pt-2 border-t border-
|
|
290
|
+
<View className="pt-2 border-t border-hairline">
|
|
291
291
|
<Text className="text-sm text-text-secondary">
|
|
292
292
|
Selected: {country ? `${countryOptions.find(c => c.value === country)?.label}` : '-'}
|
|
293
293
|
{city ? `, ${cityOptions[country!]?.find(c => c.value === city)?.label}` : ''}
|
|
@@ -238,7 +238,7 @@ export function Autocomplete<T extends string = string>({
|
|
|
238
238
|
<View
|
|
239
239
|
className={cn(
|
|
240
240
|
'absolute z-50 top-full left-0 right-0 mt-1',
|
|
241
|
-
'bg-surface-elevated rounded-md shadow-lg border border-
|
|
241
|
+
'bg-surface-elevated rounded-md shadow-lg border border-hairline',
|
|
242
242
|
'max-h-60 overflow-hidden'
|
|
243
243
|
)}
|
|
244
244
|
>
|
|
@@ -67,7 +67,7 @@ export function Avatar({
|
|
|
67
67
|
accessibilityLabel={alt || resolvedFallback || 'Avatar'}
|
|
68
68
|
className={cn(
|
|
69
69
|
'items-center justify-center rounded-full overflow-hidden',
|
|
70
|
-
!nameColor && 'bg-
|
|
70
|
+
!nameColor && 'bg-hairline-strong',
|
|
71
71
|
styles.container,
|
|
72
72
|
className
|
|
73
73
|
)}
|
|
@@ -102,7 +102,7 @@ const badgeColors: Record<string, string> = {
|
|
|
102
102
|
success: 'bg-status-success',
|
|
103
103
|
error: 'bg-status-error',
|
|
104
104
|
warning: 'bg-status-warning',
|
|
105
|
-
default: 'bg-
|
|
105
|
+
default: 'bg-hairline-strong',
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
@@ -25,7 +25,7 @@ export interface BadgeProps extends ViewProps {
|
|
|
25
25
|
|
|
26
26
|
const variantColorStyles: Record<BadgeVariant, Record<BadgeColor, string>> = {
|
|
27
27
|
solid: {
|
|
28
|
-
default: 'bg-
|
|
28
|
+
default: 'bg-hairline-strong text-text-inverse',
|
|
29
29
|
primary: 'bg-brand-primary text-text-inverse',
|
|
30
30
|
secondary: 'bg-brand-secondary text-text-inverse',
|
|
31
31
|
success: 'bg-status-success text-text-inverse',
|
|
@@ -43,7 +43,7 @@ const variantColorStyles: Record<BadgeVariant, Record<BadgeColor, string>> = {
|
|
|
43
43
|
info: 'bg-status-info-subtle text-status-info',
|
|
44
44
|
},
|
|
45
45
|
outline: {
|
|
46
|
-
default: 'border border-
|
|
46
|
+
default: 'border border-hairline-strong text-text-secondary',
|
|
47
47
|
primary: 'border border-brand-primary text-brand-primary',
|
|
48
48
|
secondary: 'border border-brand-secondary text-brand-secondary',
|
|
49
49
|
success: 'border border-status-success text-status-success',
|
|
@@ -45,10 +45,10 @@ export interface CardProps extends ViewProps {
|
|
|
45
45
|
|
|
46
46
|
const variantStyles: Record<CardVariant, string> = {
|
|
47
47
|
elevated: '', // Will be set dynamically via elevation system
|
|
48
|
-
outline: 'border-2 border-
|
|
48
|
+
outline: 'border-2 border-hairline-strong', // Thicker border with stronger contrast
|
|
49
49
|
filled: '', // Will be set dynamically via elevation system
|
|
50
|
-
accent: 'border border-
|
|
51
|
-
subtle: 'border border-
|
|
50
|
+
accent: 'border border-hairline',
|
|
51
|
+
subtle: 'border border-hairline-subtle',
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -91,7 +91,7 @@ export const Checkbox = forwardRef<View, CheckboxProps>(function Checkbox(
|
|
|
91
91
|
styles.box,
|
|
92
92
|
isChecked || isIndeterminate
|
|
93
93
|
? 'bg-brand-primary border-brand-primary'
|
|
94
|
-
: 'bg-transparent border-
|
|
94
|
+
: 'bg-transparent border-hairline',
|
|
95
95
|
isInvalid && 'border-status-error',
|
|
96
96
|
!isDisabled && 'web:hover:border-brand-primary'
|
|
97
97
|
)}
|
|
@@ -180,7 +180,7 @@ export function Accordion({
|
|
|
180
180
|
|
|
181
181
|
return (
|
|
182
182
|
<AccordionContext.Provider value={{ openIndices, toggleIndex, allowMultiple }}>
|
|
183
|
-
<View className={cn('w-full divide-y divide-
|
|
183
|
+
<View className={cn('w-full divide-y divide-hairline', className)} {...props}>
|
|
184
184
|
{React.Children.map(children, (child, index) => {
|
|
185
185
|
if (React.isValidElement(child)) {
|
|
186
186
|
return React.cloneElement(child as React.ReactElement<any>, { index })
|
|
@@ -125,7 +125,7 @@ export function Drawer({
|
|
|
125
125
|
>
|
|
126
126
|
{/* Header */}
|
|
127
127
|
{(title || showCloseButton) && (
|
|
128
|
-
<View className="flex-row items-center justify-between px-4 py-3 border-b border-
|
|
128
|
+
<View className="flex-row items-center justify-between px-4 py-3 border-b border-hairline">
|
|
129
129
|
{title && (
|
|
130
130
|
<Text className="text-lg font-semibold text-text-primary">
|
|
131
131
|
{title}
|
|
@@ -165,7 +165,7 @@ export interface DrawerHeaderProps extends ViewProps {
|
|
|
165
165
|
export function DrawerHeader({ className, children, ...props }: DrawerHeaderProps) {
|
|
166
166
|
return (
|
|
167
167
|
<View
|
|
168
|
-
className={cn('px-4 py-3 border-b border-
|
|
168
|
+
className={cn('px-4 py-3 border-b border-hairline', className)}
|
|
169
169
|
{...props}
|
|
170
170
|
>
|
|
171
171
|
{children}
|
|
@@ -217,7 +217,7 @@ export function DrawerFooter({ className, children, ...props }: DrawerFooterProp
|
|
|
217
217
|
<View
|
|
218
218
|
className={cn(
|
|
219
219
|
'flex-row items-center justify-end gap-3',
|
|
220
|
-
'px-4 py-3 border-t border-
|
|
220
|
+
'px-4 py-3 border-t border-hairline',
|
|
221
221
|
className
|
|
222
222
|
)}
|
|
223
223
|
{...props}
|